mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
631
Commits
v11.0
...
v12.0-beta3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c7256a443 | ||
|
|
97420b28e5 | ||
|
|
1467c7039d | ||
|
|
d319badc6c | ||
|
|
65d6c1c438 | ||
|
|
abeefb5655 | ||
|
|
50fecbcebe | ||
|
|
4fe8b72d42 | ||
|
|
f3b060df51 | ||
|
|
09d1cb6925 | ||
|
|
54fa72fa98 | ||
|
|
fd62fe7511 | ||
|
|
bfb34dff62 | ||
|
|
817b623596 | ||
|
|
f6dbeb1f63 | ||
|
|
c4b9bdd33f | ||
|
|
68e0d5f1b5 | ||
|
|
4939e3df55 | ||
|
|
19f19f3880 | ||
|
|
ad0c767ea8 | ||
|
|
ed5f4d5855 | ||
|
|
c2f5d37486 | ||
|
|
231b50dacb | ||
|
|
a71895cbe8 | ||
|
|
a8bede17b2 | ||
|
|
f6c8e693a5 | ||
|
|
9461e4088c | ||
|
|
efd73fd10d | ||
|
|
ddf6e84a1a | ||
|
|
633d6c34e2 | ||
|
|
6e7fbc6683 | ||
|
|
124ea1230b | ||
|
|
8b48a295b6 | ||
|
|
d285d37fdb | ||
|
|
8bb927b483 | ||
|
|
1af4cd0d63 | ||
|
|
db2f28c4cd | ||
|
|
5d9fbd2ccc | ||
|
|
7efc709538 | ||
|
|
79419a98d0 | ||
|
|
1c77d5d5e7 | ||
|
|
c6854e22a3 | ||
|
|
83acc2f285 | ||
|
|
6de95f7a3b | ||
|
|
82839a2d82 | ||
|
|
e5fc9b3132 | ||
|
|
ced4c5944a | ||
|
|
d4c460072b | ||
|
|
0b9384b556 | ||
|
|
6c3277e3e3 | ||
|
|
d9ff1eb38a | ||
|
|
e178097735 | ||
|
|
a1ab8ed208 | ||
|
|
6ababbf8f4 | ||
|
|
79629befc1 | ||
|
|
8022c0772a | ||
|
|
8ad2b4638b | ||
|
|
3a0392df8a | ||
|
|
beb28b061d | ||
|
|
c39acc5031 | ||
|
|
f54a29c415 | ||
|
|
783b111059 | ||
|
|
fb286f8b54 | ||
|
|
5999110e3f | ||
|
|
52a757ea78 | ||
|
|
28df08eea8 | ||
|
|
79505308ba | ||
|
|
a7e9d8e14b | ||
|
|
08f3abe5bf | ||
|
|
3ef09d7d9a | ||
|
|
a9142b9ce5 | ||
|
|
13e6f64d3b | ||
|
|
4a1256c772 | ||
|
|
ff9bec90ec | ||
|
|
a6fee1e510 | ||
|
|
80538f101e | ||
|
|
aee53b14c7 | ||
|
|
5eb2fc8d86 | ||
|
|
98f91bbf88 | ||
|
|
b48b36ef18 | ||
|
|
221a093e5f | ||
|
|
2ca1820d4e | ||
|
|
d58737d5be | ||
|
|
b52cee3154 | ||
|
|
9a76941e10 | ||
|
|
0285a4cce1 | ||
|
|
5a3aa1bd25 | ||
|
|
0f79351de9 | ||
|
|
3cdb25ce8e | ||
|
|
b7193bd0c6 | ||
|
|
10ff6a0095 | ||
|
|
d30f58004e | ||
|
|
17bdb22e4f | ||
|
|
a68209be37 | ||
|
|
7b40eade44 | ||
|
|
8a717c74b9 | ||
|
|
aa72ded21e | ||
|
|
8e53c83844 | ||
|
|
e2a2db1256 | ||
|
|
2afd2d4dae | ||
|
|
fad17cc094 | ||
|
|
f93d957999 | ||
|
|
8db0344cee | ||
|
|
32e531f951 | ||
|
|
a4769702f9 | ||
|
|
cf19fb8df1 | ||
|
|
79b8bb5c9f | ||
|
|
fc5889ec0b | ||
|
|
369303c46d | ||
|
|
d65670cc7b | ||
|
|
f74eeaee0f | ||
|
|
826fb3e9c2 | ||
|
|
a3d7b8a899 | ||
|
|
0cc36ed6e4 | ||
|
|
976bd52972 | ||
|
|
4a8d5def84 | ||
|
|
2f60716082 | ||
|
|
729efb04e1 | ||
|
|
4ea8b4d445 | ||
|
|
e800c165f9 | ||
|
|
fd9bd23449 | ||
|
|
8880ed1393 | ||
|
|
f7fde070ca | ||
|
|
5ada80779d | ||
|
|
8972235a0e | ||
|
|
e56f533e38 | ||
|
|
324fb7dbf7 | ||
|
|
44285b9b5d | ||
|
|
a68f477d61 | ||
|
|
ae9f99448e | ||
|
|
7c0fb10197 | ||
|
|
9e85571a7b | ||
|
|
9e10c15e2e | ||
|
|
6bd7752bac | ||
|
|
83ec437e8a | ||
|
|
4a1d05dd46 | ||
|
|
aa324e355a | ||
|
|
5cb8bcead7 | ||
|
|
bbd06c0c99 | ||
|
|
651830ea82 | ||
|
|
c70146f1dc | ||
|
|
d4e83b28bb | ||
|
|
bc58a25765 | ||
|
|
38645022c9 | ||
|
|
647cd0fbe1 | ||
|
|
ea8935a346 | ||
|
|
7ea80babfc | ||
|
|
dee761a99e | ||
|
|
88ee7a8187 | ||
|
|
eb8b408b82 | ||
|
|
3d10882f57 | ||
|
|
1988496512 | ||
|
|
88b074c208 | ||
|
|
c4c5636a81 | ||
|
|
c39d570b72 | ||
|
|
4ccd7f9f3a | ||
|
|
1c9c5aeef7 | ||
|
|
23ad540aa5 | ||
|
|
08810c2749 | ||
|
|
a52966f327 | ||
|
|
624220e9a3 | ||
|
|
842b11ed9e | ||
|
|
82267b3f56 | ||
|
|
67fb45a55b | ||
|
|
11186f1dbe | ||
|
|
0116fac201 | ||
|
|
817f4f8e7c | ||
|
|
866313209b | ||
|
|
28e351daab | ||
|
|
893e16d814 | ||
|
|
33470c47fc | ||
|
|
63f7b87572 | ||
|
|
e2901cca1b | ||
|
|
ce8006992a | ||
|
|
0b5413ad83 | ||
|
|
dd7adde387 | ||
|
|
23058b51a1 | ||
|
|
2f90c53ad0 | ||
|
|
c3be7ab4b3 | ||
|
|
a9b8fbc6c6 | ||
|
|
720988ae72 | ||
|
|
b0981a5fae | ||
|
|
ea5deeefbd | ||
|
|
054c98da7c | ||
|
|
dc434d034a | ||
|
|
a4a1b563aa | ||
|
|
3f34b83e0d | ||
|
|
9c63ef39c7 | ||
|
|
9f6106452e | ||
|
|
f9fbfe30e3 | ||
|
|
9a9bf170c6 | ||
|
|
7dd64b4f13 | ||
|
|
b6c0bac96f | ||
|
|
8ede098154 | ||
|
|
2a4a3b786e | ||
|
|
b4e0a2019e | ||
|
|
e14c3f8b59 | ||
|
|
c81f76226d | ||
|
|
edc353cc24 | ||
|
|
dcce519c69 | ||
|
|
0a16dcafc0 | ||
|
|
54d41b7f6f | ||
|
|
0541b84d09 | ||
|
|
1b420f7fe7 | ||
|
|
6a9a82ff9d | ||
|
|
aa36e6b19f | ||
|
|
51cb6aad50 | ||
|
|
b5ce409592 | ||
|
|
2119817e4a | ||
|
|
1efdfe8ea1 | ||
|
|
67d4180825 | ||
|
|
be31e190d2 | ||
|
|
a68cf3c812 | ||
|
|
c2bf9024f8 | ||
|
|
bd72eaad4c | ||
|
|
b5d1575823 | ||
|
|
1d306e5cdc | ||
|
|
b137164fe6 | ||
|
|
67d4ad50e1 | ||
|
|
c71066af4c | ||
|
|
6f0a0981bd | ||
|
|
49a4d9ba37 | ||
|
|
fcdfa741b9 | ||
|
|
e3e395836d | ||
|
|
3bab177d50 | ||
|
|
12abafb917 | ||
|
|
8dc2e47507 | ||
|
|
0be90dedf2 | ||
|
|
daf7c3357c | ||
|
|
e6bab3ceeb | ||
|
|
59387b2ae8 | ||
|
|
e8795859c5 | ||
|
|
bebe60b687 | ||
|
|
ddceec364e | ||
|
|
f8f20440d3 | ||
|
|
f8722faa4e | ||
|
|
d067fa4b73 | ||
|
|
26fbbf48a4 | ||
|
|
d5cc414221 | ||
|
|
b5e51ab937 | ||
|
|
552e55c29f | ||
|
|
a228908c1a | ||
|
|
15fc3b6c04 | ||
|
|
0456badd02 | ||
|
|
d28f154f1c | ||
|
|
399cf5118d | ||
|
|
a87f2640d3 | ||
|
|
a90ef274d7 | ||
|
|
1c02179c5c | ||
|
|
77584493fd | ||
|
|
a36709e638 | ||
|
|
341778ba67 | ||
|
|
8d63cce749 | ||
|
|
ec50b5a007 | ||
|
|
8e7bbcdbe0 | ||
|
|
37ecdc47b3 | ||
|
|
112ca3cc53 | ||
|
|
33b3a1664c | ||
|
|
8a0c02e264 | ||
|
|
31d84c8921 | ||
|
|
34bedbeef1 | ||
|
|
3b1b0985c1 | ||
|
|
e40ace9dea | ||
|
|
4c0e6e2640 | ||
|
|
08b314c37d | ||
|
|
86da9d3c7e | ||
|
|
4e61e25c02 | ||
|
|
5097d52d99 | ||
|
|
368387e1a7 | ||
|
|
09d2185bb1 | ||
|
|
5c02e2bd71 | ||
|
|
fb01389b3d | ||
|
|
aaa81210ce | ||
|
|
51269257ea | ||
|
|
23a853db1e | ||
|
|
9da430ffeb | ||
|
|
cc063124cf | ||
|
|
3b37b89951 | ||
|
|
844b537d1e | ||
|
|
caf1d4a22a | ||
|
|
d8e244df99 | ||
|
|
548e421e27 | ||
|
|
322fa7ea69 | ||
|
|
db6c887795 | ||
|
|
cf7bea0985 | ||
|
|
61fe55168a | ||
|
|
25d7f8808f | ||
|
|
1c4999ec87 | ||
|
|
c726d3f937 | ||
|
|
f70341df1b | ||
|
|
f0b791452e | ||
|
|
adf5600a9b | ||
|
|
6d4ccc5feb | ||
|
|
5f3829d5cc | ||
|
|
770ebdcd4a | ||
|
|
96f690e749 | ||
|
|
eabdd3a8f3 | ||
|
|
50650b966b | ||
|
|
65e34d4989 | ||
|
|
05dad5ab5f | ||
|
|
1a69a2f1bc | ||
|
|
435faafaad | ||
|
|
686b32dc29 | ||
|
|
0e64a22910 | ||
|
|
4f0839f27e | ||
|
|
bb1953267d | ||
|
|
acc630972a | ||
|
|
6a33be3fd8 | ||
|
|
cd8aae4681 | ||
|
|
11456496bd | ||
|
|
f5fc4e345c | ||
|
|
86eef7039f | ||
|
|
f4b2264fcf | ||
|
|
9b36ae2277 | ||
|
|
913ab03963 | ||
|
|
38448e471c | ||
|
|
67231af623 | ||
|
|
e31ef6f06f | ||
|
|
09d188106a | ||
|
|
d8e2116481 | ||
|
|
435dfbb932 | ||
|
|
ba93966474 | ||
|
|
ea8d17cdb2 | ||
|
|
082265fb25 | ||
|
|
d138a7a567 | ||
|
|
ea27d732ab | ||
|
|
9e557ce8ac | ||
|
|
924e28e9b3 | ||
|
|
e5d9af75de | ||
|
|
31c1bf8bc5 | ||
|
|
37d4055036 | ||
|
|
78b1076110 | ||
|
|
0a3c748e41 | ||
|
|
ebf79ef9e2 | ||
|
|
60a73248cd | ||
|
|
abbb7d7ba3 | ||
|
|
59c378089e | ||
|
|
0b789b5f0b | ||
|
|
4382b96a9a | ||
|
|
246e4f35a6 | ||
|
|
99b9370178 | ||
|
|
506062c6b6 | ||
|
|
d634061cd9 | ||
|
|
8353c689ca | ||
|
|
d59d8ff1fe | ||
|
|
e98e6f70ac | ||
|
|
53e442d509 | ||
|
|
134352ed7c | ||
|
|
f7cbfdff06 | ||
|
|
b28ee0819f | ||
|
|
5de626aab8 | ||
|
|
7aad5d486e | ||
|
|
701f155951 | ||
|
|
8c324d7514 | ||
|
|
522958e0e9 | ||
|
|
97390db5f5 | ||
|
|
af920d1427 | ||
|
|
779ebc0537 | ||
|
|
38949b82c3 | ||
|
|
d11386ef26 | ||
|
|
0e0377d1f0 | ||
|
|
55e0dbab27 | ||
|
|
4dc82f2c83 | ||
|
|
1ba5587404 | ||
|
|
835c4b6da3 | ||
|
|
dbd955b61e | ||
|
|
d20e2fd88c | ||
|
|
e0dea89477 | ||
|
|
ccc6b56e35 | ||
|
|
6fc2902895 | ||
|
|
c96e4b40d4 | ||
|
|
37da3e2170 | ||
|
|
2661d3c489 | ||
|
|
b89bbb9281 | ||
|
|
696bf636ed | ||
|
|
40952a788a | ||
|
|
0162e7a0c1 | ||
|
|
6ce099f176 | ||
|
|
476a4bac8e | ||
|
|
63a410a6df | ||
|
|
cca27faa3b | ||
|
|
803e6bd81a | ||
|
|
88269628a2 | ||
|
|
b920d553a0 | ||
|
|
5e2d0d0dfc | ||
|
|
2ae3bd68eb | ||
|
|
9c183f27eb | ||
|
|
8046023e82 | ||
|
|
e328520588 | ||
|
|
7eb079050c | ||
|
|
2fdd5543b2 | ||
|
|
d04164c0a6 | ||
|
|
b047731f82 | ||
|
|
4d91f92a2e | ||
|
|
98505d27b1 | ||
|
|
cd63a58ad9 | ||
|
|
170f8dd092 | ||
|
|
619dcb6a84 | ||
|
|
99ae8ea52e | ||
|
|
dc031c30eb | ||
|
|
1e702439b7 | ||
|
|
8debc42381 | ||
|
|
532d719089 | ||
|
|
b40860aca4 | ||
|
|
2cbe6b5f7f | ||
|
|
d2cc7ccdfa | ||
|
|
2cb183c6d8 | ||
|
|
84026b105f | ||
|
|
a4d0589f10 | ||
|
|
e375f6afce | ||
|
|
5a7bc04816 | ||
|
|
bd1894580e | ||
|
|
9e694c0337 | ||
|
|
c82586db28 | ||
|
|
dd2d466350 | ||
|
|
830da8de0a | ||
|
|
4e5ee333c8 | ||
|
|
9df899eb63 | ||
|
|
ca7491a702 | ||
|
|
1a07129865 | ||
|
|
4fbd67ff99 | ||
|
|
5bc6c50f42 | ||
|
|
063de3801d | ||
|
|
ae65266a4a | ||
|
|
8ed2401e0b | ||
|
|
d2e8ee8269 | ||
|
|
1f996e3b8b | ||
|
|
7108b74105 | ||
|
|
801fe1b604 | ||
|
|
fb44c87597 | ||
|
|
6b9cdbd482 | ||
|
|
0ab98033b5 | ||
|
|
14a2b96609 | ||
|
|
f829b689db | ||
|
|
dfda8b7ed5 | ||
|
|
4388466451 | ||
|
|
5c2f509a52 | ||
|
|
59582f5210 | ||
|
|
e2a8bdbdfb | ||
|
|
0916b943da | ||
|
|
9c7ebc883c | ||
|
|
0ee42b9aa0 | ||
|
|
37b3868ca3 | ||
|
|
a6835ce3f0 | ||
|
|
69c96ad99b | ||
|
|
b72877d59d | ||
|
|
05eb15d4f7 | ||
|
|
f1fec37c79 | ||
|
|
73f6880ff8 | ||
|
|
8a53742f31 | ||
|
|
9be40e85ff | ||
|
|
61079c1eb7 | ||
|
|
1075ee8fc3 | ||
|
|
a28b265197 | ||
|
|
20e534c468 | ||
|
|
da7aa5dc49 | ||
|
|
8f2a43ca0a | ||
|
|
d0909d7810 | ||
|
|
1641999d20 | ||
|
|
e16452037c | ||
|
|
344d79684a | ||
|
|
573a1d9b7b | ||
|
|
25ab57580c | ||
|
|
a332e0e3d1 | ||
|
|
376f1cb139 | ||
|
|
90f80558d7 | ||
|
|
e281994898 | ||
|
|
29fac122e1 | ||
|
|
1dc412eb90 | ||
|
|
3770a4fe0c | ||
|
|
79cda544c8 | ||
|
|
f04b97d890 | ||
|
|
3e9b4ccc45 | ||
|
|
2c3d838dd8 | ||
|
|
7668a3c660 | ||
|
|
5dd45b714a | ||
|
|
8b08895d0f | ||
|
|
8f8d99e3ed | ||
|
|
23474360ec | ||
|
|
81c255c450 | ||
|
|
ef23d52ed7 | ||
|
|
220ab773aa | ||
|
|
e3e5bff7bb | ||
|
|
7b9a841b2a | ||
|
|
40423911ef | ||
|
|
582a70b046 | ||
|
|
5b63590ebf | ||
|
|
2b6510dc19 | ||
|
|
9d49589d73 | ||
|
|
125b66c929 | ||
|
|
5255f1c052 | ||
|
|
a6ba05d60c | ||
|
|
41e963b04b | ||
|
|
6ff75bef29 | ||
|
|
72c16d0d32 | ||
|
|
94653e5c8c | ||
|
|
3e2b2a698d | ||
|
|
ae04f5aee8 | ||
|
|
5c56267662 | ||
|
|
e55ce5504e | ||
|
|
fb1e89d9ef | ||
|
|
bc550a4549 | ||
|
|
ebdea9cf76 | ||
|
|
09ec508f82 | ||
|
|
d06e9ea7f6 | ||
|
|
a36bdc54fd | ||
|
|
0814ea9711 | ||
|
|
daefe839d8 | ||
|
|
e6088dd315 | ||
|
|
fc03d6f332 | ||
|
|
2aeb7a838e | ||
|
|
99ff5dd078 | ||
|
|
49982b49b6 | ||
|
|
fd39c22616 | ||
|
|
9e79722a7f | ||
|
|
17334a1c58 | ||
|
|
c7f0ff11ac | ||
|
|
cd2cc89e6a | ||
|
|
069143092d | ||
|
|
efd41260f2 | ||
|
|
8d2410622c | ||
|
|
60554389b3 | ||
|
|
a940dc7d43 | ||
|
|
06ca68a625 | ||
|
|
5b58e5b158 | ||
|
|
74dd8fe80b | ||
|
|
75ddcda5f3 | ||
|
|
216825b98a | ||
|
|
a96defab86 | ||
|
|
0864b0a1b7 | ||
|
|
8b158d9240 | ||
|
|
f335251c2b | ||
|
|
67bc0b6931 | ||
|
|
e646dd1ed1 | ||
|
|
2b7947f9b0 | ||
|
|
ec0cfb4b3f | ||
|
|
9cdf53019c | ||
|
|
1a04a3eb3a | ||
|
|
105d3995e0 | ||
|
|
8ce3204f93 | ||
|
|
d0f15f1285 | ||
|
|
66d6f67120 | ||
|
|
a106c88054 | ||
|
|
ee784e1ccc | ||
|
|
bb75be0b44 | ||
|
|
2478aea316 | ||
|
|
1e17df5296 | ||
|
|
8583a96519 | ||
|
|
d0c184c7de | ||
|
|
0191acb2b3 | ||
|
|
277a1a32b2 | ||
|
|
7a13f57ab0 | ||
|
|
0c882836d9 | ||
|
|
228be5cd04 | ||
|
|
08cd2fd6e8 | ||
|
|
bc7b086f0f | ||
|
|
e8f3af6981 | ||
|
|
f9c7c7dab7 | ||
|
|
09a17743ad | ||
|
|
4f096a7511 | ||
|
|
2ab2130000 | ||
|
|
66558f7638 | ||
|
|
a6f9ed07e7 | ||
|
|
7268a8736f | ||
|
|
8f6b5676d7 | ||
|
|
ca9422bbe9 | ||
|
|
35d9412559 | ||
|
|
f071c07dd9 | ||
|
|
e5ff3c1ff3 | ||
|
|
f0e8ff0326 | ||
|
|
3b5cda85ff | ||
|
|
420793f9e2 | ||
|
|
cf1dbaf0d8 | ||
|
|
d187cef6b7 | ||
|
|
3b3616afda | ||
|
|
0ffebc25d0 | ||
|
|
478e2b4ebd | ||
|
|
a56ae7539a | ||
|
|
407773bda2 | ||
|
|
823eaa8918 | ||
|
|
a2be42c5ca | ||
|
|
a76b8d66ff | ||
|
|
b7f47d354f | ||
|
|
5d33c93af9 | ||
|
|
4db6859f3f | ||
|
|
45fe1bb16e | ||
|
|
b014facbd3 | ||
|
|
3b4b37f16b | ||
|
|
68d5983a14 | ||
|
|
f2cfcfdf31 | ||
|
|
10b9162dc5 | ||
|
|
c84cc8f8c9 | ||
|
|
78c71bbf0e | ||
|
|
37c2c1bf0b | ||
|
|
c8996418da | ||
|
|
76b29aa629 | ||
|
|
ee521793f8 | ||
|
|
f42e12bc13 | ||
|
|
427451a23f | ||
|
|
af7930d494 | ||
|
|
e2882d37bf | ||
|
|
942d3ee640 | ||
|
|
7b4a82b91a | ||
|
|
056c0a24d9 | ||
|
|
827df04b32 | ||
|
|
e174b31344 | ||
|
|
49959af752 | ||
|
|
c86c0912f8 | ||
|
|
268b0c9365 | ||
|
|
099fe280ba | ||
|
|
f786f60e9c | ||
|
|
f40e1eb1f2 | ||
|
|
8b9da65357 | ||
|
|
2cbbe0d48a | ||
|
|
b6e1fb3ba8 | ||
|
|
bdccffbb8e | ||
|
|
5a85474712 | ||
|
|
f50899c6fa | ||
|
|
4daa909f32 | ||
|
|
4555edf369 | ||
|
|
529ea5bf58 | ||
|
|
fe011de934 | ||
|
|
0653d430a7 | ||
|
|
a587f6e9a0 | ||
|
|
3850e1dbb5 | ||
|
|
91cde072e0 | ||
|
|
2ca4aed566 | ||
|
|
5071da6784 | ||
|
|
4c1e2f3110 | ||
|
|
2727a4dcb3 | ||
|
|
126d4c69e6 |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# An .aiignore file follows the same syntax as a .gitignore file.
|
||||
# .gitignore documentation: https://git-scm.com/docs/gitignore
|
||||
|
||||
# you can ignore files
|
||||
.DS_Store
|
||||
*.log
|
||||
*.tmp
|
||||
*.bin
|
||||
*.prg
|
||||
|
||||
# or folders
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
output/
|
||||
.gradle/
|
||||
.kotlin/
|
||||
+2
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
#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
|
||||
#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
|
||||
@@ -11,3 +11,4 @@ ko_fi: irmen
|
||||
#otechie: # Replace with a single Otechie username
|
||||
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
custom: ['https://paypal.me/irmendejong']
|
||||
@@ -7,10 +7,13 @@ build/
|
||||
dist/
|
||||
output/
|
||||
out/
|
||||
out-new/
|
||||
out-old/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
*.bin
|
||||
*.p8ir
|
||||
*.labels.txt
|
||||
*.vm.txt
|
||||
*.vice-mon-list
|
||||
|
||||
+1575
-7
File diff suppressed because it is too large.
Load diff
Generated
+4
-4
@@ -7,13 +7,13 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinCommonCompilerArguments">
|
||||
<option name="apiVersion" value="2.1" />
|
||||
<option name="languageVersion" value="2.1" />
|
||||
<option name="apiVersion" value="2.2" />
|
||||
<option name="languageVersion" value="2.2" />
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-Xwhen-guards" />
|
||||
<option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.0" />
|
||||
<option name="version" value="2.2.0" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+10
-10
@@ -1,23 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0" />
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+5
-5
@@ -1,11 +1,11 @@
|
||||
<component name="libraryTable">
|
||||
<library name="eclipse.lsp4j" type="repository">
|
||||
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.1" />
|
||||
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.23.1/org.eclipse.lsp4j-0.23.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.23.1/org.eclipse.lsp4j.jsonrpc-0.23.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.27.0/error_prone_annotations-2.27.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.13.1/gson-2.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.38.0/error_prone_annotations-2.38.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
+7
-7
@@ -1,19 +1,19 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-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$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-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$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
|
||||
Generated
+5
-1
@@ -12,6 +12,7 @@
|
||||
<option name="pkg" value="" />
|
||||
<option name="language" value="Java" />
|
||||
<option name="generateListener" value="false" />
|
||||
<option name="generateVisitor" value="true" />
|
||||
</PerGrammarGenerationSettings>
|
||||
</list>
|
||||
</option>
|
||||
@@ -22,7 +23,10 @@
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="openjdk-17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
@@ -17,6 +17,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
[](https://ko-fi.com/H2H6S0FFF)
|
||||
|
||||
PayPal: [](https://paypal.me/irmendejong)
|
||||
|
||||
[](https://prog8.readthedocs.io/)
|
||||
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
@@ -14,7 +17,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
||||
|
||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
|
||||
|
||||
|
||||
Documentation
|
||||
@@ -58,17 +61,23 @@ What does Prog8 provide?
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- Structs and typed pointers
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
- tight control over Zeropage usage
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- programs can be configured to execute in ROM
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- high-level program optimizations
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- conditional branches that map 1:1 to cpu status flags
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``on .. goto`` statement for fast jump tables
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||
- subroutines can return more than one result value
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
||||
- encode strings and characters into petscii or screencodes or even other encodings
|
||||
@@ -88,8 +97,7 @@ What does Prog8 provide?
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "pet32": Commodore PET (limited support)
|
||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
||||
- "neo": Neo6502 (experimental)
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64 OS, Foenix F256, ...
|
||||
- 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)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
; Binary Search Tree.
|
||||
; It's a simple implementation for test/demonstration purposes of the pointer support;
|
||||
; no balancing is done and memory is not freed when elements are removed.
|
||||
|
||||
%import textio
|
||||
|
||||
btree {
|
||||
|
||||
sub benchmark(uword max_time) -> uword {
|
||||
txt.nl()
|
||||
cbm.SETTIM(0,0,0)
|
||||
uword score
|
||||
while cbm.RDTIM16() < max_time {
|
||||
bench_operations()
|
||||
txt.chrout('.')
|
||||
score++
|
||||
}
|
||||
txt.nl()
|
||||
return score
|
||||
}
|
||||
|
||||
sub bench_operations() {
|
||||
arena.freeall()
|
||||
btree.root = 0
|
||||
|
||||
for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716]
|
||||
btree.add(cx16.r0)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
|
||||
void btree.contains(203)
|
||||
void btree.contains(204)
|
||||
void btree.contains(605)
|
||||
void btree.contains(606)
|
||||
|
||||
btree.remove(9999)
|
||||
btree.remove(97)
|
||||
btree.remove(187)
|
||||
btree.remove(203)
|
||||
btree.remove(275)
|
||||
btree.remove(321)
|
||||
btree.remove(520)
|
||||
btree.remove(562)
|
||||
btree.remove(606)
|
||||
btree.remove(719)
|
||||
btree.remove(794)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
}
|
||||
|
||||
struct Node {
|
||||
^^Node left
|
||||
^^Node right
|
||||
uword value
|
||||
}
|
||||
|
||||
^^Node root = 0
|
||||
|
||||
sub add(uword value) {
|
||||
^^Node node = arena.alloc(sizeof(Node))
|
||||
node.value = value
|
||||
node.left = node.right = 0
|
||||
|
||||
if root==0
|
||||
root=node
|
||||
else {
|
||||
^^Node parent = root
|
||||
repeat {
|
||||
if parent.value >= value {
|
||||
if parent.left!=0
|
||||
parent = parent.left
|
||||
else {
|
||||
parent.left = node
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if parent.right!=0
|
||||
parent = parent.right
|
||||
else {
|
||||
parent.right = node
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub contains(uword value) -> bool {
|
||||
^^Node r = root
|
||||
while r!=0 {
|
||||
if r.value==value
|
||||
return true
|
||||
if r.value>value
|
||||
r = r.left
|
||||
else
|
||||
r = r.right
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
ubyte count
|
||||
|
||||
if root!=0
|
||||
count_node(root)
|
||||
|
||||
return count
|
||||
|
||||
sub count_node(^^Node r) {
|
||||
count++
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
count_node(r.left)
|
||||
r = sys.popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
count_node(r.right)
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub remove(uword value) {
|
||||
; note: we don't deallocate the memory from the node, for simplicity sake
|
||||
^^Node n = root
|
||||
^^Node parent = 0
|
||||
while n!=0 {
|
||||
if n.value==value {
|
||||
if n.left==0
|
||||
replacechild(parent, n, n.right)
|
||||
else if n.right==0
|
||||
replacechild(parent, n, n.left)
|
||||
else {
|
||||
; Both left & right subtrees are present.
|
||||
; N = node to delete.
|
||||
; Find N's successor S. (N's right subtree's minimum element)
|
||||
; Attach N's left subtree to S.left (S doesn't have a left child)
|
||||
; Attach N's right subtree to Parent in place of N.
|
||||
^^Node successor = find_successor(n)
|
||||
successor.left = n.left
|
||||
replacechild(parent, n, n.right)
|
||||
}
|
||||
return
|
||||
}
|
||||
parent = n
|
||||
if n.value>value
|
||||
n = n.left
|
||||
else
|
||||
n = n.right
|
||||
}
|
||||
|
||||
sub find_successor(^^Node p) -> ^^Node {
|
||||
^^Node succ = p
|
||||
p = p.right
|
||||
while p!=0 {
|
||||
succ = p
|
||||
p = p.left
|
||||
}
|
||||
return succ
|
||||
}
|
||||
|
||||
sub replacechild(^^Node p, ^^Node child, ^^Node newchild) {
|
||||
if p.left==child
|
||||
p.left = newchild
|
||||
else
|
||||
p.right = newchild
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_inorder() {
|
||||
if root!=0
|
||||
process_tree(root)
|
||||
|
||||
sub process_tree(^^Node r) {
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
process_tree(r.left)
|
||||
r = sys.popw()
|
||||
}
|
||||
cx16.r0 = r.value
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
process_tree(r.right)
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_preorder() {
|
||||
if root!=0
|
||||
process_tree(root,0)
|
||||
|
||||
sub process_tree(^^Node r, ubyte depth) {
|
||||
cx16.r0 = r.value
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
sys.push(depth)
|
||||
process_tree(r.left, depth+1)
|
||||
depth = sys.pop()
|
||||
r = sys.popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
sys.push(depth)
|
||||
process_tree(r.right, depth+1)
|
||||
depth = sys.pop()
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
arena {
|
||||
; extremely trivial arena allocator (that never frees)
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
uword next = buffer
|
||||
|
||||
sub alloc(ubyte size) -> uword {
|
||||
defer next += size
|
||||
return next
|
||||
}
|
||||
|
||||
sub freeall() {
|
||||
next = buffer
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ circles {
|
||||
|
||||
sub draw(bool use_kernal, uword max_time) -> uword {
|
||||
if use_kernal
|
||||
void cx16.set_screen_mode(128)
|
||||
cx16.set_screen_mode(128)
|
||||
else
|
||||
gfx_lores.graphics_mode()
|
||||
|
||||
@@ -33,7 +33,7 @@ circles {
|
||||
}
|
||||
|
||||
if use_kernal
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
else {
|
||||
gfx_lores.text_mode()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
%import sprites
|
||||
%import coroutines
|
||||
%import math
|
||||
|
||||
|
||||
animsprites {
|
||||
uword num_iterations
|
||||
ubyte[64] sx
|
||||
ubyte[64] sy
|
||||
ubyte[64] sc
|
||||
ubyte[64] dx
|
||||
ubyte[64] dy
|
||||
uword maximum_duration
|
||||
|
||||
sub benchmark(uword max_duration) -> uword {
|
||||
maximum_duration = max_duration
|
||||
math.rndseed(1122,9876)
|
||||
cx16.set_screen_mode(3)
|
||||
cx16.mouse_config2(1)
|
||||
sprites.set_mousepointer_hand()
|
||||
repeat 64
|
||||
void coroutines.add(animsprite, 0)
|
||||
cx16.mouse_config2(0)
|
||||
|
||||
cbm.SETTIM(0,0,0)
|
||||
coroutines.run(supervisor)
|
||||
|
||||
sprites.reset(0, 64)
|
||||
return num_iterations
|
||||
}
|
||||
|
||||
sub supervisor() -> bool {
|
||||
if cbm.RDTIM16() >= maximum_duration {
|
||||
coroutines.killall()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub animsprite() {
|
||||
num_iterations++
|
||||
; set up the sprite
|
||||
ubyte sprnum = coroutines.current()
|
||||
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
|
||||
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
|
||||
sx[sprnum] = math.rnd()
|
||||
sy[sprnum] = math.rnd()
|
||||
sc[sprnum] = math.rnd()
|
||||
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
|
||||
; move the sprite around
|
||||
while sc[sprnum]!=0 {
|
||||
animate(sprnum)
|
||||
void coroutines.yield()
|
||||
sprnum = coroutines.current()
|
||||
}
|
||||
|
||||
sub animate(ubyte spr) {
|
||||
defer sc[spr]--
|
||||
sprites.pos(spr, sx[spr], sy[spr])
|
||||
sx[spr] += dx[spr]
|
||||
sy[spr] += dy[spr]
|
||||
}
|
||||
|
||||
; end the task but replace it with a fresh animated sprite task
|
||||
void coroutines.add(animsprite, 0)
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
%import b_queens
|
||||
%import b_textelite
|
||||
%import b_maze
|
||||
%import b_sprites
|
||||
%import b_btree
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
@@ -29,7 +31,7 @@ main {
|
||||
sub start() {
|
||||
ubyte benchmark_number
|
||||
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
|
||||
@@ -66,21 +68,25 @@ main {
|
||||
benchmark_score[benchmark_number] = circles.draw(false, 300)
|
||||
benchmark_number++
|
||||
|
||||
; announce_benchmark("circles with kernal")
|
||||
; benchmark_score[benchmark_number] = circles.draw(true, 300)
|
||||
; benchmark_number++
|
||||
|
||||
announce_benchmark("text-elite")
|
||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("sprites-coroutines-defer")
|
||||
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("btree-struct-pointers")
|
||||
benchmark_score[benchmark_number] = btree.benchmark(200)
|
||||
benchmark_number++
|
||||
|
||||
benchmark_names[benchmark_number] = 0
|
||||
benchmark_score[benchmark_number] = 0
|
||||
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
uword final_score
|
||||
uword total_score
|
||||
benchmark_number = 0
|
||||
txt.print("\nscore benchmark\n\n")
|
||||
do {
|
||||
@@ -88,18 +94,18 @@ main {
|
||||
txt.print_uw(benchmark_score[benchmark_number])
|
||||
txt.column(6)
|
||||
txt.print(benchmark_names[benchmark_number])
|
||||
final_score += benchmark_score[benchmark_number]
|
||||
total_score += benchmark_score[benchmark_number]
|
||||
txt.nl()
|
||||
benchmark_number++
|
||||
} until benchmark_names[benchmark_number]==0
|
||||
|
||||
txt.print("\n\nfinal score : ")
|
||||
txt.print_uw(final_score)
|
||||
txt.nl()
|
||||
txt.print("\n\ntotal score : ")
|
||||
txt.print_uw(total_score)
|
||||
txt.print(" (higher=better)\n")
|
||||
|
||||
sub announce_benchmark(str name) {
|
||||
benchmark_names[benchmark_number] = name
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
|
||||
+13
-1
@@ -1,7 +1,11 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
|
||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.0"
|
||||
kotlin("jvm") version "2.2.20"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -16,6 +20,14 @@ allprojects {
|
||||
compilerOptions {
|
||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
|
||||
// languageVersion.set(KotlinVersion.KOTLIN_2_3)
|
||||
}
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
// enable language features like so:
|
||||
// enableLanguageFeature(LanguageFeature.WhenGuards.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -7,7 +5,7 @@ plugins {
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -21,4 +19,4 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
||||
// note: there are no unit tests in this module!
|
||||
@@ -0,0 +1,32 @@
|
||||
package prog8.code
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||
|
||||
val PROG8_CONTAINER_MODULES = arrayOf(INTERNED_STRINGS_MODULENAME) // option to add more if needed one day
|
||||
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
|
||||
|
||||
/**
|
||||
* Returns the absolute path of the given path,
|
||||
* where links are replaced by the actual directories,
|
||||
* and containing no redundant path elements.
|
||||
* If the path doesn't refer to an existing directory or file on the file system,
|
||||
* it is returned unchanged.
|
||||
*/
|
||||
fun Path.sanitize(): Path {
|
||||
return try {
|
||||
this.toRealPath().normalize()
|
||||
} catch (_: java.nio.file.NoSuchFileException) {
|
||||
this.absolute().normalize()
|
||||
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
||||
} catch (iox: IOException) {
|
||||
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
sealed interface IPtSubroutine {
|
||||
val name: String
|
||||
val scopedName: String
|
||||
}
|
||||
|
||||
class PtAsmSub(
|
||||
name: String,
|
||||
val address: Address?,
|
||||
val clobbers: Set<CpuRegister>,
|
||||
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
|
||||
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
|
||||
val inline: Boolean,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine {
|
||||
|
||||
class Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt)
|
||||
}
|
||||
|
||||
|
||||
class PtSub(
|
||||
name: String,
|
||||
val parameters: List<PtSubroutineParameter>,
|
||||
val returntype: DataType?,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||
init {
|
||||
// params and return value should not be str
|
||||
if(parameters.any{ !it.type.isNumericOrBool })
|
||||
throw AssemblyError("non-numeric/non-bool parameter")
|
||||
if(returntype!=null && !returntype.isNumericOrBool)
|
||||
throw AssemblyError("non-numeric/non-bool returntype $returntype")
|
||||
parameters.forEach { it.parent=this }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtSubroutineParameter(name: String, val type: DataType, val register: RegisterOrPair?, position: Position): PtNamedNode(name, position)
|
||||
|
||||
|
||||
sealed interface IPtAssignment {
|
||||
val children: MutableList<PtNode>
|
||||
val target: PtAssignTarget
|
||||
get() {
|
||||
if(children.size==2)
|
||||
return children[0] as PtAssignTarget
|
||||
else if(children.size<2)
|
||||
throw AssemblyError("incomplete node")
|
||||
else
|
||||
throw AssemblyError("no singular target")
|
||||
}
|
||||
val value: PtExpression
|
||||
get() = children.last() as PtExpression
|
||||
val multiTarget: Boolean
|
||||
get() = children.size>2
|
||||
}
|
||||
|
||||
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
|
||||
|
||||
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
|
||||
|
||||
|
||||
class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) {
|
||||
val identifier: PtIdentifier?
|
||||
get() = children.single() as? PtIdentifier
|
||||
val array: PtArrayIndexer?
|
||||
get() = children.single() as? PtArrayIndexer
|
||||
val memory: PtMemoryByte?
|
||||
get() = children.single() as? PtMemoryByte
|
||||
|
||||
val type: DataType
|
||||
get() {
|
||||
return when(val tgt = children.single()) {
|
||||
is PtIdentifier -> tgt.type
|
||||
is PtArrayIndexer -> tgt.type
|
||||
is PtMemoryByte -> tgt.type
|
||||
else -> throw AssemblyError("weird target $tgt")
|
||||
}
|
||||
}
|
||||
|
||||
infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this)
|
||||
}
|
||||
|
||||
|
||||
class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) {
|
||||
val trueScope: PtNodeGroup
|
||||
get() = children[0] as PtNodeGroup
|
||||
val falseScope: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtForLoop(position: Position) : PtNode(position) {
|
||||
val variable: PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
val iterable: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
val statements: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtIfElse(position: Position) : PtNode(position) {
|
||||
val condition: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val ifScope: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
val elseScope: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
|
||||
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
|
||||
}
|
||||
|
||||
|
||||
class PtJump(position: Position) : PtNode(position) {
|
||||
val target: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
}
|
||||
|
||||
|
||||
class PtRepeatLoop(position: Position) : PtNode(position) {
|
||||
val count: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val statements: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtReturn(position: Position) : PtNode(position) {
|
||||
val hasValue: Boolean
|
||||
get() = 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 align: UInt,
|
||||
val value: PtExpression?,
|
||||
val arraySize: UInt?,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
value?.let {it.parent=this}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
|
||||
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
require(!type.isString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
val value: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val choices: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
|
||||
val values: PtNodeGroup
|
||||
get() = children[0] as PtNodeGroup
|
||||
val statements: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtDefer(position: Position): PtNode(position), IPtStatementContainer
|
||||
@@ -1,16 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.*
|
||||
|
||||
fun verifyFinalAstBeforeAsmGen(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
|
||||
/*
|
||||
walkAst(program) { node, _ ->
|
||||
if(node is PtVariable) {
|
||||
if(node.value!=null) {
|
||||
// require(node.type in ArrayDatatypes || node.type==DataType.STR) { "final check before asmgen: only string and array variables can still have an init value ${node.name} ${node.type} ${node.position}"}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -27,8 +27,6 @@ class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
|
||||
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
||||
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
private val ByteDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
||||
private val ArrayDatatypes = arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
|
||||
|
||||
class FSignature(val pure: Boolean, // does it have side effects?
|
||||
@@ -69,7 +67,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
}
|
||||
CallConvention(listOf(paramConv), returns)
|
||||
}
|
||||
actualParamTypes.size==2 && (actualParamTypes[0] in ByteDatatypes && actualParamTypes[1].isWord) -> {
|
||||
actualParamTypes.size==2 && (actualParamTypes[0].isByte && actualParamTypes[1].isWord) -> {
|
||||
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
|
||||
}
|
||||
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
|
||||
@@ -98,14 +96,14 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||
"prog8_ifelse_bittest_set" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
|
||||
"prog8_ifelse_bittest_notset" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
|
||||
"prog8_lib_structalloc" to FSignature(true, BaseDataType.UWORD),
|
||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
|
||||
"offsetof" to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)),
|
||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||
@@ -135,10 +133,15 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||
"peekbool" to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)),
|
||||
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||
"peekl" to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)),
|
||||
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
|
||||
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"pokebool" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokebowl" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
|
||||
"pokel" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
|
||||
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
||||
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"rsave" to FSignature(false, null),
|
||||
|
||||
@@ -11,6 +11,7 @@ class CompilationOptions(val output: OutputType,
|
||||
val zpAllowed: List<UIntRange>,
|
||||
val floats: Boolean,
|
||||
val noSysInit: Boolean,
|
||||
val romable: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
@@ -31,10 +32,11 @@ class CompilationOptions(val output: OutputType,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var ignoreFootguns: Boolean = false,
|
||||
var outputDir: Path = Path(""),
|
||||
var quiet: Boolean = false,
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
init {
|
||||
compTarget.machine.initializeMemoryAreas(this)
|
||||
compTarget.initializeMemoryAreas(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.util.Objects
|
||||
import java.util.*
|
||||
|
||||
enum class BaseDataType {
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
@@ -13,6 +13,9 @@ enum class BaseDataType {
|
||||
STR, // pass by reference
|
||||
ARRAY, // pass by reference, subtype is the element type
|
||||
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||
POINTER, // typed pointer, subtype is whatever type is pointed to
|
||||
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
|
||||
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
|
||||
UNDEFINED;
|
||||
|
||||
|
||||
@@ -23,16 +26,20 @@ enum class BaseDataType {
|
||||
this.isWord -> other.isByteOrBool
|
||||
this == LONG -> other.isByteOrBool || other.isWord
|
||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||
this.isArray && other.isArray -> false
|
||||
this.isArray -> other != FLOAT
|
||||
this == STR -> other != FLOAT
|
||||
this.isPointer -> other.isByteOrBool
|
||||
else -> true
|
||||
}
|
||||
|
||||
fun equalsSize(other: BaseDataType) =
|
||||
when {
|
||||
this == other -> true
|
||||
this.isArray && other.isArray -> true
|
||||
this.isByteOrBool -> other.isByteOrBool
|
||||
this.isWord -> other.isWord
|
||||
this.isWord -> other.isWord || other.isPointer
|
||||
this.isPointer -> other.isWord
|
||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
this == STR && other.isArray -> true
|
||||
this.isArray && other == STR -> true
|
||||
@@ -48,114 +55,192 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
|
||||
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
|
||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER // pointer arrays are also always stored as split uwords
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
|
||||
|
||||
|
||||
sealed class SubType(val dt: BaseDataType) {
|
||||
companion object {
|
||||
private val types by lazy {
|
||||
// lazy because of static initialization order
|
||||
mapOf(
|
||||
BaseDataType.UBYTE to SubUnsignedByte,
|
||||
BaseDataType.BYTE to SubSignedByte,
|
||||
BaseDataType.UWORD to SubUnsignedWord,
|
||||
BaseDataType.WORD to SubSignedWord,
|
||||
BaseDataType.FLOAT to SubFloat,
|
||||
BaseDataType.BOOL to SubBool
|
||||
)}
|
||||
|
||||
fun forDt(dt: BaseDataType) = types.getValue(dt)
|
||||
}
|
||||
interface ISubType {
|
||||
val scopedNameString: String
|
||||
fun memsize(sizer: IMemSizer): Int
|
||||
fun sameas(other: ISubType): Boolean
|
||||
fun getFieldType(name: String): DataType?
|
||||
}
|
||||
|
||||
data object SubUnsignedByte: SubType(BaseDataType.UBYTE)
|
||||
data object SubSignedByte: SubType(BaseDataType.BYTE)
|
||||
data object SubUnsignedWord: SubType(BaseDataType.UWORD)
|
||||
data object SubSignedWord: SubType(BaseDataType.WORD)
|
||||
data object SubBool: SubType(BaseDataType.BOOL)
|
||||
data object SubFloat: SubType(BaseDataType.FLOAT)
|
||||
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List<String>?=null) {
|
||||
|
||||
init {
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub.dt == BaseDataType.UWORD || sub.dt == BaseDataType.WORD)
|
||||
when {
|
||||
base.isPointerArray -> {
|
||||
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
|
||||
}
|
||||
base.isArray -> {
|
||||
require(sub != null && subType==null && subTypeFromAntlr==null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"}
|
||||
else -> {
|
||||
require(sub == null || (subType == null && subTypeFromAntlr == null)) {
|
||||
"sub and subtype can't both be set"
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(base==BaseDataType.STR)
|
||||
require(sub?.dt==BaseDataType.UBYTE) { "STR subtype should be ubyte" }
|
||||
else
|
||||
require(sub == null)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DataType) return false
|
||||
return base == other.base && sub == other.sub
|
||||
return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||
override fun hashCode(): Int = Objects.hash(base, sub, subType)
|
||||
|
||||
fun setActualSubType(actualSubType: ISubType) {
|
||||
subType = actualSubType
|
||||
subTypeFromAntlr = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, SubUnsignedByte),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
fun forDt(dt: BaseDataType): DataType {
|
||||
if(dt.isStructInstance)
|
||||
TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
|
||||
return simpletypes.getValue(dt)
|
||||
}
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && elementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, SubType.forDt(actualElementDt))
|
||||
else
|
||||
DataType(BaseDataType.ARRAY, SubType.forDt(actualElementDt))
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid basic element dt $elementDt")
|
||||
}
|
||||
}
|
||||
|
||||
fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
|
||||
fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
|
||||
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
|
||||
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
|
||||
|
||||
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
|
||||
fun pointer(dt: DataType): DataType = if(dt.isBasic)
|
||||
DataType(BaseDataType.POINTER, dt.base, null)
|
||||
else
|
||||
DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
|
||||
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType)
|
||||
fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
|
||||
fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
|
||||
fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
|
||||
}
|
||||
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||
else arrayFor(base, false)
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!.dt)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
when {
|
||||
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
|
||||
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
|
||||
else -> throw IllegalArgumentException("not an array")
|
||||
}
|
||||
|
||||
fun typeForAddressOf(msb: Boolean): DataType {
|
||||
if (isUndefined)
|
||||
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
|
||||
else {
|
||||
if (isBasic)
|
||||
return pointer(base)
|
||||
if (isString)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
if (isPointer)
|
||||
return UWORD
|
||||
if (isArray) {
|
||||
if (msb || isSplitWordArray)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
val elementDt = elementType()
|
||||
require(elementDt.isBasic)
|
||||
return pointer(elementDt)
|
||||
}
|
||||
if (subType != null)
|
||||
return pointer(this)
|
||||
return UWORD
|
||||
}
|
||||
}
|
||||
|
||||
fun dereference(): DataType {
|
||||
require(isPointer || isUnsignedWord)
|
||||
return when {
|
||||
isUnsignedWord -> forDt(BaseDataType.UBYTE)
|
||||
sub!=null -> forDt(sub)
|
||||
subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
|
||||
subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
|
||||
else -> throw IllegalArgumentException("cannot dereference this pointer type")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
SubBool -> "bool[]"
|
||||
SubFloat -> "float[]"
|
||||
SubSignedByte -> "byte[]"
|
||||
SubSignedWord -> "word[]"
|
||||
SubUnsignedByte -> "ubyte[]"
|
||||
SubUnsignedWord -> "uword[]"
|
||||
null -> throw IllegalArgumentException("invalid sub type")
|
||||
BaseDataType.BOOL -> "bool[]"
|
||||
BaseDataType.FLOAT -> "float[]"
|
||||
BaseDataType.BYTE -> "byte[]"
|
||||
BaseDataType.WORD -> "word[]"
|
||||
BaseDataType.UBYTE -> "ubyte[]"
|
||||
BaseDataType.UWORD -> "uword[]"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
SubSignedWord -> "word[] (split)"
|
||||
SubUnsignedWord -> "uword[] (split)"
|
||||
BaseDataType.WORD -> "word[] (split)"
|
||||
BaseDataType.UWORD -> "uword[] (split)"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}"
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
@@ -168,21 +253,45 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}"
|
||||
subType!=null -> "^^${subType!!.scopedNameString}"
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}"
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
when {
|
||||
sub!=null -> sub.name.lowercase()
|
||||
subType!=null -> subType!!.scopedNameString
|
||||
subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".")
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}["
|
||||
subType!=null -> "^^${subType!!.scopedNameString}["
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}["
|
||||
else -> "????? ["
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
SubUnsignedByte -> "ubyte["
|
||||
SubUnsignedWord -> "@nosplit uword["
|
||||
SubBool -> "bool["
|
||||
SubSignedByte -> "byte["
|
||||
SubSignedWord -> "@nosplit word["
|
||||
SubFloat -> "float["
|
||||
null -> throw IllegalArgumentException("invalid sub type")
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
BaseDataType.UWORD -> "@nosplit uword["
|
||||
BaseDataType.BOOL -> "bool["
|
||||
BaseDataType.BYTE -> "byte["
|
||||
BaseDataType.WORD -> "@nosplit word["
|
||||
BaseDataType.FLOAT -> "float["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
SubUnsignedWord -> "uword["
|
||||
SubSignedWord -> "word["
|
||||
BaseDataType.UWORD -> "uword["
|
||||
BaseDataType.WORD -> "word["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
@@ -195,24 +304,37 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
|
||||
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
|
||||
targetType.isPointer -> this.isUnsignedWord || this == targetType
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
|
||||
BaseDataType.ARRAY_POINTER -> false
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean {
|
||||
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
|
||||
return base.largerSizeThan(other.base)
|
||||
}
|
||||
fun equalsSize(other: DataType): Boolean {
|
||||
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
|
||||
return base.equalsSize(other.base)
|
||||
}
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
// note: for pointer types, size() doesn't return the size of the pointer itself but the size of the thing it points to
|
||||
fun size(memsizer: IMemSizer): Int = if(sub!=null) {
|
||||
memsizer.memorySize(sub)
|
||||
} else if(subType!=null) {
|
||||
subType!!.memsize(memsizer)
|
||||
} else {
|
||||
memsizer.memorySize(base)
|
||||
}
|
||||
|
||||
val isBasic = sub==null && subType==null && subTypeFromAntlr==null
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
@@ -228,22 +350,27 @@ class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isBoolArray = base.isArray && sub?.dt == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub?.dt == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && sub?.dt == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && (sub?.dt == BaseDataType.UWORD || sub?.dt == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && sub?.dt == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && sub?.dt == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && sub?.dt == BaseDataType.FLOAT
|
||||
val isPointer = base.isPointer
|
||||
val isPointerToByte = base.isPointer && sub?.isByteOrBool==true
|
||||
val isPointerToWord = base.isPointer && sub?.isWord==true
|
||||
val isStructInstance = base.isStructInstance
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
val isFloat = base == BaseDataType.FLOAT
|
||||
val isLong = base == BaseDataType.LONG
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE))
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||
val isSplitWordArray = base.isSplitWordArray
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.WORD
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
@@ -294,10 +421,13 @@ enum class RegisterOrPair {
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
||||
else -> throw IllegalArgumentException("invalid register param type")
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
||||
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
enum class Statusflag {
|
||||
@@ -342,7 +472,8 @@ val CpuRegisters = arrayOf(
|
||||
enum class OutputType {
|
||||
RAW,
|
||||
PRG,
|
||||
XEX
|
||||
XEX,
|
||||
LIBRARY
|
||||
}
|
||||
|
||||
enum class CbmPrgLauncherType {
|
||||
|
||||
@@ -1,8 +1,43 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65C02,
|
||||
VIRTUAL
|
||||
}
|
||||
|
||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
val name: String
|
||||
val machine: IMachineDefinition
|
||||
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val PROGRAM_MEMTOP_ADDRESS: UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
val BSSGOLDENRAM_START: UInt
|
||||
val BSSGOLDENRAM_END: UInt
|
||||
|
||||
val cpu: CpuType
|
||||
var zeropage: Zeropage
|
||||
var golden: GoldenRam
|
||||
val libraryPath: Path?
|
||||
val customLauncher: List<String>
|
||||
val additionalAssemblerOptions: List<String>
|
||||
val defaultOutputType: OutputType
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
|
||||
fun convertFloatToBytes(num: Double): List<UByte>
|
||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||
|
||||
@@ -13,4 +13,6 @@ interface IErrorReporter {
|
||||
}
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun printSingleError(errormessage: String)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65c02,
|
||||
VIRTUAL
|
||||
}
|
||||
|
||||
interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val PROGRAM_MEMTOP_ADDRESS: UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
val BSSGOLDENRAM_START: UInt
|
||||
val BSSGOLDENRAM_END: UInt
|
||||
|
||||
val cpu: CpuType
|
||||
var zeropage: Zeropage
|
||||
var golden: GoldenRam
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
|
||||
fun convertFloatToBytes(num: Double): List<UByte>
|
||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
}
|
||||
@@ -2,5 +2,14 @@ package prog8.code.core
|
||||
|
||||
interface IMemSizer {
|
||||
fun memorySize(dt: DataType, numElements: Int?): Int
|
||||
fun memorySize(dt: SubType): Int
|
||||
|
||||
fun memorySize(dt: BaseDataType): Int {
|
||||
if(dt.isPassByRef)
|
||||
return memorySize(DataType.UWORD, null) // a pointer size
|
||||
try {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
} catch (x: NoSuchElementException) {
|
||||
throw IllegalArgumentException(x.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@ enum class Encoding(val prefix: String) {
|
||||
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
|
||||
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
|
||||
CP437("cp437"), // cx16 (ibm pc, codepage 437)
|
||||
KATAKANA("kata") // cx16 (katakana)
|
||||
KATAKANA("kata"), // cx16 (katakana)
|
||||
C64OS("c64os") // c64 (C64 OS)
|
||||
}
|
||||
|
||||
interface IStringEncoding {
|
||||
|
||||
@@ -22,9 +22,10 @@ abstract class MemoryAllocator(protected val options: CompilationOptions) {
|
||||
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
|
||||
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
|
||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word
|
||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word
|
||||
abstract val SCRATCH_PTR : UInt // temp storage for a pointer
|
||||
|
||||
|
||||
// the variables allocated into Zeropage.
|
||||
@@ -38,7 +39,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
for (reserved in options.zpReserved)
|
||||
reserve(reserved)
|
||||
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +73,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
val size: Int =
|
||||
when {
|
||||
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isPointer -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isString || datatype.isArray -> {
|
||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||
if(position!=null)
|
||||
@@ -82,7 +84,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
}
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
|
||||
if(position!=null)
|
||||
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||
else
|
||||
@@ -122,6 +124,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
datatype.isString -> VarAllocation(address, datatype, size)
|
||||
datatype.isArray -> VarAllocation(address, datatype, size)
|
||||
datatype.isPointer -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
@@ -133,8 +136,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
require(size>0)
|
||||
return free.containsAll((address until address+size.toUInt()).toList())
|
||||
}
|
||||
|
||||
abstract fun allocateCx16VirtualRegisters()
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +157,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
||||
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
options.compTarget.memorySize(DataType.FLOAT, null)
|
||||
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||
}
|
||||
else -> throw MemAllocationError("weird dt")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.sanitize
|
||||
import prog8.code.source.SourceCode
|
||||
import java.nio.file.InvalidPathException
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||
@@ -13,7 +13,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
|
||||
if(SourceCode.isLibraryResource(file))
|
||||
return "$file:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
val path = Path(file).sanitize().toString()
|
||||
"file://$path:$line:$startCol:"
|
||||
} catch(_: InvalidPathException) {
|
||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.sanitize
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
|
||||
|
||||
@@ -9,11 +11,23 @@ import kotlin.io.path.Path
|
||||
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
|
||||
|
||||
object ImportFileSystem {
|
||||
fun getFile(path: Path): SourceCode {
|
||||
val cached = cache[path.toString()]
|
||||
if (cached != null) return cached
|
||||
val file = SourceCode.File(path)
|
||||
cache[path.toString()] = file
|
||||
|
||||
fun expandTilde(path: String): String = if (path.startsWith("~")) {
|
||||
val userHome = System.getProperty("user.home")
|
||||
userHome + path.drop(1)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
|
||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||
|
||||
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||
val normalized = path.sanitize()
|
||||
val cached = cache[normalized.toString()]
|
||||
if (cached != null)
|
||||
return cached
|
||||
val file = SourceCode.File(normalized, isLibrary)
|
||||
cache[normalized.toString()] = file
|
||||
return file
|
||||
}
|
||||
|
||||
@@ -34,11 +48,11 @@ object ImportFileSystem {
|
||||
val cached = cache[position.file]
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
val path = Path(position.file).toAbsolutePath().normalize()
|
||||
val path = Path(position.file).sanitize()
|
||||
val cached2 = cache[path.toString()]
|
||||
if(cached2 != null)
|
||||
return getLine(cached2, position.line)
|
||||
throw NoSuchElementException("cannot get source line $position")
|
||||
throw NoSuchElementException("cannot get source line $position, with path $path")
|
||||
}
|
||||
|
||||
private fun getLine(code: SourceCode, lineIndex: Int): String {
|
||||
@@ -55,6 +69,6 @@ object ImportFileSystem {
|
||||
|
||||
private class LineSpan(val start: Int, val end: Int)
|
||||
|
||||
private val cache = mutableMapOf<String, SourceCode>()
|
||||
private val cache = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER)
|
||||
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.sanitize
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.text.Normalizer
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.readText
|
||||
|
||||
/**
|
||||
@@ -21,6 +23,11 @@ sealed class SourceCode {
|
||||
*/
|
||||
abstract val isFromFilesystem: Boolean
|
||||
|
||||
/**
|
||||
* Whether this [SourceCode] instance was created from a library module file
|
||||
*/
|
||||
abstract val isFromLibrary: Boolean
|
||||
|
||||
/**
|
||||
* The logical name of the source code unit. Usually the module's name.
|
||||
*/
|
||||
@@ -52,8 +59,8 @@ sealed class SourceCode {
|
||||
*/
|
||||
private const val LIBRARYFILEPREFIX = "library:"
|
||||
private const val STRINGSOURCEPREFIX = "string:"
|
||||
val curdir: Path = Path(".").toAbsolutePath()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
||||
val curdir: Path = Path(".").absolute()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
|
||||
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||
@@ -75,6 +82,7 @@ sealed class SourceCode {
|
||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val isFromLibrary = false
|
||||
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||
override val name = "<unnamed-text>"
|
||||
}
|
||||
@@ -88,7 +96,7 @@ sealed class SourceCode {
|
||||
* @throws NoSuchFileException if the file does not exist
|
||||
* @throws FileSystemException if the file cannot be read
|
||||
*/
|
||||
internal class File(path: Path): SourceCode() {
|
||||
internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() {
|
||||
override val text: String
|
||||
override val origin: String
|
||||
override val name: String
|
||||
@@ -119,6 +127,7 @@ sealed class SourceCode {
|
||||
|
||||
override val isFromResources = true
|
||||
override val isFromFilesystem = false
|
||||
override val isFromLibrary = true
|
||||
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
||||
override val text: String
|
||||
override val name: String
|
||||
@@ -145,6 +154,7 @@ sealed class SourceCode {
|
||||
class Generated(override val name: String) : SourceCode() {
|
||||
override val isFromResources: Boolean = false
|
||||
override val isFromFilesystem: Boolean = false
|
||||
override val isFromLibrary: Boolean = false
|
||||
override val origin: String = name
|
||||
override val text: String = "<generated code node, no text representation>"
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.atari.AtariMachineDefinition
|
||||
|
||||
|
||||
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = AtariMachineDefinition()
|
||||
override val defaultEncoding = Encoding.ATASCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "atari"
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,81 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.c128.C128MachineDefinition
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.zp.C128Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
class C128Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val machine = C128MachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "c128"
|
||||
}
|
||||
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO address?
|
||||
override val BSSHIGHRAM_END = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_START = 0u // TODO address?
|
||||
override val BSSGOLDENRAM_END = 0u // TODO address?
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
if(!quiet)
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C128Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +1,95 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.c64.C64MachineDefinition
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.zp.C64Zeropage
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
class C64Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val machine = C64MachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "c64"
|
||||
|
||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
||||
}
|
||||
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
|
||||
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = 0xcfdfu
|
||||
override val BSSGOLDENRAM_START = 0u // no golden ram on C64
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
if(!quiet)
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
|
||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(_: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +98,6 @@ val CompilationTargets = listOf(
|
||||
C128Target.NAME,
|
||||
Cx16Target.NAME,
|
||||
PETTarget.NAME,
|
||||
AtariTarget.NAME,
|
||||
Neo6502Target.NAME,
|
||||
VMTarget.NAME
|
||||
)
|
||||
|
||||
@@ -36,8 +106,6 @@ fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
|
||||
C128Target.NAME -> C128Target()
|
||||
Cx16Target.NAME -> Cx16Target()
|
||||
PETTarget.NAME -> PETTarget()
|
||||
AtariTarget.NAME -> AtariTarget()
|
||||
VMTarget.NAME -> VMTarget()
|
||||
Neo6502Target.NAME -> Neo6502Target()
|
||||
else -> throw IllegalArgumentException("invalid compilation target")
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.ImportFileSystem.expandTilde
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.zp.ConfigurableZeropage
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.inputStream
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
|
||||
|
||||
class ConfigFileTarget(
|
||||
override val name: String,
|
||||
override val defaultEncoding: Encoding,
|
||||
override val cpu: CpuType,
|
||||
override val PROGRAM_LOAD_ADDRESS: UInt,
|
||||
override val PROGRAM_MEMTOP_ADDRESS: UInt,
|
||||
override val STARTUP_CODE_RESERVED_SIZE: UInt,
|
||||
override val BSSHIGHRAM_START: UInt,
|
||||
override val BSSHIGHRAM_END: UInt,
|
||||
override val BSSGOLDENRAM_START: UInt,
|
||||
override val BSSGOLDENRAM_END: UInt,
|
||||
override val defaultOutputType: OutputType,
|
||||
override val libraryPath: Path,
|
||||
override val customLauncher: List<String>,
|
||||
override val additionalAssemblerOptions: List<String>,
|
||||
val ioAddresses: List<UIntRange>,
|
||||
val zpScratchB1: UInt,
|
||||
val zpScratchReg: UInt,
|
||||
val zpScratchW1: UInt,
|
||||
val zpScratchW2: UInt,
|
||||
val zpScratchPtr: UInt,
|
||||
val virtualregistersStart: UInt,
|
||||
val zpFullsafe: List<UIntRange>,
|
||||
val zpKernalsafe: List<UIntRange>,
|
||||
val zpBasicsafe: List<UIntRange>
|
||||
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||
|
||||
companion object {
|
||||
|
||||
private fun Properties.getString(property: String): String {
|
||||
val value = this.getProperty(property, null)
|
||||
if(value!=null)
|
||||
return value
|
||||
throw NoSuchElementException("string property '$property' not found in config file")
|
||||
}
|
||||
|
||||
private fun Properties.getInteger(property: String): UInt {
|
||||
val value = this.getProperty(property, null)
|
||||
if(value!=null) return parseInt(value)
|
||||
throw NoSuchElementException("integer property '$property' not found in config file")
|
||||
}
|
||||
|
||||
private fun parseInt(value: String): UInt {
|
||||
if(value.startsWith("0x"))
|
||||
return value.drop(2).toUInt(16)
|
||||
if(value.startsWith("$"))
|
||||
return value.drop(1).toUInt(16)
|
||||
if(value.startsWith("%"))
|
||||
return value.drop(1).toUInt(2)
|
||||
return value.toUInt()
|
||||
}
|
||||
|
||||
private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> {
|
||||
val rangesStr = props.getString(key)
|
||||
if(rangesStr.isBlank())
|
||||
return emptyList()
|
||||
val result = mutableListOf<UIntRange>()
|
||||
val ranges = rangesStr.split(",").map { it.trim() }
|
||||
for(r in ranges) {
|
||||
if ('-' in r) {
|
||||
val (fromStr, toStr) = r.split("-")
|
||||
val from = parseInt(fromStr.trim())
|
||||
val to = parseInt(toStr.trim())
|
||||
result.add(from..to)
|
||||
} else {
|
||||
val address = parseInt(r)
|
||||
result.add(address..address)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun fromConfigFile(configfile: Path): ConfigFileTarget {
|
||||
val props = Properties()
|
||||
props.load(configfile.inputStream())
|
||||
|
||||
val cpuString = props.getString("cpu").uppercase()
|
||||
val cpuType = try {
|
||||
CpuType.valueOf(cpuString)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
CpuType.valueOf("CPU$cpuString")
|
||||
}
|
||||
val ioAddresses = parseAddressRanges("io_regions", props)
|
||||
val zpFullsafe = parseAddressRanges("zp_fullsafe", props)
|
||||
val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props)
|
||||
val zpBasicsafe = parseAddressRanges("zp_basicsafe", props)
|
||||
|
||||
val libraryPath = expandTilde(Path(props.getString("library")))
|
||||
if(!libraryPath.isDirectory())
|
||||
throw IOException("invalid library path: $libraryPath")
|
||||
|
||||
val customLauncherStr = props.getProperty("custom_launcher_code", null)
|
||||
val customLauncher =
|
||||
if(customLauncherStr?.isNotBlank()==true)
|
||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||
else emptyList()
|
||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||
|
||||
return ConfigFileTarget(
|
||||
configfile.nameWithoutExtension,
|
||||
Encoding.entries.first { it.prefix==props.getString("encoding") },
|
||||
cpuType,
|
||||
props.getInteger("load_address"),
|
||||
props.getInteger("memtop"),
|
||||
0u, // used only in a very specific error condition check in a certain scenario...
|
||||
props.getInteger("bss_highram_start"),
|
||||
props.getInteger("bss_highram_end"),
|
||||
props.getInteger("bss_goldenram_start"),
|
||||
props.getInteger("bss_goldenram_end"),
|
||||
defaultOutputType,
|
||||
libraryPath,
|
||||
customLauncher,
|
||||
if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "),
|
||||
ioAddresses,
|
||||
props.getInteger("zp_scratch_b1"),
|
||||
props.getInteger("zp_scratch_reg"),
|
||||
props.getInteger("zp_scratch_w1"),
|
||||
props.getInteger("zp_scratch_w2"),
|
||||
props.getInteger("zp_scratch_ptr"),
|
||||
props.getInteger("virtual_registers"),
|
||||
zpFullsafe,
|
||||
zpKernalsafe,
|
||||
zpBasicsafe,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO floats are not yet supported here, just enter some values
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 8
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam // TODO this is not yet used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it }
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = ConfigurableZeropage(
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr,
|
||||
virtualregistersStart,
|
||||
zpBasicsafe,
|
||||
zpKernalsafe,
|
||||
zpFullsafe,
|
||||
compilerOptions
|
||||
)
|
||||
// note: there's no golden ram yet
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,94 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
import prog8.code.target.cx16.CX16MachineDefinition
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.zp.CX16Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val machine = CX16MachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "cx16"
|
||||
}
|
||||
|
||||
|
||||
override val cpu = CpuType.CPU65C02
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||
override val BSSGOLDENRAM_START = 0x0400u
|
||||
override val BSSGOLDENRAM_END = 0x07ffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
val emulator: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulator = "x16emu"
|
||||
extraArgs = listOf("-debug")
|
||||
}
|
||||
2 -> {
|
||||
emulator = "box16"
|
||||
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||
}
|
||||
else -> {
|
||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if(!quiet)
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+2
-3
@@ -1,10 +1,9 @@
|
||||
package prog8.code.target.cbm
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
|
||||
|
||||
companion object {
|
||||
@@ -19,7 +18,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
|
||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||
|
||||
val flt = num.toDouble()
|
||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||
if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||
if (flt == 0.0)
|
||||
return zero
|
||||
@@ -1,37 +0,0 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.neo6502.Neo6502MachineDefinition
|
||||
|
||||
|
||||
class Neo6502Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = Neo6502MachineDefinition()
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
|
||||
companion object {
|
||||
const val NAME = "neo"
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IMemSizer
|
||||
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * floatsize
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
return numElements // treat it as the size of the given string with the length
|
||||
?: 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isPointer -> 2 // pointer is just a uword
|
||||
dt.isStructInstance -> dt.subType!!.memsize(this)
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +1,80 @@
|
||||
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
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import prog8.code.target.zp.PETZeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
class PETTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val machine = PETMachineDefinition()
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "pet32"
|
||||
}
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u
|
||||
override val BSSHIGHRAM_END = 0u
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
if(!quiet)
|
||||
println("\nStarting PET emulator...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = PETZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +1,109 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VMTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(false),
|
||||
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = VirtualMachineDefinition()
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
override val libraryPath = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "virtual"
|
||||
const val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
override lateinit var golden: GoldenRam // not actually used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number): String {
|
||||
// little endian binary representation
|
||||
val bits = num.toDouble().toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { "$$it" }
|
||||
return parts.joinToString(", ")
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val bits = num.toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
||||
return parts
|
||||
}
|
||||
|
||||
}
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==8) { "need 8 bytes" }
|
||||
val b0 = bytes[0].toLong() shl (8*7)
|
||||
val b1 = bytes[1].toLong() shl (8*6)
|
||||
val b2 = bytes[2].toLong() shl (8*5)
|
||||
val b3 = bytes[3].toLong() shl (8*4)
|
||||
val b4 = bytes[4].toLong() shl (8*3)
|
||||
val b5 = bytes[5].toLong() shl (8*2)
|
||||
val b6 = bytes[6].toLong() shl (8*1)
|
||||
val b7 = bytes[7].toLong() shl (8*0)
|
||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(!quiet)
|
||||
println("\nStarting Virtual Machine...")
|
||||
|
||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText(), quiet)
|
||||
else
|
||||
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(irSource: String, quiet: Boolean)
|
||||
}
|
||||
|
||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
override val SCRATCH_B1: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_REG: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W1: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W2: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_PTR: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package prog8.code.target.atari
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class AtariMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // TODO what's memtop?
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
val emulatorName: String
|
||||
val cmdline: List<String>
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulatorName = "atari800"
|
||||
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
|
||||
}
|
||||
2 -> {
|
||||
emulatorName = "altirra"
|
||||
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
|
||||
}
|
||||
else -> {
|
||||
System.err.println("Atari target only supports atari800 and altirra emulators.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO monlist?
|
||||
|
||||
println("\nStarting Atari800XL emulator $emulatorName...")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = AtariZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package prog8.code.target.atari
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
|
||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
|
||||
free.addAll(0x00u..0xffu)
|
||||
// TODO atari free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x80u..0xffu) // TODO
|
||||
}
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.FLOATSAFE -> {
|
||||
free.addAll(0x80u..0xffu) // TODO
|
||||
free.removeAll(0xd4u .. 0xefu) // floating point storage
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if atari can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package prog8.code.target.c128
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xff00u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C128Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package prog8.code.target.c64
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
|
||||
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = 0xcfffu
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(_: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package prog8.code.target.cbm
|
||||
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.SubType
|
||||
|
||||
|
||||
internal object CbmMemorySizer: IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * Mflpt5.FLOAT_MEM_SIZE
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> Mflpt5.FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package prog8.code.target.cx16
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU65c02
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
|
||||
override val BSSGOLDENRAM_START = 0x0400u
|
||||
override val BSSGOLDENRAM_END = 0x07ffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
val emulator: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulator = "x16emu"
|
||||
extraArgs = listOf("-debug")
|
||||
}
|
||||
2 -> {
|
||||
emulator = "box16"
|
||||
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||
}
|
||||
else -> {
|
||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ object AtasciiEncoding {
|
||||
'▖',
|
||||
|
||||
// $10
|
||||
'♣',
|
||||
'♣',
|
||||
'┌',
|
||||
'─',
|
||||
'┼',
|
||||
@@ -62,7 +62,7 @@ object AtasciiEncoding {
|
||||
'/',
|
||||
|
||||
// $30
|
||||
'0',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
@@ -80,7 +80,7 @@ object AtasciiEncoding {
|
||||
'?',
|
||||
|
||||
// $40
|
||||
'@',
|
||||
'@',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
@@ -197,6 +197,7 @@ object AtasciiEncoding {
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\r' -> 0x9bu
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
|
||||
object C64osEncoding {
|
||||
|
||||
// decoding: from C64 OS Screencodes (0-255) to unicode
|
||||
// character table from:
|
||||
// https://www.c64os.com/c64os/usersguide/appendices#charactersets
|
||||
|
||||
private val decodingC64os = charArrayOf(
|
||||
'@' , // @ 0x00 -> COMMERCIAL AT
|
||||
'a' , // a 0x01 -> LATIN SMALL LETTER A
|
||||
'b' , // b 0x02 -> LATIN SMALL LETTER B
|
||||
'c' , // c 0x03 -> LATIN SMALL LETTER C
|
||||
'd' , // d 0x04 -> LATIN SMALL LETTER D
|
||||
'e' , // e 0x05 -> LATIN SMALL LETTER E
|
||||
'f' , // f 0x06 -> LATIN SMALL LETTER F
|
||||
'g' , // g 0x07 -> LATIN SMALL LETTER G
|
||||
'h' , // h 0x08 -> LATIN SMALL LETTER H
|
||||
'i' , // i 0x09 -> LATIN SMALL LETTER I
|
||||
'j' , // j 0x0A -> LATIN SMALL LETTER J
|
||||
'k' , // k 0x0B -> LATIN SMALL LETTER K
|
||||
'l' , // l 0x0C -> LATIN SMALL LETTER L
|
||||
'm' , // m 0x0D -> LATIN SMALL LETTER M
|
||||
'n' , // n 0x0E -> LATIN SMALL LETTER N
|
||||
'o' , // o 0x0F -> LATIN SMALL LETTER O
|
||||
'p' , // p 0x10 -> LATIN SMALL LETTER P
|
||||
'q' , // q 0x11 -> LATIN SMALL LETTER Q
|
||||
'r' , // r 0x12 -> LATIN SMALL LETTER R
|
||||
's' , // s 0x13 -> LATIN SMALL LETTER S
|
||||
't' , // t 0x14 -> LATIN SMALL LETTER T
|
||||
'u' , // u 0x15 -> LATIN SMALL LETTER U
|
||||
'v' , // v 0x16 -> LATIN SMALL LETTER V
|
||||
'w' , // w 0x17 -> LATIN SMALL LETTER W
|
||||
'x' , // x 0x18 -> LATIN SMALL LETTER X
|
||||
'y' , // y 0x19 -> LATIN SMALL LETTER Y
|
||||
'z' , // z 0x1A -> LATIN SMALL LETTER Z
|
||||
'[' , // [ 0x1B -> LEFT SQUARE BRACKET
|
||||
'\\' , // \ 0x1C -> REVERSE SOLIDUS
|
||||
']' , // ] 0x1D -> RIGHT SQUARE BRACKET
|
||||
'^' , // ^ 0x1E -> CIRCUMFLEX
|
||||
'_' , // _ 0x1F -> UNDERSCORE
|
||||
' ' , // 0x20 -> SPACE
|
||||
'!' , // ! 0x21 -> EXCLAMATION MARK
|
||||
'"' , // " 0x22 -> QUOTATION MARK
|
||||
'#' , // # 0x23 -> NUMBER SIGN
|
||||
'$' , // $ 0x24 -> DOLLAR SIGN
|
||||
'%' , // % 0x25 -> PERCENT SIGN
|
||||
'&' , // & 0x26 -> AMPERSAND
|
||||
'\'' , // ' 0x27 -> APOSTROPHE
|
||||
'(' , // ( 0x28 -> LEFT PARENTHESIS
|
||||
')' , // ) 0x29 -> RIGHT PARENTHESIS
|
||||
'*' , // * 0x2A -> ASTERISK
|
||||
'+' , // + 0x2B -> PLUS SIGN
|
||||
',' , // , 0x2C -> COMMA
|
||||
'-' , // - 0x2D -> HYPHEN-MINUS
|
||||
'.' , // . 0x2E -> FULL STOP
|
||||
'/' , // / 0x2F -> SOLIDUS
|
||||
'0' , // 0 0x30 -> DIGIT ZERO
|
||||
'1' , // 1 0x31 -> DIGIT ONE
|
||||
'2' , // 2 0x32 -> DIGIT TWO
|
||||
'3' , // 3 0x33 -> DIGIT THREE
|
||||
'4' , // 4 0x34 -> DIGIT FOUR
|
||||
'5' , // 5 0x35 -> DIGIT FIVE
|
||||
'6' , // 6 0x36 -> DIGIT SIX
|
||||
'7' , // 7 0x37 -> DIGIT SEVEN
|
||||
'8' , // 8 0x38 -> DIGIT EIGHT
|
||||
'9' , // 9 0x39 -> DIGIT NINE
|
||||
':' , // : 0x3A -> COLON
|
||||
';' , // ; 0x3B -> SEMICOLON
|
||||
'<' , // < 0x3C -> LESS-THAN SIGN
|
||||
'=' , // = 0x3D -> EQUALS SIGN
|
||||
'>' , // > 0x3E -> GREATER-THAN SIGN
|
||||
'?' , // ? 0x3F -> QUESTION MARK
|
||||
'`' , // ` 0x40 -> GRAVE ACCENT
|
||||
'A' , // A 0x41 -> LATIN CAPITAL LETTER A
|
||||
'B' , // B 0x42 -> LATIN CAPITAL LETTER B
|
||||
'C' , // C 0x43 -> LATIN CAPITAL LETTER C
|
||||
'D' , // D 0x44 -> LATIN CAPITAL LETTER D
|
||||
'E' , // E 0x45 -> LATIN CAPITAL LETTER E
|
||||
'F' , // F 0x46 -> LATIN CAPITAL LETTER F
|
||||
'G' , // G 0x47 -> LATIN CAPITAL LETTER G
|
||||
'H' , // H 0x48 -> LATIN CAPITAL LETTER H
|
||||
'I' , // I 0x49 -> LATIN CAPITAL LETTER I
|
||||
'J' , // J 0x4A -> LATIN CAPITAL LETTER J
|
||||
'K' , // K 0x4B -> LATIN CAPITAL LETTER K
|
||||
'L' , // L 0x4C -> LATIN CAPITAL LETTER L
|
||||
'M' , // M 0x4D -> LATIN CAPITAL LETTER M
|
||||
'N' , // N 0x4E -> LATIN CAPITAL LETTER N
|
||||
'O' , // O 0x4F -> LATIN CAPITAL LETTER O
|
||||
'P' , // P 0x50 -> LATIN CAPITAL LETTER P
|
||||
'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
|
||||
'R' , // R 0x52 -> LATIN CAPITAL LETTER R
|
||||
'S' , // S 0x53 -> LATIN CAPITAL LETTER S
|
||||
'T' , // T 0x54 -> LATIN CAPITAL LETTER T
|
||||
'U' , // U 0x55 -> LATIN CAPITAL LETTER U
|
||||
'V' , // V 0x56 -> LATIN CAPITAL LETTER V
|
||||
'W' , // W 0x57 -> LATIN CAPITAL LETTER W
|
||||
'X' , // X 0x58 -> LATIN CAPITAL LETTER X
|
||||
'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
|
||||
'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
|
||||
'{' , // { 0x5B -> LEFT BRACE
|
||||
'|' , // | 0x5C -> VERTICAL BAR
|
||||
'}' , // } 0x5D -> RIGHT BRACE
|
||||
'~' , // ~ 0x5E -> TILDE
|
||||
'\ufffe', // 0x5F -> RESERVED
|
||||
'\u00a0', // 0x60 -> NO-BREAK SPACE (TRANSPARENT)
|
||||
'\ufffe', // 0x61 -> COMMODORE SYMBOL
|
||||
'\u2191', // ↑ 0x62 -> UP ARROW
|
||||
'\u2193', // ↓ 0x63 -> DOWN ARROW
|
||||
'\u2190', // ← 0x64 -> LEFT ARROW
|
||||
'\u2192', // → 0x65 -> RIGHT ARROW
|
||||
'\u231A', // ⌚ 0x66 -> WATCH (ANALOG CLOCKFACE)
|
||||
'\u21BB', // ↻ 0x67 -> CYCLE ARROWS
|
||||
'\u2026', // … 0x68 -> ELLIPSIS
|
||||
'\u25a7', // ▧ 0x69 -> DIAGNONAL STRIPES
|
||||
'\u2610', // ☐ 0x6A -> CHECKBOX UNCHECKED
|
||||
'\u2611', // ☑ 0x6B -> CHECKBOX CHECKED
|
||||
'\ufffe', // 0x6C -> RADIO BUTTON UNSELECTED
|
||||
'\ufffe', // 0x6D -> RADIO BUTTON SELECTED
|
||||
'\ufffe', // 0x6E -> UTILITY CLOSE BUTTON
|
||||
'\ufffe', // 0x6F -> UTILITY TITLE BAR
|
||||
'\u00a9', // © 0x70 -> COPYRIGHT
|
||||
'\u2713', // ✓ 0x71 -> CHECKMARK
|
||||
'\u2261', // ≡ 0x72 -> THREE HORIZONTAL STRIPES
|
||||
'\ufffe', // 0x73 -> TICK TRACK
|
||||
'\ufffe', // 0x74 -> TICK TRACK NUB
|
||||
'\ufffe', // 0x75 -> TAB CORNER
|
||||
'\u2980', // ⦀ 0x76 -> THREE VERTICAL STRIPES
|
||||
'\ufffe', // 0x77 -> CUSTOM 1
|
||||
'\ufffe', // 0x78 -> CUSTOM 2
|
||||
'\ufffe', // 0x79 -> CUSTOM 3
|
||||
'\ufffe', // 0x7A -> CUSTOM 4
|
||||
'\ufffe', // 0x7B -> CUSTOM 5
|
||||
'\ufffe', // 0x7C -> CUSTOM 6
|
||||
'\ufffe', // 0x7D -> CUSTOM 7
|
||||
'\ufffe', // 0x7E -> CUSTOM 8
|
||||
'\ufffe', // 0x7F -> CUSTOM 9
|
||||
'\ufffe', // 0x80 -> REVERSED COMMERCIAL AT
|
||||
'\ufffe', // 0x81 -> REVERSED LATIN SMALL LETTER A
|
||||
'\ufffe', // 0x82 -> REVERSED LATIN SMALL LETTER B
|
||||
'\ufffe', // 0x83 -> REVERSED LATIN SMALL LETTER C
|
||||
'\ufffe', // 0x84 -> REVERSED LATIN SMALL LETTER D
|
||||
'\ufffe', // 0x85 -> REVERSED LATIN SMALL LETTER E
|
||||
'\ufffe', // 0x86 -> REVERSED LATIN SMALL LETTER F
|
||||
'\ufffe', // 0x87 -> REVERSED LATIN SMALL LETTER G
|
||||
'\ufffe', // 0x88 -> REVERSED LATIN SMALL LETTER H
|
||||
'\ufffe', // 0x89 -> REVERSED LATIN SMALL LETTER I
|
||||
'\ufffe', // 0x8A -> REVERSED LATIN SMALL LETTER J
|
||||
'\ufffe', // 0x8B -> REVERSED LATIN SMALL LETTER K
|
||||
'\ufffe', // 0x8C -> REVERSED LATIN SMALL LETTER L
|
||||
'\ufffe', // 0x8D -> REVERSED LATIN SMALL LETTER M
|
||||
'\ufffe', // 0x8E -> REVERSED LATIN SMALL LETTER N
|
||||
'\ufffe', // 0x8F -> REVERSED LATIN SMALL LETTER O
|
||||
'\ufffe', // 0x90 -> REVERSED LATIN SMALL LETTER P
|
||||
'\ufffe', // 0x91 -> REVERSED LATIN SMALL LETTER Q
|
||||
'\ufffe', // 0x92 -> REVERSED LATIN SMALL LETTER R
|
||||
'\ufffe', // 0x93 -> REVERSED LATIN SMALL LETTER S
|
||||
'\ufffe', // 0x94 -> REVERSED LATIN SMALL LETTER T
|
||||
'\ufffe', // 0x95 -> REVERSED LATIN SMALL LETTER U
|
||||
'\ufffe', // 0x96 -> REVERSED LATIN SMALL LETTER V
|
||||
'\ufffe', // 0x97 -> REVERSED LATIN SMALL LETTER W
|
||||
'\ufffe', // 0x98 -> REVERSED LATIN SMALL LETTER X
|
||||
'\ufffe', // 0x99 -> REVERSED LATIN SMALL LETTER Y
|
||||
'\ufffe', // 0x9A -> REVERSED LATIN SMALL LETTER Z
|
||||
'\ufffe', // 0x9B -> REVERSED LEFT SQUARE BRACKET
|
||||
'\ufffe', // 0x9C -> REVERSED REVERSE SOLIDUS
|
||||
'\ufffe', // 0x9D -> REVERSED RIGHT SQUARE BRACKET
|
||||
'\ufffe', // 0x9E -> REVERSED CIRCUMFLEX
|
||||
'\ufffe', // 0x9F -> REVERSED UNDERSCORE
|
||||
'\ufffe', // 0xA0 -> REVERSED SPACE
|
||||
'\ufffe', // 0xA1 -> REVERSED EXCLAMATION MARK
|
||||
'\ufffe', // 0xA2 -> REVERSED QUOTATION MARK
|
||||
'\ufffe', // 0xA3 -> REVERSED NUMBER SIGN
|
||||
'\ufffe', // 0xA4 -> REVERSED DOLLAR SIGN
|
||||
'\ufffe', // 0xA5 -> REVERSED PERCENT SIGN
|
||||
'\ufffe', // 0xA6 -> REVERSED AMPERSAND
|
||||
'\ufffe', // 0xA7 -> REVERSED APOSTROPHE
|
||||
'\ufffe', // 0xA8 -> REVERSED LEFT PARENTHESIS
|
||||
'\ufffe', // 0xA9 -> REVERSED RIGHT PARENTHESIS
|
||||
'\ufffe', // 0xAA -> REVERSED ASTERISK
|
||||
'\ufffe', // 0xAB -> REVERSED PLUS SIGN
|
||||
'\ufffe', // 0xAC -> REVERSED COMMA
|
||||
'\ufffe', // 0xAD -> REVERSED HYPHEN-MINUS
|
||||
'\ufffe', // 0xAE -> REVERSED FULL STOP
|
||||
'\ufffe', // 0xAF -> REVERSED SOLIDUS
|
||||
'\ufffe', // 0xB0 -> REVERSED DIGIT ZERO
|
||||
'\ufffe', // 0xB1 -> REVERSED DIGIT ONE
|
||||
'\ufffe', // 0xB2 -> REVERSED DIGIT TWO
|
||||
'\ufffe', // 0xB3 -> REVERSED DIGIT THREE
|
||||
'\ufffe', // 0xB4 -> REVERSED DIGIT FOUR
|
||||
'\ufffe', // 0xB5 -> REVERSED DIGIT FIVE
|
||||
'\ufffe', // 0xB6 -> REVERSED DIGIT SIX
|
||||
'\ufffe', // 0xB7 -> REVERSED DIGIT SEVEN
|
||||
'\ufffe', // 0xB8 -> REVERSED DIGIT EIGHT
|
||||
'\ufffe', // 0xB9 -> REVERSED DIGIT NINE
|
||||
'\ufffe', // 0xBA -> REVERSED COLON
|
||||
'\ufffe', // 0xBB -> REVERSED SEMICOLON
|
||||
'\ufffe', // 0xBC -> REVERSED LESS-THAN SIGN
|
||||
'\ufffe', // 0xBD -> REVERSED EQUALS SIGN
|
||||
'\ufffe', // 0xBE -> REVERSED GREATER-THAN SIGN
|
||||
'\ufffe', // 0xBF -> REVERSED QUESTION MARK
|
||||
'\ufffe', // 0xC0 -> REVERSED GRAVE ACCENT
|
||||
'\ufffe', // 0xC1 -> REVERSED LATIN CAPITAL LETTER A
|
||||
'\ufffe', // 0xC2 -> REVERSED LATIN CAPITAL LETTER B
|
||||
'\ufffe', // 0xC3 -> REVERSED LATIN CAPITAL LETTER C
|
||||
'\ufffe', // 0xC4 -> REVERSED LATIN CAPITAL LETTER D
|
||||
'\ufffe', // 0xC5 -> REVERSED LATIN CAPITAL LETTER E
|
||||
'\ufffe', // 0xC6 -> REVERSED LATIN CAPITAL LETTER F
|
||||
'\ufffe', // 0xC7 -> REVERSED LATIN CAPITAL LETTER G
|
||||
'\ufffe', // 0xC8 -> REVERSED LATIN CAPITAL LETTER H
|
||||
'\ufffe', // 0xC9 -> REVERSED LATIN CAPITAL LETTER I
|
||||
'\ufffe', // 0xCA -> REVERSED LATIN CAPITAL LETTER J
|
||||
'\ufffe', // 0xCB -> REVERSED LATIN CAPITAL LETTER K
|
||||
'\ufffe', // 0xCC -> REVERSED LATIN CAPITAL LETTER L
|
||||
'\ufffe', // 0xCD -> REVERSED LATIN CAPITAL LETTER M
|
||||
'\ufffe', // 0xCE -> REVERSED LATIN CAPITAL LETTER N
|
||||
'\ufffe', // 0xCF -> REVERSED LATIN CAPITAL LETTER O
|
||||
'\ufffe', // 0xD0 -> REVERSED LATIN CAPITAL LETTER P
|
||||
'\ufffe', // 0xD1 -> REVERSED LATIN CAPITAL LETTER Q
|
||||
'\ufffe', // 0xD2 -> REVERSED LATIN CAPITAL LETTER R
|
||||
'\ufffe', // 0xD3 -> REVERSED LATIN CAPITAL LETTER S
|
||||
'\ufffe', // 0xD4 -> REVERSED LATIN CAPITAL LETTER T
|
||||
'\ufffe', // 0xD5 -> REVERSED LATIN CAPITAL LETTER U
|
||||
'\ufffe', // 0xD6 -> REVERSED LATIN CAPITAL LETTER V
|
||||
'\ufffe', // 0xD7 -> REVERSED LATIN CAPITAL LETTER W
|
||||
'\ufffe', // 0xD8 -> REVERSED LATIN CAPITAL LETTER X
|
||||
'\ufffe', // 0xD9 -> REVERSED LATIN CAPITAL LETTER Y
|
||||
'\ufffe', // 0xDA -> REVERSED LATIN CAPITAL LETTER Z
|
||||
'\ufffe', // 0xDB -> REVERSED LEFT BRACE
|
||||
'\ufffe', // 0xDC -> REVERSED VERTICAL BAR
|
||||
'\ufffe', // 0xDD -> REVERSED RIGHT BRACE
|
||||
'\ufffe', // 0xDE -> REVERSED TILDE
|
||||
'\ufffe', // 0xDF -> RESERVED
|
||||
'\ufffe', // 0xE0 -> RESERVED
|
||||
'\ufffe', // 0xE1 -> REVERSED COMMODORE SYMBOL
|
||||
'\ufffe', // 0xE2 -> REVERSED UP ARROW
|
||||
'\ufffe', // 0xE3 -> REVERSED DOWN ARROW
|
||||
'\ufffe', // 0xE4 -> REVERSED LEFT ARROW
|
||||
'\ufffe', // 0xE5 -> REVERSED RIGHT ARROW
|
||||
'\ufffe', // 0xE6 -> REVERSED ANALOG CLOCKFACE
|
||||
'\ufffe', // 0xE7 -> REVERSED CYCLE ARROWS
|
||||
'\ufffe', // 0xE8 -> REVERSED ELLIPSIS
|
||||
'\ufffe', // 0xE9 -> REVERSED DIAGONAL STRIPES
|
||||
'\ufffe', // 0xEA -> REVERSED CHECKBOX UNCHECKED
|
||||
'\ufffe', // 0xEB -> REVERSED CHECKBOX CHECKED
|
||||
'\ufffe', // 0xEC -> REVERSED RADIO BUTTON UNSELECTED
|
||||
'\ufffe', // 0xED -> REVERSED RADIO BUTTON SELECTED
|
||||
'\ufffe', // 0xEE -> MEMORY CHIP ICON
|
||||
'\u21e7', // ⇧ 0xEF -> SHIFT SYMBOL
|
||||
'\ufffe', // 0xF0 -> REVERSED COPYRIGHT SYMBOL
|
||||
'\ufffe', // 0xF1 -> REVERSED CHECKMARK
|
||||
'\ufffe', // 0xF2 -> REVERSED THREE HORIZONTAL STRIPES
|
||||
'\ufffe', // 0xF3 -> REVERSED TICK TRACK
|
||||
'\ufffe', // 0xF4 -> REVERSED TICK TRACK NUB
|
||||
'\ufffe', // 0xF5 -> REVERSED TAB CORNER
|
||||
'\ufffe', // 0xF6 -> REVERSED THREE VERTICAL STRIPES
|
||||
'\ufffe', // 0xF7 -> CUSTOM 10
|
||||
'\ufffe', // 0xF8 -> CUSTOM 11
|
||||
'\ufffe', // 0xF9 -> CUSTOM 12
|
||||
'\ufffe', // 0xFA -> CUSTOM 13
|
||||
'\ufffe', // 0xFB -> CUSTOM 14
|
||||
'\ufffe', // 0xFC -> CUSTOM 15
|
||||
'\ufffe', // 0xFD -> CUSTOM 16
|
||||
'\ufffe', // 0xFE -> CUSTOM 17
|
||||
'\ufffe' // 0xFF -> CUSTOM 18
|
||||
)
|
||||
|
||||
// encoding: from unicode to C64 OS Screencodes (0-255)
|
||||
private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index}
|
||||
|
||||
private fun replaceSpecial(chr: Char): Char =
|
||||
when(chr) {
|
||||
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
||||
else -> chr
|
||||
}
|
||||
|
||||
fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
||||
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
||||
val chr = replaceSpecial(chr3)
|
||||
val screencode = encodingC64os[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 13u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
}
|
||||
else -> {
|
||||
if(chr.isISOControl())
|
||||
throw CharConversionException("no c64os character for char #${chr.code}")
|
||||
else
|
||||
throw CharConversionException("no c64os character for char #${chr.code} '${chr}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return try {
|
||||
Ok(text.map {
|
||||
try {
|
||||
encodeChar(it, lowercase)
|
||||
} catch (x: CharConversionException) {
|
||||
encodeChar(it, !lowercase)
|
||||
}
|
||||
})
|
||||
} catch(cx: CharConversionException) {
|
||||
Err(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(screencode.map {
|
||||
val code = it.toInt()
|
||||
if(code<0 || code>= decodingC64os.size)
|
||||
throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}")
|
||||
decodingC64os[code]
|
||||
}.joinToString(""))
|
||||
} catch(ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-15
@@ -1,25 +1,24 @@
|
||||
package prog8.code.target
|
||||
package prog8.code.target.encodings
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.target.encodings.*
|
||||
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
val coded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||
Encoding.ISO -> IsoEncoding.encode(str)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
||||
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return coded.fold(
|
||||
@@ -31,12 +30,13 @@ object Encoder: IStringEncoding {
|
||||
val decoded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
return decoded.fold(
|
||||
@@ -44,4 +44,4 @@ object Encoder: IStringEncoding {
|
||||
success = { it }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
open class IsoEncodingBase(charsetName: String) {
|
||||
val charset: Charset = Charset.forName(charsetName)
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
@@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
||||
@@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
||||
object KatakanaEncoding {
|
||||
val charset: Charset = Charset.forName("JIS_X0201")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||
@@ -112,9 +113,14 @@ object KatakanaEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x07 -> UNDEFINED
|
||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
@@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 141u
|
||||
'\r' -> 141u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package prog8.code.target.neo6502
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class Neo6502MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU65c02
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0800u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xfc00u // kernal starts here
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The neo target only supports the main emulator (neo).")
|
||||
return
|
||||
}
|
||||
|
||||
val cmdline = listOf("neo", "${programNameWithPath}.bin@800", "cold")
|
||||
|
||||
println("\nStarting Neo6502 emulator...")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address in 0xff00u..0xff0fu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = Neo6502Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package prog8.code.target.neo6502
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
class Neo6502Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0xfau // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0xfbu // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfcu // temp storage 1 for a word $fc+$fd
|
||||
override val SCRATCH_W2 = 0xfeu // temp storage 2 for a word $fe+$ff
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("Neo6502 target doesn't support floating point routines")
|
||||
}
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
else -> {
|
||||
free.addAll(0x22u..0xffu)
|
||||
}
|
||||
}
|
||||
|
||||
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 zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package prog8.code.target.pet
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u
|
||||
override val BSSHIGHRAM_END = 0u
|
||||
override val BSSGOLDENRAM_START = 0u
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val m5 = Mflpt5.fromNumber(num)
|
||||
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==5) { "need 5 bytes" }
|
||||
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting PET emulator...")
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = PETZeropage(compilerOptions)
|
||||
// there's no golden ram.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package prog8.code.target.virtual
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VirtualMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MEM_SIZE = 8 // 64-bits double
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
override lateinit var golden: GoldenRam // not actually used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number): String {
|
||||
// little endian binary representation
|
||||
val bits = num.toDouble().toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||
return parts.joinToString(", ")
|
||||
}
|
||||
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> {
|
||||
val bits = num.toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
|
||||
return parts
|
||||
}
|
||||
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double {
|
||||
require(bytes.size==8) { "need 8 bytes" }
|
||||
val b0 = bytes[0].toLong() shl (8*7)
|
||||
val b1 = bytes[1].toLong() shl (8*6)
|
||||
val b2 = bytes[2].toLong() shl (8*5)
|
||||
val b3 = bytes[3].toLong() shl (8*4)
|
||||
val b4 = bytes[4].toLong() shl (8*3)
|
||||
val b5 = bytes[5].toLong() shl (8*2)
|
||||
val b6 = bytes[6].toLong() shl (8*1)
|
||||
val b7 = bytes[7].toLong() shl (8*0)
|
||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
println("\nStarting Virtual Machine...")
|
||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText())
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText())
|
||||
else
|
||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
}
|
||||
}
|
||||
|
||||
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 */ }
|
||||
}
|
||||
+3
-12
@@ -1,4 +1,4 @@
|
||||
package prog8.code.target.c128
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
@@ -11,17 +11,12 @@ import prog8.code.core.ZeropageType
|
||||
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_REG = 0x75u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
override val SCRATCH_PTR = 0x0bu // temp storage for a pointer $0b+$0c
|
||||
|
||||
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,
|
||||
@@ -71,8 +66,4 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
removeReservedFromFreePool()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if C128 can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
package prog8.code.target.c64
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
@@ -6,9 +6,10 @@ import prog8.code.core.*
|
||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x03u // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
override val SCRATCH_PTR = 0x9bu // temp storage for a pointer $9b+$9c
|
||||
|
||||
|
||||
init {
|
||||
@@ -21,7 +22,6 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x02u..0xffu)
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
@@ -79,18 +79,18 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
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())
|
||||
}
|
||||
+10
-9
@@ -1,4 +1,4 @@
|
||||
package prog8.code.target.cx16
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
@@ -6,9 +6,10 @@ import prog8.code.core.*
|
||||
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||
override val SCRATCH_PTR = 0x22u // temp storage for a pointer $22+$23
|
||||
|
||||
|
||||
init {
|
||||
@@ -52,17 +53,17 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class ConfigurableZeropage(
|
||||
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
||||
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1: UInt, // temp storage 1 for a word
|
||||
override val SCRATCH_W2: UInt, // temp storage 2 for a word
|
||||
override val SCRATCH_PTR: UInt, // temp storage for a pointer
|
||||
|
||||
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
||||
basicsafe: List<UIntRange>,
|
||||
kernalsafe: List<UIntRange>,
|
||||
fullsafe: List<UIntRange>,
|
||||
options: CompilationOptions
|
||||
) : Zeropage(options) {
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
TODO("floats in configurable target zp")
|
||||
}
|
||||
|
||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||
throw IllegalArgumentException("Zero page scratch variable REG should be B1+1")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.DONTUSE -> { /* don't use any zeropage at all */ }
|
||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
allocateCx16VirtualRegisters()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||
if(address<=0xffu) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-11
@@ -1,4 +1,4 @@
|
||||
package prog8.code.target.pet
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
@@ -11,15 +11,12 @@ import prog8.code.core.ZeropageType
|
||||
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_REG = 0xb4u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
override val SCRATCH_PTR = 0xb1u // temp storage for a pointer $b1+$b2
|
||||
|
||||
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,
|
||||
@@ -38,7 +35,7 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0xb3u..0xbau) // TODO more?
|
||||
free.addAll(0xb1u..0xbau) // TODO more?
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
@@ -52,8 +49,4 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
removeReservedFromFreePool()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if PET can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -43,4 +40,4 @@ tasks.test {
|
||||
testLogging {
|
||||
events("skipped", "failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,16 +1,16 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.GENERATED_LABEL_PREFIX
|
||||
import prog8.code.StConstant
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtLabel
|
||||
import prog8.code.core.IMachineDefinition
|
||||
import prog8.code.core.ICompilationTarget
|
||||
|
||||
|
||||
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||
|
||||
|
||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
|
||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
|
||||
|
||||
var numberOfOptimizations = 0
|
||||
|
||||
@@ -73,6 +73,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
@@ -123,7 +130,7 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
|
||||
private fun optimizeSameAssignments(
|
||||
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
machine: ICompilationTarget,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
|
||||
@@ -362,7 +369,7 @@ or *_afterif labels.
|
||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||
*/
|
||||
|
||||
val autoLabelPrefix = PtLabel.GENERATED_LABEL_PREFIX
|
||||
val autoLabelPrefix = GENERATED_LABEL_PREFIX
|
||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||
@@ -386,7 +393,7 @@ This gets generated after certain if conditions, and only the branch instruction
|
||||
|
||||
private fun optimizeStoreLoadSame(
|
||||
linesByFour: Sequence<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
machine: ICompilationTarget,
|
||||
symbolTable: SymbolTable
|
||||
): List<Modification> {
|
||||
val mods = mutableListOf<Modification>()
|
||||
@@ -408,7 +415,7 @@ private fun optimizeStoreLoadSame(
|
||||
// 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)
|
||||
val loadinstruction = second.take(3)
|
||||
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||
}
|
||||
else {
|
||||
@@ -446,20 +453,35 @@ private fun optimizeStoreLoadSame(
|
||||
|
||||
|
||||
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
|
||||
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
|
||||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
|
||||
(first.startsWith("ldy ") && second.startsWith("sty "))
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ")
|
||||
) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
|
||||
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated IF NOT IO ADDRESS
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") && third.startsWith("lda ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") && third.startsWith("ldx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ") && third.startsWith("ldy ")
|
||||
) {
|
||||
val firstVal = first.substring(4).trimStart()
|
||||
val thirdVal = third.substring(4).trimStart()
|
||||
if (firstVal == thirdVal) {
|
||||
val address = getAddressArg(third, symbolTable)
|
||||
if (address != null && !machine.isIOAddress(address)) {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
|
||||
|
||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||
// try to get the constant value address, could return null if it's a symbol instead
|
||||
@@ -508,9 +530,11 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
||||
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// jmp Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
|
||||
// and some other optimizations.
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
@@ -520,7 +544,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
val third = lines[2].value
|
||||
|
||||
if(!haslabel(second)) {
|
||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
|
||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
@@ -563,6 +590,15 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only remove bra followed by jmp or jmp followed by bra
|
||||
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
|
||||
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -680,25 +716,36 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
||||
optimize('x', lines)
|
||||
optimize('y', lines)
|
||||
|
||||
val first = lines[1].value.trimStart()
|
||||
val second = lines[2].value.trimStart()
|
||||
val third = lines[3].value.trimStart()
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
|
||||
// phy + ldy + pla -> tya + ldy
|
||||
// phx + ldx + pla -> txa + ldx
|
||||
// pha + lda + pla -> nop
|
||||
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " tya"))
|
||||
}
|
||||
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " txa"))
|
||||
}
|
||||
else if(first=="pha" && second.startsWith("lda ") && third=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
// pha + tya + tay + pla -> nop
|
||||
// pha + txa + tax + pla -> nop
|
||||
when (first) {
|
||||
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[0].index, false, " tya"))
|
||||
}
|
||||
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[0].index, false, " txa"))
|
||||
}
|
||||
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
"pha" if ((second=="tya" && third=="tay") || (second=="txa" && third=="tax")) && fourth=="pla" -> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,7 +753,6 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
||||
return mods
|
||||
}
|
||||
|
||||
|
||||
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
|
||||
val mods = mutableListOf<Modification>()
|
||||
@@ -751,3 +797,120 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
/*
|
||||
; FIRST SEQUYENCE: P8ZP_SCRATCH_PTR += AY :
|
||||
clc
|
||||
adc P8ZP_SCRATCH_PTR
|
||||
pha
|
||||
tya
|
||||
adc P8ZP_SCRATCH_PTR+1
|
||||
tay
|
||||
pla
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
|
||||
->
|
||||
|
||||
clc
|
||||
adc P8ZP_SCRATCH_PTR
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
tya
|
||||
adc P8ZP_SCRATCH_PTR+1
|
||||
sta P8ZP_SCRATCH_PTR+1
|
||||
|
||||
|
||||
also SECOND SEQUENCE:
|
||||
|
||||
ldx VALUE/ ldy VALUE
|
||||
sta SOMEWHERE_WITHOUT_,x_OR_,y
|
||||
txa / tya
|
||||
ldy #1
|
||||
sta SOMEWHERE
|
||||
-->
|
||||
sta SOMEWHERE_WITHOUT_,x_OR_,y
|
||||
lda VALUE
|
||||
ldy #1
|
||||
sta SOMEWHERE
|
||||
|
||||
|
||||
also THIRD SEQUENCE:
|
||||
|
||||
ldx VALUE
|
||||
ldy #0
|
||||
sta SOMEWHERE_WITHOUT_,x
|
||||
txa
|
||||
iny
|
||||
sta SOMEWHERE
|
||||
-->
|
||||
ldy #0
|
||||
sta SOMEWHERE_WITHOUT_,x
|
||||
lda VALUE
|
||||
iny
|
||||
sta SOMEWHERE
|
||||
*/
|
||||
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 eight = lines[7].value.trimStart()
|
||||
val ninth = lines[8].value.trimStart()
|
||||
|
||||
// FIRST SEQUENCE
|
||||
if(first=="clc" && second.startsWith("adc") && third=="pha" && fourth=="tya" &&
|
||||
fifth.startsWith("adc") && sixth=="tay" && seventh=="pla" && eight.startsWith("sta") && ninth.startsWith("sty")) {
|
||||
val var2 = second.substring(4)
|
||||
val var5 = fifth.substring(4).substringBefore('+')
|
||||
val var8 = eight.substring(4)
|
||||
val var9 = ninth.substring(4).substringBefore('+')
|
||||
if(var2==var5 && var2==var8 && var2==var9) {
|
||||
if(fifth.endsWith("$var5+1") && ninth.endsWith("$var9+1")) {
|
||||
mods.add(Modification(lines[2].index, false, " sta $var2"))
|
||||
mods.add(Modification(lines[5].index, false, " sta $var2+1"))
|
||||
mods.add(Modification(lines[6].index, true, null))
|
||||
mods.add(Modification(lines[7].index, true, null))
|
||||
mods.add(Modification(lines[8].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SECOND SEQUENCE
|
||||
if(first.startsWith("ldx ") && second.startsWith("sta ") &&
|
||||
third=="txa" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
|
||||
) {
|
||||
if(",x" !in second) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
if(first.startsWith("ldy ") && second.startsWith("sta ") &&
|
||||
third=="tya" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
|
||||
) {
|
||||
if(",y" !in second) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[2].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
|
||||
// THIRD SEQUENCE
|
||||
if(first.startsWith("ldx ") && second.startsWith("ldy ") && third.startsWith("sta ") &&
|
||||
fourth=="txa" && fifth=="iny" && sixth.startsWith("sta ")
|
||||
) {
|
||||
if(",x" !in third) {
|
||||
val value = first.substring(4)
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[3].index, false, " lda $value"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return mods
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtLabel
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.AtariTarget
|
||||
import prog8.code.GENERATED_LABEL_PREFIX
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.OutputType
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Neo6502Target
|
||||
import prog8.code.target.PETTarget
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
@@ -19,21 +23,25 @@ internal class AssemblyProgram(
|
||||
private val binFile = outputDir.resolve("$name.bin")
|
||||
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
|
||||
private val listFile = outputDir.resolve("$name.list")
|
||||
private val targetWithoutBreakpointsForEmulator = arrayOf(AtariTarget.NAME, Neo6502Target.NAME)
|
||||
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
|
||||
val assemblerCommand: List<String>
|
||||
|
||||
when (compTarget.name) {
|
||||
in arrayOf("c64", "c128", "cx16", "pet32") -> {
|
||||
fun addRemainingOptions(command: MutableList<String>, program: Path, assembly: Path): List<String> {
|
||||
if(options.compTarget.additionalAssemblerOptions.isNotEmpty())
|
||||
command.addAll(options.compTarget.additionalAssemblerOptions)
|
||||
|
||||
command.addAll(listOf("--output", program.toString(), assembly.toString()))
|
||||
return command
|
||||
}
|
||||
|
||||
when(options.output) {
|
||||
OutputType.PRG -> {
|
||||
// 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"
|
||||
)
|
||||
val command = mutableListOf("64tass", "--cbm-prg", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
@@ -44,34 +52,59 @@ internal class AssemblyProgram(
|
||||
command.add("--quiet")
|
||||
|
||||
if(options.asmListfile) {
|
||||
command.addAll(listOf("--list=$listFile", "--no-monitor"))
|
||||
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
|
||||
|
||||
assemblerCommand = addRemainingOptions(command, prgFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
}
|
||||
"atari" -> {
|
||||
OutputType.XEX -> {
|
||||
// Atari800XL .xex generation.
|
||||
|
||||
// TODO are these options okay for atari?
|
||||
val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
assemblerCommand = addRemainingOptions(command,xexFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.RAW -> {
|
||||
// Neo6502/headerless raw program generation.
|
||||
val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
if(options.asmListfile)
|
||||
command.add("--list=$listFile")
|
||||
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
if(!options.quiet)
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.LIBRARY -> {
|
||||
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
|
||||
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", // "-Werror", "-Wno-strict-bool"
|
||||
"--no-monitor"
|
||||
)
|
||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
@@ -84,63 +117,26 @@ internal class AssemblyProgram(
|
||||
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")
|
||||
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
||||
if(!options.quiet)
|
||||
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||
command.add("--cbm-prg")
|
||||
} else {
|
||||
if(!options.quiet)
|
||||
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||
}
|
||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
|
||||
assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
|
||||
}
|
||||
"neo" -> {
|
||||
// Neo6502 raw program generation.
|
||||
|
||||
if(options.output!=OutputType.RAW || options.loadAddress!=0x0800u || options.launcher!=CbmPrgLauncherType.NONE) {
|
||||
throw AssemblyError("invalid program compilation options. Neo6502 requires %output raw, %launcher none, %address $0800")
|
||||
}
|
||||
|
||||
// TODO are these options okay for neo?
|
||||
val command = mutableListOf("64tass", "--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.RAW -> {
|
||||
command.add("--nostart")
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
binFile
|
||||
}
|
||||
else -> throw AssemblyError("invalid output type, need 'raw'")
|
||||
}
|
||||
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 !in targetWithoutBreakpointsForEmulator) {
|
||||
val proc = ProcessBuilder(assemblerCommand)
|
||||
if(!options.quiet)
|
||||
proc.inheritIO()
|
||||
val process = proc.start()
|
||||
val result = process.waitFor()
|
||||
if (result == 0) {
|
||||
removeGeneratedLabelsFromMonlist()
|
||||
generateBreakpointList()
|
||||
}
|
||||
@@ -148,7 +144,7 @@ internal class AssemblyProgram(
|
||||
}
|
||||
|
||||
private fun removeGeneratedLabelsFromMonlist() {
|
||||
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""")
|
||||
val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
|
||||
val lines = viceMonListFile.toFile().readLines()
|
||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||
for (line in lines) {
|
||||
@@ -165,7 +161,7 @@ internal class AssemblyProgram(
|
||||
for (line in viceMonListFile.toFile().readLines()) {
|
||||
val match = pattern.matchEntire(line)
|
||||
if (match != null)
|
||||
breakpoints.add("break \$" + match.groupValues[1])
|
||||
breakpoints.add("break $" + match.groupValues[1])
|
||||
}
|
||||
val num = breakpoints.size
|
||||
breakpoints.add(0, "; breakpoint list now follows")
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StMemorySlabBlockName
|
||||
import prog8.code.StStructInstanceBlockName
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
@@ -46,6 +49,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peekbool" -> funcPeekBool(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokef" -> funcPokeF(fcall)
|
||||
@@ -53,21 +57,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val memread = PtMemoryByte(fcall.position)
|
||||
memread.add(fcall.args[0])
|
||||
memread.parent = fcall
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.position, memory=memread)
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
|
||||
asmgen.assignExpressionTo(fcall.args[1], memtarget)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||
"pokebool" -> funcPokeBool(fcall)
|
||||
"rsave" -> funcRsave()
|
||||
"rrestore" -> funcRrestore()
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||
"callfar2" -> funcCallFar2(fcall, resultRegister)
|
||||
"call" -> funcCall(fcall)
|
||||
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
|
||||
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
|
||||
@@ -109,8 +113,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[3].position, var3name)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
|
||||
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
|
||||
}
|
||||
@@ -122,7 +126,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
|
||||
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -141,7 +145,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcRsave() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
php
|
||||
pha
|
||||
@@ -161,7 +165,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcRrestore() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
plx
|
||||
ply
|
||||
@@ -198,7 +202,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.forDt(BaseDataType.UWORD)) // jump address
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
|
||||
asmgen.out("""
|
||||
; push a return address so the jmp becomes indirect jsr
|
||||
lda #>((+)-1)
|
||||
@@ -229,6 +233,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
.word ${constAddress.toHex()}
|
||||
.byte $constBank""")
|
||||
} else {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no code for non-const callfar (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||
// self-modifying code: set jsrfar arguments
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||
asmgen.out(" sta (++)+0")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||
@@ -285,6 +292,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
.word ${constAddress.toHex()}
|
||||
.byte $constBank""")
|
||||
} else {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no code for non-const callfar2 (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||
// self-modifying code: set jsrfar arguments
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||
asmgen.out(" sta (++)+0")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||
@@ -348,7 +358,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
cmp ${asmgen.asmVariableName(arg2)}
|
||||
+""")
|
||||
}
|
||||
is PtBool -> TODO("word compare against bool")
|
||||
is PtBool -> TODO("word compare against bool ${arg2.position}")
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -376,16 +386,33 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.forDt(BaseDataType.UWORD), fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.position)
|
||||
|
||||
val slabname = PtIdentifier("$StMemorySlabBlockName.memory_$name", DataType.UWORD, fcall.position)
|
||||
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UWORD), expression = addressOf)
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
|
||||
val prefix = if(fcall.args.isEmpty()) "${StStructInstanceBlockName}_bss" else StStructInstanceBlockName
|
||||
val labelname = PtIdentifier("$prefix.${SymbolTable.labelnameForStructInstance(fcall)}", fcall.type, fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
|
||||
addressOf.add(labelname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
when(fcall.args[0].type.base) {
|
||||
@@ -411,14 +438,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #\$80 |+ | sta ${varname},x")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #$80 |+ | sta ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||
@@ -426,7 +456,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -434,16 +464,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #\$80 | sta ${varname}_msb,x |+ ")
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
|
||||
else
|
||||
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #\$80 | sta ${varname}+1,x |+ ")
|
||||
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #$80 | sta ${varname}+1,x |+ ")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -461,33 +493,26 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" ror ${number.toHex()}")
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff ; modified""")
|
||||
when {
|
||||
what.address is PtNumber -> {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" ror ${number.toHex()}")
|
||||
}
|
||||
what.address is PtIdentifier -> {
|
||||
asmgen.out(" php") // save Carry
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||
asmgen.out(" plp | ror a")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
else -> {
|
||||
TODO("ror ptr-expression ${what.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,7 +529,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
|
||||
else
|
||||
@@ -527,14 +554,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.UBYTE -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | cmp #\$80 | rol a | sta ${varname},x")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
||||
asmgen.out(" lda ${number.toHex()} | cmp #$80 | rol a | sta ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||
@@ -542,7 +571,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||
asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -551,9 +580,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb |+")
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
|
||||
else
|
||||
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
||||
}
|
||||
@@ -577,33 +608,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" rol ${number.toHex()}")
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff ; modified""")
|
||||
when {
|
||||
what.address is PtNumber -> {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" rol ${number.toHex()}")
|
||||
}
|
||||
what.address is PtIdentifier -> {
|
||||
asmgen.out(" php") // save Carry
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||
asmgen.out(" plp | rol a")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
else -> {
|
||||
TODO("rol ptr-expression ${what.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,7 +643,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
if(!what.index.isSimple()) asmgen.out(" plp")
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.variable==null)
|
||||
TODO("support for ptr indexing ${what.position}")
|
||||
val varname = asmgen.asmVariableName(what.variable!!)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
|
||||
else
|
||||
@@ -642,40 +667,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when(fcall.args[0]) {
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
}
|
||||
is PtNumber -> {
|
||||
val num = (fcall.args[0] as PtNumber).number + if(msb) 1 else 0
|
||||
val mem = PtMemoryByte(fcall.position)
|
||||
mem.add(PtNumber(BaseDataType.UBYTE, num, fcall.position))
|
||||
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
val addrof = fcall.args[0] as PtAddressOf
|
||||
if(addrof.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
} else {
|
||||
val mem = PtMemoryByte(fcall.position)
|
||||
if(addrof.isFromArrayElement)
|
||||
TODO("address-of arrayelement")
|
||||
if(msb) {
|
||||
val address = PtBinaryExpression("+", DataType.forDt(BaseDataType.UWORD), addrof.position)
|
||||
address.add(addrof)
|
||||
address.add(PtNumber(address.type.base, 1.0, addrof.position))
|
||||
mem.add(address)
|
||||
} else {
|
||||
mem.add(addrof)
|
||||
}
|
||||
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
|
||||
}
|
||||
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
val arrayVariable = indexer.variable
|
||||
val arrayVariable = indexer.variable ?: TODO("support for ptr indexing ${indexer.position}")
|
||||
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
|
||||
indexer.children[0].parent = indexer
|
||||
elementSize = 1
|
||||
@@ -710,7 +709,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
multipliedIndex.parent=indexer
|
||||
}
|
||||
}
|
||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, array = indexer)
|
||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
|
||||
}
|
||||
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
|
||||
}
|
||||
@@ -718,7 +717,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(fcall.args[1].asConstInteger() == 0) {
|
||||
assignAsmGen.assignConstantByte(target, 0)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
}
|
||||
@@ -781,8 +780,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignConstFloatToPointerAY(number)
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT))
|
||||
val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
pha
|
||||
@@ -796,6 +795,52 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPokeBool(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
val addr = addrExpr.number.toHex()
|
||||
asmgen.out(" sta $addr")
|
||||
return
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
asmgen.storeIndirectByteReg(CpuRegister.A, varname, 0u, false, false)
|
||||
return
|
||||
}
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out(" sta ($varname),y")
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> { /* fall through */ }
|
||||
}
|
||||
|
||||
// fall through method:
|
||||
if(fcall.args[1].isSimple()) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
} else {
|
||||
asmgen.pushCpuStack(BaseDataType.UBYTE, fcall.args[1])
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
asmgen.storeIndirectByteReg(CpuRegister.A, "P8ZP_SCRATCH_W1", 0u, false, false)
|
||||
}
|
||||
|
||||
private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
@@ -809,20 +854,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
asmgen.out("""
|
||||
sta ($varname)
|
||||
txa
|
||||
ldy #1
|
||||
sta ($varname),y""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sta ($varname),y
|
||||
txa
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
}
|
||||
asmgen.storeIndirectWordReg(RegisterOrPair.AX, varname, 0u)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -857,7 +889,46 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignFAC1float(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), register=resultRegister, position=fcall.position))
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPeekBool(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
fun fallback() {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.func_peek")
|
||||
}
|
||||
when(val addrExpr = fcall.args[0]) {
|
||||
is PtNumber -> {
|
||||
val addr = addrExpr.number.toHex()
|
||||
asmgen.out(" lda $addr")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr))
|
||||
asmgen.loadIndirectByte(varname, 0u)
|
||||
else
|
||||
fallback()
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
val pointer = result?.first as? PtIdentifier
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.out(" lda ($varname),y")
|
||||
} else fallback()
|
||||
}
|
||||
else -> fallback()
|
||||
}
|
||||
|
||||
when(resultRegister ?: RegisterOrPair.A) {
|
||||
RegisterOrPair.A -> {}
|
||||
RegisterOrPair.X -> asmgen.out(" tax")
|
||||
RegisterOrPair.Y -> asmgen.out(" tay")
|
||||
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().lowercase()}")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,25 +944,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda ($varname),y
|
||||
tay
|
||||
lda ($varname)""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($varname),y
|
||||
tax
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
txa""")
|
||||
}
|
||||
} else fallback()
|
||||
if(asmgen.isZpVar(addrExpr))
|
||||
asmgen.loadIndirectWord(varname, 0u)
|
||||
else
|
||||
fallback()
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||
@@ -1061,38 +1117,58 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val reg = resultRegister ?: RegisterOrPair.AY
|
||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
if(!needAsave) {
|
||||
var needAsaveForArg0 = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
if(!needAsaveForArg0) {
|
||||
val mr0 = fcall.args[0] as? PtMemoryByte
|
||||
val mr1 = fcall.args[1] as? PtMemoryByte
|
||||
if (mr0 != null)
|
||||
needAsave = mr0.address !is PtNumber
|
||||
needAsaveForArg0 = mr0.address !is PtNumber
|
||||
if (mr1 != null)
|
||||
needAsave = needAsave or (mr1.address !is PtNumber)
|
||||
needAsaveForArg0 = needAsaveForArg0 or (mr1.address !is PtNumber)
|
||||
}
|
||||
when(reg) {
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
if(needAsaveForArg0 && !asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||
// first 0 then 1
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
} else if(!needAsaveForArg0 && asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||
// first 1 then 0
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if (needAsaveForArg0)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
if (needAsaveForArg0)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
if(needAsaveForArg0 && !asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||
// first 0 then 1
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
} else if(!needAsaveForArg0 && asmgen.needAsaveForExpr(fcall.args[1])) {
|
||||
// first 1 then 0
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if (needAsaveForArg0)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if (needAsaveForArg0)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
if (needAsaveForArg0)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
if (needAsaveForArg0)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" tax")
|
||||
}
|
||||
@@ -1123,7 +1199,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
@@ -1134,7 +1210,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the msb byte out of the word array
|
||||
if(arg.splitWords) {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1151,7 +1229,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable)
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
val arrayVar = asmgen.asmVariableName(arg.variable!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1223,7 +1303,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
@@ -1233,7 +1313,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
|
||||
// just read the lsb byte out of the word array
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
|
||||
if(arg.variable==null)
|
||||
TODO("support for ptr indexing ${arg.position}")
|
||||
|
||||
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable!!)+"_lsb" else asmgen.asmVariableName(arg.variable!!)
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
|
||||
@@ -1298,7 +1381,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
|
||||
return when (value) {
|
||||
is PtIdentifier -> {
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1311,11 +1394,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
||||
|
||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
|
||||
addr.add(variable)
|
||||
addr.parent = call
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT))
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
}
|
||||
}
|
||||
@@ -1332,7 +1415,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1342,7 +1425,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(conv.dt), null, value.position, variableAsmName = varname)
|
||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||
val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
|
||||
asmgen.translateNormalAssignment(assign, scope)
|
||||
}
|
||||
conv.reg != null -> {
|
||||
@@ -1350,7 +1433,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(value.position)
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,value.position)
|
||||
addr.add(value)
|
||||
addr.parent = call
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
@@ -1360,7 +1443,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||
val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
|
||||
asmgen.translateNormalAssignment(assign, scope)
|
||||
}
|
||||
else -> throw AssemblyError("callconv")
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.IPtSubroutine
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.ast.PtSub
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.core.RegisterOrStatusflag
|
||||
|
||||
|
||||
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!!.isByteOrBool -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
returntype!!.isWord -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
returntype!!.isFloat -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
}
|
||||
listOf(Pair(register, returntype!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
||||
return when {
|
||||
returntype?.isByteOrBool==true -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
returntype?.isWord==true -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
returntype?.isFloat==true -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
returntype==null -> null
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
}
|
||||
}
|
||||
@@ -33,224 +33,351 @@ internal class ForLoopsAsmGen(
|
||||
}
|
||||
|
||||
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.add(endLabel)
|
||||
val stepsize=range.step.asConstInteger()!!
|
||||
|
||||
if(stepsize < -1) {
|
||||
if(range.step.asConstInteger()!! < -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.isByteArray -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||
if (stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
|
||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun forOverNonconstByteRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
|
||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||
when (stepsize) {
|
||||
-1 if range.to.asConstInteger()==0 -> {
|
||||
// simple loop downto 0 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
}
|
||||
-1 if range.to.asConstInteger()==1 -> {
|
||||
// simple loop downto 1 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
}
|
||||
1, -1 -> forOverBytesRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else -> forOverBytesRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverBytesRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// bytes range, step 1 or -1
|
||||
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
else {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
}
|
||||
else if (stepsize==1 || stepsize==-1) {
|
||||
// bytes array, step 1 or -1
|
||||
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
}
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
// bytes, step >= 2 or <= -2
|
||||
|
||||
// loop over byte range via loopvar
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bmi $loopLabel
|
||||
beq $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bpl $loopLabel""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
}
|
||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp $toValueVar
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
// use self-modifying code to store the loop end comparison value
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #1
|
||||
bne +
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
}
|
||||
else if (stepsize == 1 || stepsize == -1) {
|
||||
// words, step 1 or -1
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverBytesRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// bytes range, step >= 2 or <= -2
|
||||
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
|
||||
asmgen.romableError("self-modifying code (forloop over bytes range)", forloop.position) // TODO fix romable; there is self-modifying code below
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bmi $loopLabel
|
||||
beq $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bpl $loopLabel""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
private fun forOverNonconstWordRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #1
|
||||
bne +
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize == 1 || stepsize == -1)
|
||||
forOverWordsRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else if (stepsize > 0)
|
||||
forOverWordsRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else
|
||||
forOverWordsRangeStepGreaterOneDescending(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
}
|
||||
|
||||
private fun forOverWordsRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// words range, step 1 or -1
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
asmgen.out(" sty $toValueVar+1")
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
cmp $toValueVar+1
|
||||
bne +
|
||||
lda $varname
|
||||
cmp $toValueVar
|
||||
beq $endLabel""")
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bne +
|
||||
lda $varname
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
beq $endLabel""")
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize > 0) {
|
||||
// (u)words, step >= 2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||
// (u)words, step >= 2
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
if (iterableDt.isUnsignedWordArray) {
|
||||
asmgen.out("""
|
||||
asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||
if (iterableDt.isUnsignedWordArray) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -266,8 +393,8 @@ $modifiedLabel2 lda #0 ; modified
|
||||
bcc $endLabel
|
||||
bcs $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -283,20 +410,23 @@ $modifiedLabel lda #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
// (u)words, step <= -2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||
// (u)words, step <= -2
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
@@ -313,12 +443,6 @@ $modifiedLabel sbc #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||
@@ -330,23 +454,23 @@ $endLabel""")
|
||||
sta P8ZP_SCRATCH_W2 ; to
|
||||
sty P8ZP_SCRATCH_W2+1 ; to
|
||||
lda $fromVar
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
lda $fromVar+1
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel
|
||||
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
|
||||
cmp $fromVar
|
||||
tya
|
||||
sbc $fromVar+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
@@ -362,11 +486,11 @@ $endLabel""")
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
cpy $fromVar+1
|
||||
bcc $endLabel
|
||||
bne +
|
||||
cmp $fromVar
|
||||
bcc $endLabel
|
||||
cpy $fromVar+1
|
||||
bcc $endLabel
|
||||
bne +
|
||||
cmp $fromVar
|
||||
bcc $endLabel
|
||||
+""")
|
||||
}
|
||||
}
|
||||
@@ -376,38 +500,56 @@ $endLabel""")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0
|
||||
else -> 0u
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
asmgen.out("""
|
||||
lda #<$iterableName
|
||||
ldy #>$iterableName
|
||||
sta $loopLabel+1
|
||||
sty $loopLabel+2
|
||||
$loopLabel lda ${65535.toHex()} ; modified
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $loopLabel+1
|
||||
bne $loopLabel
|
||||
inc $loopLabel+2
|
||||
bne $loopLabel
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$indexVar .byte 0
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -422,20 +564,25 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.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")
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isSplitWordArray -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@@ -445,7 +592,7 @@ $loopLabel sty $indexVar
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255) {
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -460,21 +607,25 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.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")
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isWordArray -> {
|
||||
val length = numElements * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@@ -484,16 +635,16 @@ $loopLabel sty $indexVar
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(length<=127) {
|
||||
if(numElements<=127u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #$length
|
||||
cpy #${numElements*2u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 128 words, 256 bytes
|
||||
// array size is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -501,15 +652,17 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.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")
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||
@@ -15,12 +16,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// just ignore any result values from the function call.
|
||||
}
|
||||
|
||||
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>) =
|
||||
when(params.size) {
|
||||
1 -> params[0].type.isIntegerOrBool
|
||||
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool
|
||||
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>): Boolean {
|
||||
// When the parameter(s) are passed via an explicit register or register pair,
|
||||
// we consider them NOT to be optimized into (possibly different) CPU registers.
|
||||
// Just load them in whatever the register spec says.
|
||||
return when (params.size) {
|
||||
1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
|
||||
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translateFunctionCall(call: PtFunctionCall) {
|
||||
// Output only the code to set up the parameters and perform the actual call
|
||||
@@ -28,8 +33,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||
// (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 symbol = asmgen.symbolTable.lookup(call.name)!!
|
||||
if(symbol.type == StNodeType.LABEL) {
|
||||
require(call.void)
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
|
||||
return
|
||||
}
|
||||
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||
|
||||
if(sub is PtAsmSub) {
|
||||
@@ -47,6 +58,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
if(bank==null) {
|
||||
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
||||
if(varbank!=null) {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no codegen yet for non-const bank in subroutine call that's usable in ROM ${call.position}")
|
||||
|
||||
// self-modifying code: set jsrfar bank argument
|
||||
when(asmgen.options.compTarget.name) {
|
||||
"cx16" -> {
|
||||
// JSRFAR can jump to a banked RAM address as well!
|
||||
@@ -124,22 +139,24 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
}
|
||||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
|
||||
val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(optimizeIntArgsViaCpuRegisters(parameters)) {
|
||||
// Note that if the args fit into cpu registers, we don't concern ourselves here
|
||||
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
|
||||
// That is now up to the subroutine itself.
|
||||
useCpuRegistersForArgs(call.args, sub)
|
||||
} else {
|
||||
// arguments via variables
|
||||
val (normalParams, registerParams) = sub.parameters.withIndex().partition { it.value.register == null }
|
||||
val paramValues = parameters.zip(call.args)
|
||||
val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
|
||||
if (normalParams.isNotEmpty()) {
|
||||
for (arg in normalParams.zip(call.args))
|
||||
argumentViaVariable(sub, arg.first.value, arg.second)
|
||||
for (p in normalParams)
|
||||
argumentViaVariable(sub, p.first, p.second)
|
||||
}
|
||||
if (registerParams.isNotEmpty()) {
|
||||
// the R0-R15 'registers' are not really registers. They're just special variables.
|
||||
for (arg in registerParams.zip(call.args))
|
||||
argumentViaVariable(sub, arg.first.value, arg.second)
|
||||
for (p in registerParams)
|
||||
argumentViaVariable(sub, p.first, p.second)
|
||||
}
|
||||
}
|
||||
asmgen.out(" jsr $subAsmName")
|
||||
@@ -150,7 +167,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
|
||||
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
|
||||
val params = sub.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
when(params.size) {
|
||||
1 -> {
|
||||
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
|
||||
@@ -159,12 +176,22 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
2 -> {
|
||||
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
|
||||
// 2 byte params, second in Y, first in A
|
||||
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||
if(asmgen.needAsaveForExpr(args[1]))
|
||||
asmgen.out(" pha")
|
||||
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||
if(asmgen.needAsaveForExpr(args[1]))
|
||||
asmgen.out(" pla")
|
||||
if(asmgen.needAsaveForExpr(args[0]) && !asmgen.needAsaveForExpr(args[1])) {
|
||||
// first 0 then 1
|
||||
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||
} else if(!asmgen.needAsaveForExpr(args[0]) && asmgen.needAsaveForExpr(args[1])) {
|
||||
// first 1 then 0
|
||||
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||
} else {
|
||||
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
|
||||
if (asmgen.needAsaveForExpr(args[1]))
|
||||
asmgen.out(" pha")
|
||||
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
|
||||
if (asmgen.needAsaveForExpr(args[1]))
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
} else {
|
||||
throw AssemblyError("cannot use registers for word+byte")
|
||||
}
|
||||
@@ -208,9 +235,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
val param = sub.parameters[it]
|
||||
val arg = call.args[it]
|
||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
||||
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
|
||||
if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
|
||||
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||
else if(registersUsed.any {it.statusflag!=null}) {
|
||||
else if(registersUsed.any { r-> r.statusflag!=null }) {
|
||||
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||
}
|
||||
else {
|
||||
@@ -300,20 +327,20 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
register!!
|
||||
if(requiredDt.largerSizeThan(value.type)) {
|
||||
// we need to sign extend the source, do this via temporary word variable
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||
} else {
|
||||
val scope = value.definingISub()
|
||||
val target: AsmAssignTarget =
|
||||
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||
if(parameter.value.type.isByte && register.isWord())
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||
else {
|
||||
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
|
||||
}
|
||||
val src = if(value.type.isPassByRef) {
|
||||
if(value is PtIdentifier) {
|
||||
val addr = PtAddressOf(Position.DUMMY)
|
||||
val addr = PtAddressOf(value.type.typeForAddressOf(false), false, Position.DUMMY)
|
||||
addr.add(value)
|
||||
addr.parent = scope as PtNode
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
@@ -323,7 +350,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
}
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, target.position), scope)
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, listOf(target), program.memsizer, target.position), scope)
|
||||
}
|
||||
return RegisterOrStatusflag(register, null)
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
|
||||
import prog8.codegen.cpu6502.assignment.PointerAssignmentsGen
|
||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
|
||||
internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private val st: SymbolTable,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val pointergen: PointerAssignmentsGen,
|
||||
private val assignmentAsmGen: AssignmentAsmGen,
|
||||
private val errors: IErrorReporter) {
|
||||
|
||||
@@ -32,10 +34,18 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
|
||||
val compareCond = stmt.condition as? PtBinaryExpression
|
||||
if(compareCond!=null) {
|
||||
|
||||
val useBIT = asmgen.checkIfConditionCanUseBIT(compareCond)
|
||||
if(useBIT!=null) {
|
||||
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||
val (testBitSet, variable, bitmask) = useBIT
|
||||
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
|
||||
}
|
||||
|
||||
val rightDt = compareCond.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
||||
rightDt.isWord -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -55,9 +65,80 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
val dereference = stmt.condition as? PtPointerDeref
|
||||
if(dereference!=null) {
|
||||
val (zpPtrVar, offset) = pointergen.deref(dereference)
|
||||
asmgen.loadIndirectByte(zpPtrVar, offset)
|
||||
return if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("beq", stmt)
|
||||
}
|
||||
|
||||
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
|
||||
}
|
||||
|
||||
private fun translateIfBIT(ifElse: PtIfElse, jumpAfterIf: PtJump?, testForBitSet: Boolean, variable: PtIdentifier, bitmask: Int) {
|
||||
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||
|
||||
fun branch(branchInstr: String, target: AsmGen6502Internal.JumpTarget) {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
if(target.indirect)
|
||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||
if(ifElse.hasElse())
|
||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||
else
|
||||
asmgen.out(" $branchInstr ${target.asmLabel}")
|
||||
}
|
||||
|
||||
when (bitmask) {
|
||||
128 -> {
|
||||
// test via bit + N flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bmi", target)
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bpl", ifElse)
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bpl", target)
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bmi", ifElse)
|
||||
}
|
||||
return
|
||||
}
|
||||
64 -> {
|
||||
// test via bit + V flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bvs", target)
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bvc", ifElse)
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
branch("bvc", target)
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bvs", ifElse)
|
||||
}
|
||||
return
|
||||
}
|
||||
else -> throw AssemblyError("BIT only works for bits 6 and 7")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNotExtsubReturnsStatusReg(condition: PtExpression) {
|
||||
val fcall = condition as? PtFunctionCall
|
||||
@@ -70,82 +151,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun fallbackTranslateForSimpleCondition(ifElse: PtIfElse) {
|
||||
val bittest = ifElse.condition as? PtBuiltinFunctionCall
|
||||
val jumpAfterIf = ifElse.ifScope.children.singleOrNull() as? PtJump
|
||||
|
||||
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
|
||||
val variable = bittest.args[0] as PtIdentifier
|
||||
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
|
||||
val testForBitSet = bittest.name.endsWith("_set")
|
||||
when (bitnumber) {
|
||||
7 -> {
|
||||
// test via bit + N flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.needsExpressionEvaluation)
|
||||
if(target.indirect)
|
||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||
if(ifElse.hasElse())
|
||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||
else
|
||||
asmgen.out(" bmi ${target.asmLabel}")
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bpl", ifElse)
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.needsExpressionEvaluation)
|
||||
if(target.indirect)
|
||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||
if(ifElse.hasElse())
|
||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||
else
|
||||
asmgen.out(" bpl ${target.asmLabel}")
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bmi", ifElse)
|
||||
}
|
||||
return
|
||||
}
|
||||
6 -> {
|
||||
// test via bit + V flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.needsExpressionEvaluation)
|
||||
if(target.indirect)
|
||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||
if(ifElse.hasElse())
|
||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||
else
|
||||
asmgen.out(" bvs ${target.asmLabel}")
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bvc", ifElse)
|
||||
} else {
|
||||
if(jumpAfterIf!=null) {
|
||||
val target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.needsExpressionEvaluation)
|
||||
if(target.indirect)
|
||||
throw AssemblyError("cannot BIT to indirect label ${ifElse.position}")
|
||||
if(ifElse.hasElse())
|
||||
throw AssemblyError("didn't expect else part here ${ifElse.position}")
|
||||
else
|
||||
asmgen.out(" bvc ${target.asmLabel}")
|
||||
}
|
||||
else
|
||||
translateIfElseBodies("bvs", ifElse)
|
||||
}
|
||||
return
|
||||
}
|
||||
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
|
||||
}
|
||||
}
|
||||
|
||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||
asmgen.assignConditionValueToRegisterAndTest(ifElse.condition)
|
||||
if(jumpAfterIf!=null)
|
||||
@@ -154,6 +161,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", ifElse)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodiesSignedByte(elseConditional: String, value: PtExpression, stmt: PtIfElse) {
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(stmt.hasElse()) {
|
||||
// if and else blocks
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
branchElse(elseLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
// no else block
|
||||
branchElse(afterIfLabel)
|
||||
asmgen.translate(stmt.ifScope)
|
||||
}
|
||||
asmgen.out(afterIfLabel)
|
||||
}
|
||||
|
||||
private fun translateJumpElseBodiesSignedByte(elseConditional: String, value: PtExpression, jump: PtJump, elseBlock: PtNodeGroup) {
|
||||
fun branchTarget(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
fun branchElse(label: String) {
|
||||
when (elseConditional) {
|
||||
"<" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $label""")
|
||||
}
|
||||
">=" -> {
|
||||
asmgen.out("""
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $label""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong conditional $elseConditional")
|
||||
}
|
||||
}
|
||||
|
||||
var target = asmgen.getJumpTarget(jump, false)
|
||||
asmgen.cmpAwithByteValue(value, true)
|
||||
if(target.indirect) {
|
||||
branchElse("+")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
asmgen.out("+")
|
||||
} else {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
branchTarget(target.asmLabel)
|
||||
}
|
||||
asmgen.translate(elseBlock)
|
||||
}
|
||||
|
||||
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
|
||||
// comparison value is already in A
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
@@ -162,7 +256,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" $elseBranchInstr $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -179,10 +273,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" $falseBranch +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
asmgen.out("+")
|
||||
} else {
|
||||
require(!target.needsExpressionEvaluation)
|
||||
asmgen.out(" $branchInstr ${target.asmLabel}")
|
||||
@@ -218,40 +311,11 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
translateIfElseBodies("beq", stmt)
|
||||
}
|
||||
"<" -> translateByteLess(stmt, signed, jumpAfterIf)
|
||||
"<=" -> {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
"<=" -> translateByteLessEqual(stmt, signed, jumpAfterIf)
|
||||
">" -> translateByteGreater(stmt, signed, jumpAfterIf)
|
||||
">=" -> {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
} else {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
">=" -> translateByteGreaterEqual(stmt, signed, jumpAfterIf)
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||
@@ -294,7 +358,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -314,7 +379,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -361,7 +426,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | bne ++")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -383,7 +449,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
bpl $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -410,13 +476,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
@@ -424,25 +490,43 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteLessEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
// X<=Y -> Y>=X (reverse of >=)
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.left, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
if(signed) {
|
||||
// X>Y --> Y<X
|
||||
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
|
||||
asmgen.cmpAwithByteValue(condition.left, true)
|
||||
if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
|
||||
translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
translateIfElseBodiesSignedByte("<", condition.left, stmt)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null) {
|
||||
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bcc + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -461,7 +545,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -474,6 +558,23 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateByteGreaterEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
|
||||
val condition = stmt.condition as PtBinaryExpression
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
|
||||
return if(signed) {
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodiesSignedByte(">=", condition.right, jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodiesSignedByte(">=", condition.right, stmt)
|
||||
} else {
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null)
|
||||
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val signed = condition.left.type.isSigned
|
||||
val constValue = condition.right.asConstInteger()
|
||||
@@ -543,7 +644,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -570,7 +672,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -598,7 +700,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
cmp $valueLsb
|
||||
bcs +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
_jump jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -623,7 +726,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcs $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -675,7 +778,8 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -702,7 +806,7 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -729,7 +833,8 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -752,7 +857,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -792,7 +897,9 @@ _jump jmp (${target.asmLabel})
|
||||
private fun loadAndCmp0MSB(value: PtExpression) {
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords)
|
||||
asmgen.out(" lda ${varname}_msb,y")
|
||||
@@ -808,11 +915,11 @@ _jump jmp (${target.asmLabel})
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
} else {
|
||||
if(value.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
} else {
|
||||
asmgen.out(" lda #>${asmgen.asmVariableName(value.identifier)}")
|
||||
var varname = asmgen.asmVariableName(value.identifier!!)
|
||||
if(value.identifier!!.type.isSplitWordArray) {
|
||||
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
|
||||
}
|
||||
asmgen.out(" lda #>$varname")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -837,7 +944,8 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -865,7 +973,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -886,8 +994,10 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@@ -916,7 +1026,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -944,7 +1055,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -981,7 +1092,8 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1009,7 +1121,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1031,8 +1143,10 @@ _jump jmp (${target.asmLabel})
|
||||
if(value is PtIdentifier)
|
||||
return compareLsbMsb(value.name, value.name+"+1")
|
||||
if(value is PtArrayIndexer) {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val constIndex = value.index.asConstInteger()
|
||||
val varname = asmgen.asmVariableName(value.variable)
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
if(constIndex!=null) {
|
||||
if(value.splitWords) {
|
||||
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
|
||||
@@ -1061,7 +1175,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1089,7 +1204,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1144,7 +1259,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
|
||||
}
|
||||
@@ -1165,7 +1282,9 @@ _jump jmp (${target.asmLabel})
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if (constIndex != null) {
|
||||
val varName = asmgen.asmVariableName(value.variable)
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varName = asmgen.asmVariableName(value.variable!!)
|
||||
if(value.splitWords) {
|
||||
return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
|
||||
}
|
||||
@@ -1204,7 +1323,8 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1229,7 +1349,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1256,7 +1376,8 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1281,7 +1402,7 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1310,7 +1431,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1339,7 +1461,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1369,7 +1491,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1398,7 +1521,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1431,7 +1554,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1460,7 +1584,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1489,7 +1613,8 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
require(!target.indexedX)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1518,7 +1643,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1540,8 +1665,10 @@ _jump jmp (${target.asmLabel})
|
||||
fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) {
|
||||
val constIndex = left.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
if(left.variable==null)
|
||||
TODO("support for ptr indexing ${left.position}")
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varName = asmgen.asmVariableName(left.variable)
|
||||
val varName = asmgen.asmVariableName(left.variable!!)
|
||||
if(left.splitWords) {
|
||||
return if(notEquals)
|
||||
translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex")
|
||||
@@ -1604,16 +1731,16 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
if(left.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varname = left.identifier.name
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYNotEquals("#<$varname", "#>$varname")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -1656,16 +1783,16 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
if(left.identifier.type.isSplitWordArray) {
|
||||
TODO("address of split word array")
|
||||
} else {
|
||||
val varname = if(left.identifier!!.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
val varname = left.identifier.name
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
left.identifier!!.name
|
||||
}
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
|
||||
translateAYEquals("#<$varname", "#>$varname")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtBuiltinFunctionCall
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtIfExpression
|
||||
import prog8.code.ast.PtNumber
|
||||
import prog8.code.ast.PtPrefix
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.CpuRegister
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.LogicalOperators
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
|
||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
@@ -21,24 +9,26 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
|
||||
|
||||
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
|
||||
require(target.datatype==expr.type)
|
||||
require(target.datatype==expr.type ||
|
||||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
|
||||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
|
||||
val falseLabel = asmgen.makeLabel("ifexpr_false")
|
||||
val endLabel = asmgen.makeLabel("ifexpr_end")
|
||||
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
expr.type.isWord -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
|
||||
expr.type.isWord || expr.type.isString -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
@@ -54,47 +44,90 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||
if (condition is PtBinaryExpression) {
|
||||
val rightDt = condition.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
internal fun assignBranchCondExpression(target: AsmAssignTarget, expr: PtBranchCondExpression) {
|
||||
require(target.datatype==expr.type ||
|
||||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
|
||||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
|
||||
|
||||
if(target.kind==TargetStorageKind.REGISTER && target.datatype.isUnsignedByte && target.register==RegisterOrPair.A) {
|
||||
if(expr.condition==BranchCondition.CC) {
|
||||
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
return
|
||||
}
|
||||
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
|
||||
asmgen.out(" lda #0 | rol a | eor #1")
|
||||
return
|
||||
}
|
||||
}
|
||||
else if(expr.condition==BranchCondition.CS) {
|
||||
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
|
||||
asmgen.out(" lda #0 | rol a | eor #1")
|
||||
return
|
||||
}
|
||||
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(condition is PtPrefix && condition.operator=="not") {
|
||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||
} else {
|
||||
// 'simple' condition, check if it is a byte bittest
|
||||
val bittest = condition as? PtBuiltinFunctionCall
|
||||
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
|
||||
val variable = bittest.args[0] as PtIdentifier
|
||||
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
|
||||
val testForBitSet = bittest.name.endsWith("_set")
|
||||
when (bitnumber) {
|
||||
7 -> {
|
||||
// test via bit + N flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) asmgen.out(" bpl $falseLabel")
|
||||
else asmgen.out(" bmi $falseLabel")
|
||||
return
|
||||
}
|
||||
6 -> {
|
||||
// test via bit + V flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) asmgen.out(" bvc $falseLabel")
|
||||
else asmgen.out(" bvs $falseLabel")
|
||||
return
|
||||
}
|
||||
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
|
||||
|
||||
val trueLabel = asmgen.makeLabel("branchexpr_true")
|
||||
val endLabel = asmgen.makeLabel("branchexpr_end")
|
||||
val branch = asmgen.branchInstruction(expr.condition, false)
|
||||
|
||||
asmgen.out(" $branch $trueLabel")
|
||||
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
expr.type.isWord || expr.type.isString -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(trueLabel)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
|
||||
asmgen.out(endLabel)
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||
when (condition) {
|
||||
is PtBinaryExpression -> {
|
||||
val rightDt = condition.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||
asmgen.out(" beq $falseLabel")
|
||||
is PtPrefix if condition.operator=="not" -> {
|
||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||
}
|
||||
|
||||
else -> {
|
||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +152,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
asmgen.out(" beq $falseLabel")
|
||||
} else {
|
||||
@@ -227,7 +260,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne +
|
||||
@@ -251,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp $varRight+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne $falseLabel
|
||||
@@ -274,7 +307,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp #<$number
|
||||
bne +
|
||||
@@ -297,7 +330,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp #>$number
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out( """
|
||||
cmp #<$number
|
||||
bne $falseLabel
|
||||
@@ -316,7 +349,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
beq $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
||||
}
|
||||
}
|
||||
@@ -331,13 +364,37 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
|
||||
// optimized code for byte comparisons with 0
|
||||
|
||||
val useBIT = asmgen.checkIfConditionCanUseBIT(condition)
|
||||
if(useBIT!=null) {
|
||||
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||
val (testForBitSet, variable, bitmask) = useBIT
|
||||
when (bitmask) {
|
||||
128 -> {
|
||||
// test via bit + N flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) asmgen.out(" bpl $falseLabel")
|
||||
else asmgen.out(" bmi $falseLabel")
|
||||
return
|
||||
}
|
||||
64 -> {
|
||||
// test via bit + V flag
|
||||
asmgen.out(" bit ${variable.name}")
|
||||
if(testForBitSet) asmgen.out(" bvc $falseLabel")
|
||||
else asmgen.out(" bvs $falseLabel")
|
||||
return
|
||||
}
|
||||
else -> throw AssemblyError("BIT can only work on bits 7 and 6")
|
||||
}
|
||||
}
|
||||
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition.left)
|
||||
when (condition.operator) {
|
||||
"==" -> asmgen.out(" bne $falseLabel")
|
||||
|
||||
@@ -39,22 +39,22 @@ internal class ProgramAndVarsGen(
|
||||
// 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 floatFill = compTarget.getFloatAsmBytes(flt.key)
|
||||
val floatvalue = flt.key
|
||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||
}
|
||||
|
||||
structInstances2asm()
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
|
||||
private fun header() {
|
||||
val ourName = this.javaClass.name
|
||||
val cpu = when(compTarget.machine.cpu) {
|
||||
val cpu = when(compTarget.cpu) {
|
||||
CpuType.CPU6502 -> "6502"
|
||||
CpuType.CPU65c02 -> "w65c02"
|
||||
CpuType.CPU65C02 -> "w65c02"
|
||||
else -> "unsupported"
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ internal class ProgramAndVarsGen(
|
||||
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("P8ZP_SCRATCH_PTR = ${zp.SCRATCH_PTR} ; word")
|
||||
if(compTarget.name=="c64") {
|
||||
if(options.floats)
|
||||
asmgen.out("PROG8_C64_BANK_CONFIG=31 ; basic+IO+kernal")
|
||||
@@ -87,209 +88,224 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
when(options.output) {
|
||||
OutputType.RAW -> {
|
||||
asmgen.out("; ---- raw assembler program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
if(options.compTarget.customLauncher.isNotEmpty()) {
|
||||
asmgen.out("; ---- custom launcher assembler program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
for(line in options.compTarget.customLauncher) {
|
||||
asmgen.out(line)
|
||||
}
|
||||
OutputType.PRG -> {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
if(options.output == OutputType.LIBRARY) {
|
||||
|
||||
asmgen.out("; ---- library assembler program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" jmp p8b_main.p8s_start")
|
||||
// note: the jmp above has 2 effects:
|
||||
// 1. it prevents 64tass from stripping away all procs as unused code
|
||||
// 2. it functions as the first entrypoint of the library, required anyway, to run the variable initialization/bss clear bootstrap code.
|
||||
|
||||
} else {
|
||||
|
||||
when (options.output) {
|
||||
OutputType.LIBRARY -> { }
|
||||
OutputType.RAW -> {
|
||||
asmgen.out("; ---- raw assembler program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if (!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
}
|
||||
OutputType.PRG -> {
|
||||
when (options.launcher) {
|
||||
CbmPrgLauncherType.BASIC -> {
|
||||
if (options.loadAddress != options.compTarget.PROGRAM_LOAD_ADDRESS) {
|
||||
errors.err(
|
||||
"BASIC output must have load address ${options.compTarget.PROGRAM_LOAD_ADDRESS.toHex()}",
|
||||
program.position
|
||||
)
|
||||
}
|
||||
asmgen.out("; ---- basic program with sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
val year = LocalDate.now().year
|
||||
asmgen.out(" .word (+), $year")
|
||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||
asmgen.out("+\t.word 0")
|
||||
asmgen.out("prog8_entrypoint")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if (!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
}
|
||||
|
||||
CbmPrgLauncherType.NONE -> {
|
||||
// this is the same as RAW
|
||||
asmgen.out("; ---- program without basic sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if (!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
}
|
||||
asmgen.out("; ---- basic program with sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
val year = LocalDate.now().year
|
||||
asmgen.out(" .word (+), $year")
|
||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||
asmgen.out("+\t.word 0")
|
||||
asmgen.out("prog8_entrypoint")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
}
|
||||
CbmPrgLauncherType.NONE -> {
|
||||
// this is the same as RAW
|
||||
asmgen.out("; ---- program without basic sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
}
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
asmgen.out("; ---- atari xex program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if (!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
}
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
asmgen.out("; ---- atari xex program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
asmgen.out("prog8_program_start\t; start of program label")
|
||||
asmgen.out(" cld")
|
||||
asmgen.out(" tsx ; save stackpointer for sys.exit()")
|
||||
asmgen.out(" stx prog8_lib.orig_stackpointer")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr p8_sys_startup.init_system")
|
||||
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
|
||||
}
|
||||
}
|
||||
|
||||
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""")
|
||||
}
|
||||
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 p8_sys_startup.cleanup_at_exit")
|
||||
}
|
||||
"c64" -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||
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 p8_sys_startup.cleanup_at_exit")
|
||||
}
|
||||
"c64" -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out(" jsr p8b_main.p8s_start")
|
||||
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun memorySlabs() {
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
asmgen.out("; memory slabs\n .section slabs_BSS")
|
||||
asmgen.out("prog8_slabs\t.block")
|
||||
asmgen.out("; memory slabs\n .section BSS_SLABS")
|
||||
asmgen.out("$StMemorySlabBlockName\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")
|
||||
asmgen.out("\t.bend\n .send BSS_SLABS")
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.BYTE -> asmgen.out("$name .char ?")
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.WORD -> asmgen.out("$name .sint ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.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() {
|
||||
asmgen.out(" .dsection STRUCTINSTANCES\n")
|
||||
|
||||
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) {
|
||||
if(options.compTarget.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssVars = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.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) {
|
||||
if(options.compTarget.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||
}
|
||||
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
|
||||
relocatedBssStart = options.compTarget.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.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) {
|
||||
if(options.compTarget.BSSGOLDENRAM_START == 0u ||
|
||||
options.compTarget.BSSGOLDENRAM_END == 0u ||
|
||||
options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
|
||||
}
|
||||
relocateBssSlabs = true
|
||||
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
|
||||
relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
|
||||
relocatedBssEnd = options.compTarget.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) {
|
||||
if(options.compTarget.BSSHIGHRAM_START == 0u ||
|
||||
options.compTarget.BSSHIGHRAM_END == 0u ||
|
||||
options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
|
||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
|
||||
}
|
||||
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
|
||||
relocatedBssStart = options.compTarget.BSSHIGHRAM_START
|
||||
relocatedBssEnd = options.compTarget.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(" .dsection BSS_SLABS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
if(relocateBssSlabs)
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
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(" .dsection BSS_NOCLEAR")
|
||||
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(" .dsection BSS_SLABS")
|
||||
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\"")
|
||||
asmgen.out(" .dsection BSS_SLABS")
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for BSS_SLABS section\"")
|
||||
}
|
||||
}
|
||||
|
||||
if(relocatedBssEnd >= options.memtopAddress)
|
||||
options.memtopAddress = relocatedBssEnd+1u
|
||||
|
||||
asmgen.out(" ; memtop check")
|
||||
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||
}
|
||||
@@ -316,9 +332,10 @@ internal class ProgramAndVarsGen(
|
||||
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))
|
||||
val constvalue = assign.value as? PtNumber
|
||||
if(constvalue==null || constvalue.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.
|
||||
// the other variables that should be set to zero are done so as part of the BSS section clear.
|
||||
}
|
||||
asmgen.out(" rts\n .bend")
|
||||
}
|
||||
@@ -335,7 +352,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInBlock = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -349,11 +366,100 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInBlock
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
}
|
||||
|
||||
private fun asmTypeString(dt: DataType): String {
|
||||
return when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte"
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun structInstances2asm() {
|
||||
|
||||
fun initValues(instance: StStructInstance): List<String> {
|
||||
val structtype: StStruct = symboltable.lookup(instance.structName) as StStruct
|
||||
return structtype.fields.zip(instance.initialValues).map { (field, value) ->
|
||||
if(field.first.isFloat) {
|
||||
"["+compTarget.getFloatAsmBytes(value.number!!)+"]"
|
||||
} else {
|
||||
when {
|
||||
value.number!=null -> {
|
||||
if(field.first.isPointer)
|
||||
"$"+value.number!!.toInt().toString(16)
|
||||
else if(field.first.isInteger)
|
||||
value.number!!.toInt().toString()
|
||||
else
|
||||
value.number.toString()
|
||||
}
|
||||
value.addressOfSymbol!=null -> value.addressOfSymbol!!
|
||||
value.boolean!=null -> if(value.boolean==true) "1" else "0"
|
||||
else -> throw AssemblyError("weird struct initial value $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asmgen.out("; struct types")
|
||||
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
|
||||
asmgen.out("${it.structName} .struct $structargs\n")
|
||||
structtype.fields.withIndex().forEach { (index, field) ->
|
||||
val dt = field.first
|
||||
val varname = "f${index}"
|
||||
val type = when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
|
||||
}
|
||||
asmgen.out(" .endstruct\n")
|
||||
}
|
||||
|
||||
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
|
||||
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
|
||||
asmgen.out(" .section BSS\n")
|
||||
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
|
||||
instancesNoInit.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val zerovalues = structtype.fields.map { field ->
|
||||
if(field.first.isFloat) {
|
||||
val floatbytes = List(compTarget.memorySize(BaseDataType.FLOAT)) { "?" }
|
||||
"[${floatbytes.joinToString(",")}]"
|
||||
}
|
||||
else "?"
|
||||
}
|
||||
asmgen.out("${it.name} .dstruct ${it.structName}, ${zerovalues.joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .endblock\n")
|
||||
asmgen.out(" .send BSS\n")
|
||||
|
||||
asmgen.out("; struct instances with initialization values\n")
|
||||
asmgen.out(" .section STRUCTINSTANCES\n")
|
||||
asmgen.out("$StStructInstanceBlockName .block\n")
|
||||
instances.forEach {
|
||||
val instancename = it.name.substringAfter('.')
|
||||
asmgen.out("$instancename .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .endblock\n")
|
||||
asmgen.out(" .send STRUCTINSTANCES\n")
|
||||
}
|
||||
|
||||
internal fun translateAsmSubroutine(sub: PtAsmSub) {
|
||||
if(sub.inline) {
|
||||
return // subroutine gets inlined at call site.
|
||||
@@ -403,7 +509,7 @@ internal class ProgramAndVarsGen(
|
||||
val varsInSubroutine = getVars(scope)
|
||||
|
||||
// Zeropage Variables
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
|
||||
zeropagevars2asm(varnames)
|
||||
|
||||
// MemDefs and Consts
|
||||
@@ -421,7 +527,7 @@ internal class ProgramAndVarsGen(
|
||||
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
|
||||
entrypointInitialization()
|
||||
|
||||
val params = sub.parameters
|
||||
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
|
||||
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
|
||||
asmgen.out("; simple int arg(s) passed via cpu register(s)")
|
||||
|
||||
@@ -457,27 +563,27 @@ internal class ProgramAndVarsGen(
|
||||
sub.children.forEach { asmgen.translate(it) }
|
||||
|
||||
asmgen.out("; variables")
|
||||
asmgen.out(" .section BSS")
|
||||
asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
|
||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||
if(addr!=null)
|
||||
asmgen.out("$name = $addr")
|
||||
else when(dt) {
|
||||
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
asmgen.out(" .send BSS")
|
||||
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
asmgen.out(" .send BSS_NOCLEAR")
|
||||
|
||||
// normal statically allocated variables
|
||||
val variables = varsInSubroutine
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
|
||||
.map { it.value as StStaticVariable }
|
||||
nonZpVariables2asm(variables)
|
||||
|
||||
@@ -532,12 +638,12 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
stringVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
outputStringvar(varname, 0, it.value.second, it.value.first)
|
||||
outputStringvar(varname, 0u, it.value.second, it.value.first)
|
||||
}
|
||||
|
||||
arrayVarsWithInitInZp.forEach {
|
||||
val varname = asmgen.asmVariableName(it.name)+"_init_value"
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
|
||||
arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
|
||||
}
|
||||
|
||||
asmgen.out("+")
|
||||
@@ -601,22 +707,37 @@ internal class ProgramAndVarsGen(
|
||||
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||
if(varsNoInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables")
|
||||
asmgen.out(" .section BSS")
|
||||
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
val (dirty, clean) = varsNoInit.partition { it.dirty }
|
||||
|
||||
fun generate(section: String, variables: List<StStaticVariable>) {
|
||||
asmgen.out(" .section $section")
|
||||
val (notAligned, aligned) = variables.partition { it.align == 0u }
|
||||
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
}
|
||||
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
|
||||
.forEach { uninitializedVariable2asm(it) }
|
||||
asmgen.out(" .send $section")
|
||||
}
|
||||
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach {
|
||||
uninitializedVariable2asm(it)
|
||||
|
||||
if(clean.isNotEmpty()) {
|
||||
// clean vars end up in BSS so they're at least cleared to 0 at startup
|
||||
generate("BSS", clean)
|
||||
}
|
||||
if(dirty.isNotEmpty()) {
|
||||
// Dirty vars actually are ALSO are put into BSS so they're cleared to 0 at program startup,
|
||||
// but NOT at each entry of the subroutine they're declared in.
|
||||
// This saves the STZ's instructions in the subroutine, while still having deterministic start state.
|
||||
// So there is no actual difference here when compared to the way non-dirty variables are allocated.
|
||||
generate("BSS", dirty)
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
if(varsWithInit.isNotEmpty()) {
|
||||
asmgen.out("; non-zeropage variables with init value")
|
||||
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
|
||||
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
|
||||
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
|
||||
notAlignedStrings.forEach {
|
||||
outputStringvar(
|
||||
it.name,
|
||||
@@ -624,6 +745,7 @@ internal class ProgramAndVarsGen(
|
||||
it.initializationStringValue!!.second,
|
||||
it.initializationStringValue!!.first
|
||||
)
|
||||
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
}
|
||||
alignedStrings.sortedBy { it.align }.forEach {
|
||||
outputStringvar(
|
||||
@@ -632,13 +754,22 @@ internal class ProgramAndVarsGen(
|
||||
it.initializationStringValue!!.second,
|
||||
it.initializationStringValue!!.first
|
||||
)
|
||||
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
}
|
||||
|
||||
notAlignedOther.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
if(it.dt.isArray)
|
||||
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
else
|
||||
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||
}
|
||||
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
if(it.dt.isArray)
|
||||
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
else
|
||||
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -650,57 +781,67 @@ internal class ProgramAndVarsGen(
|
||||
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
|
||||
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
|
||||
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
|
||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||
dt.isSplitWordArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 2
|
||||
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
||||
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
||||
}
|
||||
dt.isArray -> {
|
||||
alignVar(variable.align)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
|
||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||
}
|
||||
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
|
||||
dt.isPointerArray -> {
|
||||
TODO("pointers are not supported yet")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun alignVar(align: Int) {
|
||||
if(align > 1)
|
||||
private fun alignVar(align: UInt) {
|
||||
if(align > 1u)
|
||||
asmgen.out(" .align ${align.toHex()}")
|
||||
}
|
||||
|
||||
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||
val initialValue: Number =
|
||||
if(variable.initializationNumericValue!=null) {
|
||||
if(variable.dt.isFloat)
|
||||
variable.initializationNumericValue!!
|
||||
else
|
||||
variable.initializationNumericValue!!.toInt()
|
||||
} else 0
|
||||
if(!variable.dt.isArray && !variable.dt.isString) {
|
||||
throw AssemblyError("static variables with an initialization value can only be an array or a string, not ${variable.dt} (${variable.name} ${variable.astNode?.position}")
|
||||
// because numeric variables are in the BSS section and get initialized via assignment statements
|
||||
}
|
||||
|
||||
// val initialValue: Number =
|
||||
// if(variable.initializationNumericValue!=null) {
|
||||
// if(variable.dt.isFloat)
|
||||
// variable.initializationNumericValue!!
|
||||
// else
|
||||
// variable.initializationNumericValue!!.toInt()
|
||||
// } else 0
|
||||
//
|
||||
|
||||
val dt=variable.dt
|
||||
when {
|
||||
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||
dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||
dt.isFloat -> {
|
||||
if(initialValue==0) {
|
||||
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||
} else {
|
||||
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
|
||||
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
||||
}
|
||||
}
|
||||
// dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||
// dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||
// dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||
// dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||
// dt.isFloat -> {
|
||||
// if(initialValue==0) {
|
||||
// asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||
// } else {
|
||||
// val floatFill = compTarget.getFloatAsmBytes(initialValue)
|
||||
// asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
||||
// }
|
||||
// }
|
||||
dt.isString -> {
|
||||
throw AssemblyError("all string vars should have been interned into prog")
|
||||
}
|
||||
dt.isArray -> {
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
|
||||
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -708,7 +849,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
|
||||
private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
|
||||
alignVar(align)
|
||||
when {
|
||||
dt.isUnsignedByteArray || dt.isBoolArray -> {
|
||||
@@ -732,7 +873,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
dt.isSplitWordArray -> {
|
||||
if(dt.elementType().isUnsignedWord) {
|
||||
if(dt.elementType().isUnsignedWord || dt.elementType().isPointer) {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||
@@ -767,7 +908,7 @@ internal class ProgramAndVarsGen(
|
||||
dt.isFloatArray -> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
val floatFills = array.map {
|
||||
compTarget.machine.getFloatAsmBytes(it.number!!)
|
||||
compTarget.getFloatAsmBytes(it.number!!)
|
||||
}
|
||||
asmgen.out(varname)
|
||||
for (f in array.zip(floatFills))
|
||||
@@ -780,7 +921,7 @@ internal class ProgramAndVarsGen(
|
||||
private fun zeroFilledArray(numElts: Int): StArray {
|
||||
val values = mutableListOf<StArrayElement>()
|
||||
repeat(numElts) {
|
||||
values.add(StArrayElement(0.0, null, null))
|
||||
values.add(StArrayElement(0.0, null, null,null,null))
|
||||
}
|
||||
return values
|
||||
}
|
||||
@@ -810,7 +951,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
|
||||
private fun outputStringvar(varname: String, align: UInt, encoding: Encoding, value: String) {
|
||||
alignVar(align)
|
||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||
@@ -838,7 +979,7 @@ internal class ProgramAndVarsGen(
|
||||
val number = it.number!!.toInt()
|
||||
"$"+number.toString(16).padStart(2, '0')
|
||||
}
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
|
||||
if(it.number!=null) {
|
||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||
}
|
||||
@@ -850,8 +991,15 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
asmgen.asmSymbolName(addrOfSymbol)
|
||||
}
|
||||
else
|
||||
else if(it.structInstance!=null) {
|
||||
asmgen.asmSymbolName("${StStructInstanceBlockName}.${it.structInstance!!}")
|
||||
}
|
||||
else if(it.structInstanceUninitialized!=null) {
|
||||
asmgen.asmSymbolName("${StStructInstanceBlockName}_bss.${it.structInstanceUninitialized!!}")
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("weird array elt")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
@@ -875,7 +1023,7 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ 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.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
@@ -14,7 +11,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
private val errors: IErrorReporter
|
||||
) {
|
||||
|
||||
private val zeropage = options.compTarget.machine.zeropage
|
||||
private val zeropage = options.compTarget.zeropage
|
||||
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
|
||||
|
||||
@@ -23,7 +20,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
zeropageVars = zeropage.allocatedVariables
|
||||
}
|
||||
|
||||
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
|
||||
internal fun isZpVar(scopedName: String): Boolean {
|
||||
if(scopedName in zeropageVars)
|
||||
return true
|
||||
|
||||
val v = symboltable.lookup(scopedName)
|
||||
return if(v is StMemVar) v.address <= 255u else false
|
||||
}
|
||||
|
||||
internal fun getFloatAsmConst(number: Double): String {
|
||||
val asmName = globalFloatConsts[number]
|
||||
@@ -49,7 +52,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
|
||||
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
|
||||
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
|
||||
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
|
||||
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
|
||||
|
||||
var numVariablesAllocatedInZP = 0
|
||||
@@ -57,10 +60,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
|
||||
varsRequiringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
result.fold(
|
||||
@@ -68,7 +71,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
numVariablesAllocatedInZP++
|
||||
},
|
||||
failure = {
|
||||
errors.err(it.message!!, variable.astNode.position)
|
||||
errors.err(it.message!!, variable.astNode?.position ?: Position.DUMMY)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -76,10 +79,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
if(errors.noErrors()) {
|
||||
varsPreferringZp.forEach { variable ->
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||
@@ -89,17 +92,17 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
// try to allocate the "don't care" interger variables into the zeropage until it is full.
|
||||
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt.isIntegerOrBool) {
|
||||
if(variable.dt.isIntegerOrBool || variable.dt.isPointer) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
val result = zeropage.allocate(
|
||||
variable.scopedName,
|
||||
variable.scopedNameString,
|
||||
variable.dt,
|
||||
variable.length,
|
||||
variable.astNode.position,
|
||||
variable.length?.toInt(),
|
||||
variable.astNode?.position ?: Position.DUMMY,
|
||||
errors
|
||||
)
|
||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||
|
||||
@@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.ComparisonOperators
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
@@ -17,6 +20,8 @@ internal class AnyExprAsmGen(
|
||||
private val asmgen: AsmGen6502Internal
|
||||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
if(expr.operator==".")
|
||||
throw AssemblyError("pointer deref expression should have been handled elsewhere ${expr.position}")
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
|
||||
@@ -43,6 +48,12 @@ internal class AnyExprAsmGen(
|
||||
}
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
expr.type.isPointer -> {
|
||||
require((expr.left.type.isPointer || expr.left.type.isUnsignedWord) && (expr.right.type.isPointer || expr.right.type.isUnsignedWord)) {
|
||||
"both operands must be pointers or uwords"
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: pointer ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
else -> throw AssemblyError("weird expression type in assignment")
|
||||
}
|
||||
}
|
||||
@@ -50,17 +61,17 @@ internal class AnyExprAsmGen(
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
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.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
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
|
||||
@@ -73,25 +84,25 @@ internal class AnyExprAsmGen(
|
||||
"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.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
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.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
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.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -190,14 +201,14 @@ internal class AnyExprAsmGen(
|
||||
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")
|
||||
else -> TODO("don't know how to evaluate float expression for selected compilation target ${left.position}")
|
||||
}
|
||||
}
|
||||
|
||||
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.forDt(BaseDataType.FLOAT))
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -3,14 +3,15 @@ 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,
|
||||
VARIABLE, // non-pointer variable
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER
|
||||
REGISTER,
|
||||
POINTER,
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@@ -32,6 +33,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val array: PtArrayIndexer? = null,
|
||||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val pointer: PtPointerDeref? = null,
|
||||
val origAstTarget: PtAssignTarget? = null
|
||||
)
|
||||
{
|
||||
@@ -39,16 +41,36 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if(register!=null && !datatype.isNumericOrBool)
|
||||
throw AssemblyError("must be numeric type")
|
||||
if(kind==TargetStorageKind.REGISTER)
|
||||
require(register!=null)
|
||||
else
|
||||
require(register==null)
|
||||
if(kind==TargetStorageKind.POINTER)
|
||||
require(pointer!=null)
|
||||
if(pointer!=null)
|
||||
require(kind==TargetStorageKind.POINTER)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
|
||||
return targets.map {
|
||||
if(it.void)
|
||||
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
|
||||
else
|
||||
fromAstAssignment(it, definingSub, asmgen)
|
||||
}
|
||||
}
|
||||
|
||||
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
||||
with(target) {
|
||||
when {
|
||||
@@ -68,6 +90,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
}
|
||||
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)
|
||||
pointerDeref != null -> return AsmAssignTarget(TargetStorageKind.POINTER, asmgen, type, definingSub, target.position, pointer = pointerDeref, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
@@ -78,18 +101,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
|
||||
val dt = if(signed) DataType.BYTE else DataType.UBYTE
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.FAC1,
|
||||
RegisterOrPair.FAC2 -> {
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), scope, pos, register = registers)
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
@@ -107,7 +130,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
}
|
||||
@@ -125,14 +148,16 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
left is PtIdentifier && left.name==scopedName
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords && (left.pointerderef==null && array.pointerderef==null || left.pointerderef!! isSameAs array.pointerderef!!)
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
false
|
||||
TargetStorageKind.POINTER -> {
|
||||
TODO("is pointer deref target same as expression? ${this.position}")
|
||||
}
|
||||
TargetStorageKind.REGISTER -> false
|
||||
TargetStorageKind.VOID -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,8 +177,11 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.variable)
|
||||
else {
|
||||
if(array.variable==null)
|
||||
TODO("asmVarname for array with pointer")
|
||||
asmgen.asmVariableName(array.variable!!)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
|
||||
@@ -162,7 +190,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||
val bv = value as? PtBool
|
||||
if(bv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.forDt(BaseDataType.BOOL), boolean = bv)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
|
||||
|
||||
return when(value) {
|
||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||
@@ -183,7 +211,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
}
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value)
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
||||
@@ -194,9 +222,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
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
|
||||
val returnType =
|
||||
if(sub is PtSub && sub.signature.returns.size>1)
|
||||
DataType.UNDEFINED // TODO list of types instead?
|
||||
else
|
||||
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||
}
|
||||
else -> {
|
||||
@@ -226,27 +257,30 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
|
||||
|
||||
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
||||
val target: AsmAssignTarget,
|
||||
val targets: List<AsmAssignTarget>,
|
||||
val memsizer: IMemSizer,
|
||||
val position: Position) {
|
||||
init {
|
||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(!source.datatype.isUndefined) { "must not be placeholder/undefined datatype at $position" }
|
||||
if(!source.datatype.isArray && !target.datatype.isArray)
|
||||
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
|
||||
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
||||
}
|
||||
targets.forEach { target ->
|
||||
if (!source.datatype.isArray && !source.datatype.isUndefined && !target.datatype.isArray && !target.datatype.isUndefined)
|
||||
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
|
||||
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val target: AsmAssignTarget
|
||||
get() = targets.single()
|
||||
}
|
||||
|
||||
internal class AsmAssignment(source: AsmAssignSource,
|
||||
target: AsmAssignTarget,
|
||||
targets: List<AsmAssignTarget>,
|
||||
memsizer: IMemSizer,
|
||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
||||
position: Position): AsmAssignmentBase(source, targets, memsizer, position)
|
||||
|
||||
internal class AsmAugmentedAssignment(source: AsmAssignSource,
|
||||
val operator: String,
|
||||
target: AsmAssignTarget,
|
||||
memsizer: IMemSizer,
|
||||
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
||||
position: Position): AsmAssignmentBase(source, listOf(target), memsizer, position)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+297
-219
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -5,9 +5,11 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when(dt.sub?.dt) {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
@@ -21,8 +23,8 @@ internal object DummyMemsizer : IMemSizer {
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
override fun memorySize(dt: BaseDataType): Int {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +70,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -5,12 +5,15 @@ import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502
|
||||
import prog8.codegen.cpu6502.VariableAllocator
|
||||
import java.nio.file.Files
|
||||
import kotlin.io.path.Path
|
||||
|
||||
@@ -26,8 +29,9 @@ class TestCodegen: FunSpec({
|
||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
}
|
||||
@@ -48,13 +52,14 @@ class TestCodegen: FunSpec({
|
||||
val codegen = AsmGen6502(prefixSymbols = false, 0)
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"pi",
|
||||
DataType.forDt(BaseDataType.UBYTE),
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
PtNumber(BaseDataType.UBYTE, 0.0, Position.DUMMY),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
@@ -63,6 +68,7 @@ class TestCodegen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
@@ -72,29 +78,31 @@ class TestCodegen: FunSpec({
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
false,
|
||||
null,
|
||||
3u,
|
||||
Position.DUMMY
|
||||
))
|
||||
sub.add(PtVariable(
|
||||
"xx",
|
||||
DataType.forDt(BaseDataType.WORD),
|
||||
DataType.WORD,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
PtNumber(BaseDataType.WORD, 1.0, Position.DUMMY),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Position.DUMMY
|
||||
))
|
||||
|
||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
|
||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||
}
|
||||
it.add(targetIdx)
|
||||
}
|
||||
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
||||
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||
assign.add(target)
|
||||
@@ -103,15 +111,15 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
prefixAssign.add(prefixTarget)
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
sub.add(prefixAssign)
|
||||
|
||||
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
numberAssign.add(numberAssignTarget)
|
||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||
@@ -119,10 +127,10 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
cxregAssign.add(cxregAssignTarget)
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||
sub.add(cxregAssign)
|
||||
|
||||
block.add(sub)
|
||||
@@ -130,7 +138,7 @@ class TestCodegen: FunSpec({
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
val options = getTestOptions()
|
||||
@@ -159,5 +167,15 @@ class TestCodegen: FunSpec({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("memory mapped zp var is correctly considered to be zp var") {
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val st = SymbolTable(program)
|
||||
st.add(StMemVar("zpmemvar", DataType.WORD, 0x20u, null, null))
|
||||
st.add(StMemVar("normalmemvar", DataType.WORD, 0x9000u, null, null))
|
||||
val allocator = VariableAllocator(st, getTestOptions(), ErrorReporterForTests())
|
||||
allocator.isZpVar("zpmemvar") shouldBe true
|
||||
allocator.isZpVar("normalmemvar") shouldBe false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
implementation(project(":intermediate"))
|
||||
implementation(project(":codeGenIntermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -24,4 +23,4 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
||||
// note: there are no unit tests in this module!
|
||||
@@ -13,5 +13,6 @@
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.codegen.experimental
|
||||
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.code.core.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
@@ -21,11 +21,13 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
// but you can also use the Intermediate Representation to build a codegen on:
|
||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||
val irProgram = irCodeGen.generate()
|
||||
irProgram.verifyRegisterTypes(irCodeGen.registerTypes())
|
||||
|
||||
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||
IRFileWriter(irProgram, null).write()
|
||||
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
if(!options.quiet)
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
return EmptyProgram
|
||||
}
|
||||
}
|
||||
@@ -33,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
private object EmptyProgram : IAssemblyProgram {
|
||||
override val name = "<Empty Program>"
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
println("** nothing assembled **")
|
||||
if(!options.quiet)
|
||||
println("** nothing assembled **")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
@@ -7,15 +5,14 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
implementation(project(":intermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -45,4 +42,4 @@ tasks.test {
|
||||
testLogging {
|
||||
events("skipped", "failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,10 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,8 +1,12 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StMemorySlabBlockName
|
||||
import prog8.code.StStructInstanceBlockName
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@@ -26,9 +30,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"lsb" -> funcLsb(call)
|
||||
"memory" -> funcMemory(call)
|
||||
"peek" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekbool" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekw" -> funcPeek(call, IRDataType.WORD)
|
||||
"peekf" -> funcPeek(call, IRDataType.FLOAT)
|
||||
"poke" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebool" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebowl" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokew" -> funcPoke(call, IRDataType.WORD)
|
||||
"pokef" -> funcPoke(call, IRDataType.FLOAT)
|
||||
"pokemon" -> funcPokemon(call)
|
||||
@@ -42,11 +49,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"ror" -> funcRolRor(call)
|
||||
"rol2" -> funcRolRor(call)
|
||||
"ror2" -> funcRolRor(call)
|
||||
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse/ifExpression statement")
|
||||
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse/ifExpression statement")
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(call)
|
||||
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
|
||||
"offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
@@ -56,12 +64,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
|
||||
return if(resultType==IRDataType.FLOAT) {
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null)
|
||||
ExpressionCodeResult(result, resultType, -1, resultFpReg)
|
||||
}
|
||||
else {
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(resultType)
|
||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null)
|
||||
ExpressionCodeResult(result, resultType, resultReg, -1)
|
||||
}
|
||||
@@ -75,12 +83,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return if(call.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else
|
||||
ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
|
||||
ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.next(IRDataType.WORD), -1) // TODO actually the result is returned in CPU registers AY...
|
||||
}
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||
@@ -93,7 +100,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentA = exprGen.translateExpression(call.args[2])
|
||||
@@ -125,7 +131,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null)
|
||||
divisionReg = tr.resultReg
|
||||
remainderReg = codeGen.registers.nextFree()
|
||||
remainderReg = codeGen.registers.next(type)
|
||||
} else {
|
||||
val numTr = exprGen.translateExpression(number)
|
||||
addToResult(result, numTr, numTr.resultReg, -1)
|
||||
@@ -145,13 +151,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
addToResult(result, right, right.resultReg, -1)
|
||||
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -178,7 +184,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
when (sourceDt.base) {
|
||||
BaseDataType.BYTE -> {
|
||||
val notNegativeLabel = codeGen.createLabelName()
|
||||
val compareReg = codeGen.registers.nextFree()
|
||||
val compareReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||
@@ -189,7 +195,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
val notNegativeLabel = codeGen.createLabelName()
|
||||
val compareReg = codeGen.registers.nextFree()
|
||||
val compareReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||
@@ -199,7 +205,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||
}
|
||||
@@ -210,7 +216,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(tr.dt)
|
||||
|
||||
if(tr.dt==IRDataType.FLOAT) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
@@ -233,7 +239,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
when(dt.base) {
|
||||
BaseDataType.UBYTE -> {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
|
||||
}
|
||||
@@ -241,15 +247,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE) // sqrt of a word still produces just a byte result
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
|
||||
}
|
||||
@@ -261,7 +267,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
if((call.args[0] as? PtNumber)?.number == 0.0) {
|
||||
// msb is 0, use EXT
|
||||
val lsbTr = exprGen.translateExpression(call.args[1])
|
||||
@@ -279,7 +285,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val type = irType(call.type)
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||
@@ -406,7 +411,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
return if(dt==IRDataType.FLOAT) {
|
||||
if(call.args[0] is PtNumber) {
|
||||
val resultFpRegister = codeGen.registers.nextFreeFloat()
|
||||
val resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address)
|
||||
@@ -415,7 +420,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg)
|
||||
}
|
||||
@@ -423,7 +428,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
} else {
|
||||
if (call.args[0] is PtNumber) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val resultRegister = codeGen.registers.next(dt)
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address)
|
||||
@@ -432,7 +437,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(dt)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
}
|
||||
@@ -474,7 +479,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
|
||||
return if(address is PtNumber) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val resultRegister = codeGen.registers.next(IRDataType.BYTE)
|
||||
val addressNum = address.number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = addressNum)
|
||||
@@ -484,7 +489,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
} else {
|
||||
val addressTr = exprGen.translateExpression(address)
|
||||
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = addressTr.resultReg)
|
||||
}
|
||||
@@ -497,22 +502,34 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val name = (call.args[0] as PtString).value
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabBlockName.memory_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val labelname = SymbolTable.labelnameForStructInstance(call)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "${StStructInstanceBlockName}.$labelname")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
return exprGen.translateExpression(call.args.single())
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
// ....To be more strict, maybe we should introduce a new result register that is of type .b?
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
@@ -545,10 +562,27 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val arr = (arg as? PtArrayIndexer)
|
||||
val index = arr?.index?.asConstInteger()
|
||||
if(arr!=null && index!=null) {
|
||||
if(arr.splitWords) TODO("IR rol/ror on split words array")
|
||||
val variable = arr.variable.name
|
||||
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
|
||||
if(arr.variable==null)
|
||||
TODO("support for ptr indexing ${arr.position}")
|
||||
val variable = arr.variable!!.name
|
||||
if(arr.splitWords) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
when(opcodeMemAndReg.first) {
|
||||
Opcode.ROXRM, Opcode.RORM -> {
|
||||
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = index)
|
||||
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = index)
|
||||
}
|
||||
Opcode.ROXLM, Opcode.ROLM -> {
|
||||
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = index)
|
||||
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = index)
|
||||
}
|
||||
else -> throw AssemblyError("wrong rol/ror opcode")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
@@ -577,7 +611,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
is PtIdentifier -> {
|
||||
if(isConstZeroValue) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val pointerReg = codeGen.registers.nextFree()
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
||||
if (msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
||||
@@ -587,7 +621,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val pointerReg = codeGen.registers.nextFree()
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
||||
if (msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
||||
@@ -599,10 +633,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
||||
if(target.variable==null)
|
||||
TODO("support for ptr indexing ${target.position}")
|
||||
val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName)
|
||||
@@ -618,7 +654,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName)
|
||||
@@ -633,46 +669,48 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
}
|
||||
else {
|
||||
val targetVariable = target.variable ?: TODO("support for ptr indexing ${target.position}")
|
||||
|
||||
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
|
||||
val constIndex = target.index.asConstInteger()
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -25,7 +25,7 @@ class IRCodeGen(
|
||||
verifyNameScoping(program, symbolTable)
|
||||
changeGlobalVarInits(symbolTable)
|
||||
|
||||
val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable)
|
||||
val irSymbolTable = convertStToIRSt(symbolTable)
|
||||
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||
|
||||
// collect global variables initializers
|
||||
@@ -48,6 +48,7 @@ class IRCodeGen(
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
|
||||
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
@@ -55,6 +56,8 @@ class IRCodeGen(
|
||||
return irProg
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
private fun changeGlobalVarInits(symbolTable: SymbolTable) {
|
||||
// Normally, block level (global) variables that have a numeric initialization value
|
||||
// are initialized via an assignment statement.
|
||||
@@ -64,15 +67,16 @@ class IRCodeGen(
|
||||
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
||||
val block = variable.parent.astNode as PtBlock
|
||||
val initialization = (block.children.firstOrNull {
|
||||
it is PtAssignment && it.target.identifier?.name==variable.scopedName
|
||||
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
|
||||
} as PtAssignment?)
|
||||
val initValue = initialization?.value
|
||||
when(initValue){
|
||||
when(val initValue = initialization?.value){
|
||||
is PtBool -> {
|
||||
require(initValue.asInt()!=0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
is PtNumber -> {
|
||||
require(initValue.number!=0.0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.number)
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
@@ -101,6 +105,15 @@ class IRCodeGen(
|
||||
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
|
||||
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
is PtPointerDeref -> require('.' in node.startpointer.name) { "node $node name is not scoped: ${node.startpointer.name}" }
|
||||
is PtIdentifier -> {
|
||||
if('.' !in node.name) {
|
||||
// there is 1 case where the identifier is not scoped: if it's the value field name after a pointer array indexing.
|
||||
val expr = node.parent as? PtBinaryExpression
|
||||
if (expr?.operator != "." || expr.right !== node || expr.left !is PtArrayIndexer || (!expr.left.type.isPointer && !expr.left.type.isStructInstance))
|
||||
require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
|
||||
}
|
||||
}
|
||||
else -> { /* node has no name or is ok to have no dots in the name */ }
|
||||
}
|
||||
node.children.forEach { verifyPtNode(it) }
|
||||
@@ -194,10 +207,7 @@ class IRCodeGen(
|
||||
old.fpReg1,
|
||||
old.fpReg2,
|
||||
immediate = immediateValue,
|
||||
null,
|
||||
address = addressValue,
|
||||
null,
|
||||
null
|
||||
address = addressValue
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -231,7 +241,11 @@ class IRCodeGen(
|
||||
chunk += IRInstruction(Opcode.BREAKPOINT)
|
||||
listOf(chunk)
|
||||
}
|
||||
is PtAlign -> TODO("ir support for inline %align")
|
||||
is PtAlign -> {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.ALIGN, immediate = node.align.toInt())
|
||||
listOf(chunk)
|
||||
}
|
||||
is PtConditionalBranch -> translate(node)
|
||||
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, null))
|
||||
is PtIncludeBinary -> listOf(IRInlineBinaryChunk(null, readBinaryData(node), null))
|
||||
@@ -252,9 +266,11 @@ class IRCodeGen(
|
||||
is PtBool,
|
||||
is PtArray,
|
||||
is PtBlock,
|
||||
is PtDefer -> throw AssemblyError("should have been transformed")
|
||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtString -> throw AssemblyError("string should not occur as separate statement node $node")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened $node")
|
||||
is PtStructDecl -> emptyList()
|
||||
is PtSubSignature -> emptyList()
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
|
||||
@@ -280,31 +296,23 @@ class IRCodeGen(
|
||||
val address = goto.target.asConstInteger()
|
||||
val label = (goto.target as? PtIdentifier)?.name
|
||||
if(address!=null) {
|
||||
val branchIns = when(branch.condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, address = address)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, address = address)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, address = address)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, address = address)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, address = address)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, address = address)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
|
||||
}
|
||||
val branchIns = IRBranchInstr(branch.condition, address=address)
|
||||
addInstr(result, branchIns, null)
|
||||
} else if(label!=null && !isIndirectJump(goto)) {
|
||||
val branchIns = when(branch.condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, labelSymbol = label)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, labelSymbol = label)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, labelSymbol = label)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
|
||||
}
|
||||
val branchIns = IRBranchInstr(branch.condition, label = label)
|
||||
addInstr(result, branchIns, null)
|
||||
} else {
|
||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
||||
val skipJumpLabel = createLabelName()
|
||||
// note that the branch opcode used is the opposite as the branch condition, because it needs to skip the indirect jump
|
||||
val branchIns = IRInvertedBranchInstr(branch.condition, label = skipJumpLabel)
|
||||
// evaluate jump address expression into a register and jump indirectly to it
|
||||
addInstr(result, branchIns, null)
|
||||
val tr = expressionEval.translateExpression(goto.target)
|
||||
result += tr.chunks
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.JUMPI, reg1=tr.resultReg)
|
||||
}
|
||||
result += IRCodeChunk(skipJumpLabel, null)
|
||||
}
|
||||
if(branch.falseScope.children.isNotEmpty())
|
||||
result += translateNode(branch.falseScope)
|
||||
@@ -313,16 +321,7 @@ class IRCodeGen(
|
||||
|
||||
val elseLabel = createLabelName()
|
||||
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
|
||||
val branchIns = when(branch.condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVS, labelSymbol = elseLabel)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVC, labelSymbol = elseLabel)
|
||||
}
|
||||
val branchIns = IRInvertedBranchInstr(branch.condition, label = elseLabel)
|
||||
addInstr(result, branchIns, null)
|
||||
result += translateNode(branch.trueScope)
|
||||
if(branch.falseScope.children.isNotEmpty()) {
|
||||
@@ -337,6 +336,60 @@ class IRCodeGen(
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||
if(label!=null)
|
||||
return when(condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, labelSymbol = label)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, labelSymbol = label)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, labelSymbol = label)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
|
||||
}
|
||||
else if(address!=null) {
|
||||
return when(condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, address = address)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, address = address)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, address = address)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, address = address)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, address = address)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, address = address)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
|
||||
}
|
||||
}
|
||||
else throw AssemblyError("need label or address for branch")
|
||||
}
|
||||
|
||||
private fun IRInvertedBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
|
||||
if(label!=null)
|
||||
return when(condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = label)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = label)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVC, labelSymbol = label)
|
||||
}
|
||||
else if(address!=null) {
|
||||
return when(condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, address = address)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, address = address)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, address = address)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, address = address)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, address = address)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, address = address)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVS, address = address)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVC, address = address)
|
||||
}
|
||||
}
|
||||
else throw AssemblyError("need label or address for branch")
|
||||
}
|
||||
|
||||
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
|
||||
if(chunks.isEmpty()) {
|
||||
return listOf(
|
||||
@@ -379,35 +432,44 @@ class IRCodeGen(
|
||||
whenStmt.choices.children.forEach {
|
||||
val choice = it as PtWhenChoice
|
||||
if(choice.isElse) {
|
||||
require(choice.parent.children.last() === choice)
|
||||
result += translateNode(choice.statements)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
// is always the last node so can fall through
|
||||
} else {
|
||||
if(choice.statements.children.isEmpty()) {
|
||||
// no statements for this choice value, jump to the end immediately
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val choiceLabel = createLabelName()
|
||||
choices.add(choiceLabel to choice)
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
||||
val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
|
||||
val branchLabel: String
|
||||
if(onlyJumpLabel==null) {
|
||||
choices.add(choiceLabel to choice)
|
||||
branchLabel = choiceLabel
|
||||
} else {
|
||||
branchLabel = onlyJumpLabel
|
||||
}
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = branchLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
if(choices.isNotEmpty())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
|
||||
choices.forEach { (label, choice) ->
|
||||
result += labelFirstChunk(translateNode(choice.statements), label)
|
||||
val lastStatement = choice.statements.children.last()
|
||||
if(lastStatement !is PtReturn && lastStatement !is PtJump)
|
||||
if(!choice.isOnlyGotoOrReturn())
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
|
||||
}
|
||||
|
||||
@@ -427,11 +489,12 @@ class IRCodeGen(
|
||||
translateForInNonConstantRange(forLoop, loopvar)
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val elementDt = irType(iterable.type.elementType())
|
||||
val iterableLength = symbolTable.getLength(iterable.name)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val indexReg = registers.nextFree()
|
||||
val tmpReg = registers.nextFree()
|
||||
val indexReg = registers.next(IRDataType.BYTE)
|
||||
val tmpReg = registers.next(elementDt)
|
||||
val loopLabel = createLabelName()
|
||||
val endLabel = createLabelName()
|
||||
when {
|
||||
@@ -439,9 +502,9 @@ class IRCodeGen(
|
||||
// iterate over a zero-terminated string
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.LOADX, elementDt, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
val jumpChunk = IRCodeChunk(null, null)
|
||||
@@ -450,20 +513,19 @@ class IRCodeGen(
|
||||
result += jumpChunk
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
}
|
||||
iterable.type.isSplitWordArray -> {
|
||||
iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
|
||||
// iterate over lsb/msb split word array
|
||||
val elementDt = iterable.type.elementType()
|
||||
if(!elementDt.isWord)
|
||||
if(elementDt!=IRDataType.WORD)
|
||||
throw AssemblyError("weird dt")
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
val tmpRegLsb = registers.nextFree()
|
||||
val tmpRegMsb = registers.nextFree()
|
||||
val concatReg = registers.nextFree()
|
||||
val tmpRegLsb = registers.next(IRDataType.BYTE)
|
||||
val tmpRegMsb = registers.next(IRDataType.BYTE)
|
||||
val concatReg = registers.next(IRDataType.WORD)
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
|
||||
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.STOREM, elementDt, reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@@ -477,9 +539,8 @@ class IRCodeGen(
|
||||
}
|
||||
else -> {
|
||||
// iterate over regular array
|
||||
val element = iterable.type.sub!!
|
||||
val elementDt = element.dt
|
||||
val elementSize = program.memsizer.memorySize(element)
|
||||
val elementDt = iterable.type.sub!!
|
||||
val elementSize = program.memsizer.memorySize(elementDt)
|
||||
val lengthBytes = iterableLength!! * elementSize
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
@@ -487,7 +548,7 @@ class IRCodeGen(
|
||||
it += IRInstruction(Opcode.STOREM, irType(DataType.forDt(elementDt)), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
result += addConstIntToReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(lengthBytes!=256) {
|
||||
// for length 256, the compare is actually against 0, which doesn't require a separate CMP instruction
|
||||
@@ -508,7 +569,7 @@ class IRCodeGen(
|
||||
val step = iterable.step.number.toInt()
|
||||
if (step==0)
|
||||
throw AssemblyError("step 0")
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
@@ -594,9 +655,8 @@ class IRCodeGen(
|
||||
|
||||
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
|
||||
val loopLabel = createLabelName()
|
||||
require(forLoop.variable.name == loopvar.scopedName)
|
||||
require(forLoop.variable.name == loopvar.scopedNameString)
|
||||
val loopvarSymbol = forLoop.variable.name
|
||||
val indexReg = registers.nextFree()
|
||||
val loopvarDt = when(loopvar) {
|
||||
is StMemVar -> loopvar.dt
|
||||
is StStaticVariable -> loopvar.dt
|
||||
@@ -612,6 +672,7 @@ class IRCodeGen(
|
||||
val rangeEndExclusiveWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndExclusiveUntyped and 255 else rangeEndExclusiveUntyped and 65535
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
val indexReg = registers.next(loopvarDtIr)
|
||||
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = iterable.first)
|
||||
chunk += IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||
result += chunk
|
||||
@@ -640,7 +701,7 @@ class IRCodeGen(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun addConstReg(dt: IRDataType, reg: Int, value: Int): IRCodeChunk {
|
||||
private fun addConstIntToReg(dt: IRDataType, reg: Int, value: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
when(value) {
|
||||
0 -> { /* do nothing */ }
|
||||
@@ -704,7 +765,7 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val valueReg = registers.nextFree()
|
||||
val valueReg = registers.next(dt)
|
||||
if(value>0) {
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, immediate = value)
|
||||
code += if(knownAddress!=null)
|
||||
@@ -731,7 +792,7 @@ class IRCodeGen(
|
||||
code += if(factor==0.0) {
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -746,41 +807,43 @@ class IRCodeGen(
|
||||
else
|
||||
IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, labelSymbol = symbol)
|
||||
} else {
|
||||
val factorReg = registers.nextFreeFloat()
|
||||
val factorReg = registers.next(IRDataType.FLOAT)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConst(dt: IRDataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConst(dt: DataType, reg: Int, factor: Int): IRCodeChunk {
|
||||
val irdt = irType(dt)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
val pow2 = powersOfTwoInt.indexOf(factor)
|
||||
if(pow2==1) {
|
||||
// just shift 1 bit
|
||||
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||
val pow2reg = registers.next(IRDataType.BYTE)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.LSLN, irdt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
|
||||
IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, dt, reg1=reg, immediate = factor)
|
||||
val opcode = if(dt.isSigned) Opcode.MULS else Opcode.MUL
|
||||
IRInstruction(opcode, irdt, reg1=reg, immediate = factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, signed: Boolean, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
@@ -794,7 +857,7 @@ class IRCodeGen(
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.nextFree()
|
||||
val pow2reg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, address = knownAddress)
|
||||
@@ -808,12 +871,13 @@ class IRCodeGen(
|
||||
IRInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val factorReg = registers.nextFree()
|
||||
val factorReg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -836,14 +900,14 @@ class IRCodeGen(
|
||||
if(factor==1.0)
|
||||
return code
|
||||
if(factor==0.0) {
|
||||
val maxvalueReg = registers.nextFreeFloat()
|
||||
val maxvalueReg = registers.next(IRDataType.FLOAT)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = maxvalueReg, immediateFp = Double.MAX_VALUE)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
|
||||
} else {
|
||||
val factorReg = registers.nextFreeFloat()
|
||||
val factorReg = registers.next(IRDataType.FLOAT)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
@@ -865,8 +929,8 @@ class IRCodeGen(
|
||||
code += IRInstruction(Opcode.ASR, dt, reg1=reg)
|
||||
} else {
|
||||
// just shift multiple bits (signed)
|
||||
val pow2reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
val pow2reg = registers.next(IRDataType.BYTE)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
|
||||
}
|
||||
} else {
|
||||
@@ -875,8 +939,8 @@ class IRCodeGen(
|
||||
code += IRInstruction(Opcode.LSR, dt, reg1=reg)
|
||||
} else {
|
||||
// just shift multiple bits (unsigned)
|
||||
val pow2reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1 = pow2reg, immediate = pow2)
|
||||
val pow2reg = registers.next(IRDataType.BYTE)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.LSRN, dt, reg1 = reg, reg2 = pow2reg)
|
||||
}
|
||||
}
|
||||
@@ -911,7 +975,7 @@ class IRCodeGen(
|
||||
IRInstruction(Opcode.ASRM, dt, labelSymbol = symbol)
|
||||
} else {
|
||||
// just shift multiple bits (signed)
|
||||
val pow2reg = registers.nextFree()
|
||||
val pow2reg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1 = pow2reg, immediate = pow2)
|
||||
code += if (knownAddress != null)
|
||||
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, address = knownAddress)
|
||||
@@ -928,7 +992,7 @@ class IRCodeGen(
|
||||
}
|
||||
else {
|
||||
// just shift multiple bits (unsigned)
|
||||
val pow2reg = registers.nextFree()
|
||||
val pow2reg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, address = knownAddress)
|
||||
@@ -942,7 +1006,7 @@ class IRCodeGen(
|
||||
{
|
||||
// regular div
|
||||
if (factor == 0) {
|
||||
val reg = registers.nextFree()
|
||||
val reg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0xffff)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, address = knownAddress)
|
||||
@@ -950,7 +1014,7 @@ class IRCodeGen(
|
||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val factorReg = registers.nextFree()
|
||||
val factorReg = registers.next(dt)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||
code += if(signed) {
|
||||
if(knownAddress!=null)
|
||||
@@ -987,6 +1051,7 @@ class IRCodeGen(
|
||||
ifWithOnlyNormalJump_IntegerCond(ifElse, goto)
|
||||
}
|
||||
|
||||
// floating-point condition only from here!
|
||||
// we assume only a binary expression can contain a floating point.
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
@@ -995,7 +1060,7 @@ class IRCodeGen(
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
var afterIfLabel = ""
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val compResultReg = registers.nextFree()
|
||||
val compResultReg = registers.next(IRDataType.BYTE)
|
||||
it += IRInstruction(
|
||||
Opcode.FCOMP,
|
||||
IRDataType.FLOAT,
|
||||
@@ -1029,7 +1094,7 @@ class IRCodeGen(
|
||||
}
|
||||
// evaluate jump address expression into a register and jump indirectly to it
|
||||
val tr = expressionEval.translateExpression(goto.target)
|
||||
for(i in tr.chunks.flatMap { it.instructions }) {
|
||||
for(i in tr.chunks.flatMap { c -> c.instructions }) {
|
||||
it += i
|
||||
}
|
||||
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
||||
@@ -1057,7 +1122,7 @@ class IRCodeGen(
|
||||
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
|
||||
else
|
||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
||||
throw AssemblyError("non-indirect jump shouldn't have an expression as target")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1076,7 +1141,7 @@ class IRCodeGen(
|
||||
if(identifier!=null && !isIndirectJump(goto))
|
||||
IRInstruction(branchOpcode, labelSymbol = identifier.name)
|
||||
else
|
||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
||||
TODO("JUMP to expression address ${goto.target}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,7 +1190,6 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val firstReg: Int
|
||||
@@ -1255,7 +1319,7 @@ class IRCodeGen(
|
||||
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
|
||||
else
|
||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
||||
throw AssemblyError("non-indirect jump shouldn't have an expression as target")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1314,7 +1378,7 @@ class IRCodeGen(
|
||||
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
|
||||
else
|
||||
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
|
||||
throw AssemblyError("non-indirect jump shouldn't have an expression as target")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1349,7 +1413,7 @@ class IRCodeGen(
|
||||
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null || !condition.left.type.isFloat) {
|
||||
return ifWithElse_IntegerCond(ifElse)
|
||||
return ifElse_IntegerCond(ifElse)
|
||||
}
|
||||
|
||||
// we assume only a binary expression can contain a floating point.
|
||||
@@ -1358,7 +1422,7 @@ class IRCodeGen(
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
val compResultReg = registers.nextFree()
|
||||
val compResultReg = registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1 = compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
val elseBranch: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
@@ -1409,14 +1473,10 @@ class IRCodeGen(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
|
||||
private fun ifElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
|
||||
|
||||
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_"))
|
||||
throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND")
|
||||
|
||||
val tr = expressionEval.translateExpression(condition)
|
||||
if(addCmpiZero)
|
||||
tr.chunks.last().instructions.add(IRInstruction(Opcode.CMPI, tr.dt, reg1 = tr.resultReg, immediate = 0))
|
||||
@@ -1441,6 +1501,41 @@ class IRCodeGen(
|
||||
if(condition.operator in LogicalOperators)
|
||||
return translateSimple(condition, Opcode.BSTEQ, false)
|
||||
|
||||
val useBIT = expressionEval.checkIfConditionCanUseBIT(condition)
|
||||
if(useBIT!=null) {
|
||||
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||
val (testBitSet, variable, bitmask) = useBIT
|
||||
addInstr(result, IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = variable.name), null)
|
||||
val bitBranchOpcode = when(testBitSet) {
|
||||
true -> when(bitmask) {
|
||||
64 -> Opcode.BSTVC
|
||||
128 -> Opcode.BSTPOS
|
||||
else -> throw AssemblyError("need bit 6 or 7")
|
||||
}
|
||||
false -> when(bitmask) {
|
||||
64 -> Opcode.BSTVS
|
||||
128 -> Opcode.BSTNEG
|
||||
else -> throw AssemblyError("need bit 6 or 7")
|
||||
}
|
||||
}
|
||||
|
||||
if(ifElse.hasElse()) {
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val signed = condition.left.type.isSigned
|
||||
val elseBranchFirstReg: Int
|
||||
val elseBranchSecondReg: Int
|
||||
@@ -1582,7 +1677,7 @@ class IRCodeGen(
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtTypeCast -> {
|
||||
require(cond.type.isBool && cond.value.type.isNumeric)
|
||||
require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
|
||||
@@ -1598,6 +1693,9 @@ class IRCodeGen(
|
||||
is PtBinaryExpression -> {
|
||||
translateBinExpr(cond)
|
||||
}
|
||||
is PtPointerDeref -> {
|
||||
translateSimple(cond, Opcode.BSTEQ, false)
|
||||
}
|
||||
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
|
||||
}
|
||||
return result
|
||||
@@ -1619,7 +1717,7 @@ class IRCodeGen(
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(constRepeats==65536) {
|
||||
// make use of the word wrap around to count to 65536
|
||||
val resultRegister = registers.nextFree()
|
||||
val resultRegister = registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, immediate = 0), null)
|
||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@@ -1672,9 +1770,7 @@ class IRCodeGen(
|
||||
private fun isIndirectJump(jump: PtJump): Boolean {
|
||||
if(jump.target.asConstInteger()!=null)
|
||||
return false
|
||||
val identifier = jump.target as? PtIdentifier
|
||||
if(identifier==null)
|
||||
return true
|
||||
val identifier = jump.target as? PtIdentifier ?: return true
|
||||
val symbol = symbolTable.lookup(identifier.name)
|
||||
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
|
||||
}
|
||||
@@ -1687,10 +1783,46 @@ class IRCodeGen(
|
||||
|
||||
private fun translate(ret: PtReturn): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val value = ret.value
|
||||
if(ret.children.size>1) {
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
// a floating point value is passed via FAC (just one fp value is possible)
|
||||
|
||||
val returnRegs = ret.definingISub()!!.returnsWhatWhere()
|
||||
val values = ret.children.zip(returnRegs)
|
||||
// first all but the first return values
|
||||
for ((value, register) in values.drop(1)) {
|
||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||
if(register.second.isFloat) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||
}
|
||||
else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
// finally do the first of the return values (this avoids clobbering of its value in AY)
|
||||
values.first().also { (value, register) ->
|
||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||
if(register.second.isFloat) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||
}
|
||||
else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||
return result
|
||||
}
|
||||
|
||||
val value = ret.children.singleOrNull()
|
||||
if(value==null) {
|
||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||
} else {
|
||||
value as PtExpression
|
||||
if(value.type.isFloat) {
|
||||
if(value is PtNumber) {
|
||||
addInstr(result, IRInstruction(Opcode.RETURNI, IRDataType.FLOAT, immediateFp = value.number), null)
|
||||
@@ -1729,7 +1861,7 @@ class IRCodeGen(
|
||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
||||
is PtAlign -> TODO("ir support for inline %align")
|
||||
is PtSub -> {
|
||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
|
||||
val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
|
||||
for (subchild in child.children) {
|
||||
translateNode(subchild).forEach { sub += it }
|
||||
}
|
||||
@@ -1770,21 +1902,30 @@ class IRCodeGen(
|
||||
is PtLabel -> {
|
||||
irBlock += IRCodeChunk(child.name, null)
|
||||
}
|
||||
is PtJmpTable -> {
|
||||
irBlock += IRCodeChunk(null, null).also {
|
||||
for(addr in child.children) {
|
||||
addr as PtIdentifier
|
||||
it += IRInstruction(Opcode.JUMP, labelSymbol = addr.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
return irBlock
|
||||
}
|
||||
|
||||
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
|
||||
private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
|
||||
val result = mutableListOf<IRSubroutine.IRParam>()
|
||||
parameters.forEach {
|
||||
it as PtSubroutineParameter
|
||||
if(it.register==null) {
|
||||
val flattenedName = it.definingISub()!!.name + "." + it.name
|
||||
if (symbolTable.lookup(flattenedName) == null)
|
||||
TODO("fix missing lookup for: $flattenedName parameter")
|
||||
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
|
||||
result += IRSubroutine.IRParam(flattenedName, orig.dt)
|
||||
require('.' in it.name) { "even parameter names should have been made fully scoped by now" }
|
||||
val orig = symbolTable.lookup(it.name) as? StStaticVariable
|
||||
?: TODO("fix missing lookup for: ${it.name} parameter")
|
||||
result += IRSubroutine.IRParam(it.name, orig.dt)
|
||||
} else {
|
||||
val reg = it.register
|
||||
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
|
||||
@@ -1799,7 +1940,7 @@ class IRCodeGen(
|
||||
private var labelSequenceNumber = 0
|
||||
internal fun createLabelName(): String {
|
||||
labelSequenceNumber++
|
||||
return "${PtLabel.GENERATED_LABEL_PREFIX}$labelSequenceNumber"
|
||||
return "${GENERATED_LABEL_PREFIX}$labelSequenceNumber"
|
||||
}
|
||||
|
||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
||||
@@ -1809,7 +1950,7 @@ class IRCodeGen(
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
|
||||
|
||||
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
internal fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
return IRCodeChunk(label, null).also {
|
||||
val args = params.map { (dt, reg)->
|
||||
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
|
||||
@@ -1819,4 +1960,101 @@ class IRCodeGen(
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number, fcallArgs = FunctionCallArgs(args, returnSpec))
|
||||
}
|
||||
}
|
||||
|
||||
internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
when(registerOrFlag.registerOrPair) {
|
||||
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
|
||||
RegisterOrPair.X -> chunk += IRInstruction(Opcode.STOREHX, IRDataType.BYTE, reg1=resultReg)
|
||||
RegisterOrPair.Y -> chunk += IRInstruction(Opcode.STOREHY, IRDataType.BYTE, reg1=resultReg)
|
||||
RegisterOrPair.AX -> chunk += IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=resultReg)
|
||||
RegisterOrPair.AY -> chunk += IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=resultReg)
|
||||
RegisterOrPair.XY -> chunk += IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=resultReg)
|
||||
RegisterOrPair.FAC1 -> chunk += IRInstruction(Opcode.STOREHFACZERO, IRDataType.FLOAT, fpReg1 = resultFpReg)
|
||||
RegisterOrPair.FAC2 -> chunk += IRInstruction(Opcode.STOREHFACONE, IRDataType.FLOAT, fpReg1 = resultFpReg)
|
||||
in Cx16VirtualRegisters -> {
|
||||
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}")
|
||||
}
|
||||
null -> when(registerOrFlag.statusflag) {
|
||||
// TODO: do the statusflag argument as last
|
||||
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
||||
else -> throw AssemblyError("unsupported statusflag as param")
|
||||
}
|
||||
else -> throw AssemblyError("unsupported register arg")
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UByte> {
|
||||
// calculates the pointer address and returns the register it's in + remaining offset into the struct (so that LOADFIELD/STOREFIELD instructions can be used)
|
||||
val pointerTr = expressionEval.translateExpression(deref.startpointer)
|
||||
result += pointerTr.chunks
|
||||
val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
|
||||
result += instructions
|
||||
return pointerTr.resultReg to offset
|
||||
}
|
||||
|
||||
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UByte, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
|
||||
if(offset<=0u) {
|
||||
val irdt = irType(type)
|
||||
val instr = if(type.isFloat) {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
|
||||
} else {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
|
||||
}
|
||||
addInstr(result, instr, null)
|
||||
return
|
||||
}
|
||||
|
||||
// store with field offset
|
||||
var valueRegister = existingValueRegister
|
||||
val irdt = irType(type)
|
||||
if(valueIsZero && valueRegister<0) {
|
||||
if(type.isFloat) {
|
||||
valueRegister = registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
|
||||
} else {
|
||||
valueRegister = registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
|
||||
}
|
||||
}
|
||||
val instr = if (type.isFloat)
|
||||
IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
|
||||
addInstr(result, instr, null)
|
||||
}
|
||||
|
||||
internal fun loadIndexReg(index: PtExpression, itemsize: Int, wordIndex: Boolean, arrayIsSplitWords: Boolean): Pair<IRCodeChunks, Int> {
|
||||
// returns the code to load the Index into the register, which is also returned.
|
||||
|
||||
require(index !is PtNumber) { "index should not be a constant number here, calling code should handle that in a more efficient way" }
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(wordIndex) {
|
||||
val tr = expressionEval.translateExpression(index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
var indexReg = tr.resultReg
|
||||
if(tr.dt==IRDataType.BYTE) {
|
||||
indexReg = registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=tr.resultReg), null)
|
||||
}
|
||||
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
|
||||
return Pair(result, indexReg)
|
||||
}
|
||||
|
||||
// regular byte size index value.
|
||||
val byteIndexTr = expressionEval.translateExpression(index)
|
||||
addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1)
|
||||
|
||||
if(itemsize==1 || arrayIsSplitWords)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
|
||||
result += multiplyByConst(DataType.UBYTE, byteIndexTr.resultReg, itemsize)
|
||||
return Pair(result, byteIndexTr.resultReg)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
|
||||
private fun optimizeOnlyJoinChunks() {
|
||||
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
|
||||
irprog.foreachSub { sub ->
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
@@ -52,6 +53,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
||||
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||
|| simplifyConstantReturns(chunk1, indexedInstructions)
|
||||
|| removeNeedlessLoads(chunk1, indexedInstructions)
|
||||
} while (changed)
|
||||
}
|
||||
}
|
||||
@@ -84,7 +87,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
/*
|
||||
Empty Code chunk with label ->
|
||||
If next chunk has no label -> move label to next chunk, remove original
|
||||
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
|
||||
If next chunk has label -> label name should be the same, in which case remove original, otherwise leave everything untouched.
|
||||
If is last chunk -> keep chunk in place because of the label.
|
||||
Empty Code chunk without label ->
|
||||
should not have been generated! ERROR.
|
||||
@@ -111,14 +114,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(index>0) {
|
||||
if (chunk.label == nextchunk.label)
|
||||
removeChunks += index
|
||||
else {
|
||||
removeChunks += index
|
||||
replaceLabels[chunk.label!!] = nextchunk.label!!
|
||||
replaceLabels.entries.forEach { (key, value) ->
|
||||
if (value == chunk.label)
|
||||
replaceLabels[key] = nextchunk.label!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,6 +194,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
|
||||
else
|
||||
candidate
|
||||
} else {
|
||||
chunks += candidate
|
||||
}
|
||||
}
|
||||
is IRInlineBinaryChunk -> {
|
||||
@@ -210,6 +207,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
IRInlineBinaryChunk(label, candidate.data, candidate.next)
|
||||
else
|
||||
candidate
|
||||
} else {
|
||||
chunks += candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,31 +350,31 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a SNZ etc. whose target register is not used can be removed altogether
|
||||
if(ins.opcode in OpcodesThatSetRegFromStatusbits) {
|
||||
val usages = regUsages(ins.reg1!!)
|
||||
if(usages.toList().sumOf { it.second } <= 1) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun regUsages(register: Int): Map<IRCodeChunkBase, Int> {
|
||||
val chunks = mutableMapOf<IRCodeChunkBase, Int>()
|
||||
irprog.foreachSub { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
val used = chunk.usedRegisters()
|
||||
val numUsages = used.readRegs.getOrDefault(register, 0) + used.writeRegs.getOrDefault(register, 0)
|
||||
if(numUsages>0) {
|
||||
chunks[chunk] = numUsages
|
||||
private fun removeNeedlessLoads(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
/*
|
||||
load.b r2,#2
|
||||
loadr.b r1,r2
|
||||
jump p8_label_gen_2
|
||||
*/
|
||||
var changed=false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(idx>=2 && ins.opcode in OpcodesThatJump) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
val previous2 = indexedInstructions[idx-2].value
|
||||
if(previous.opcode==Opcode.LOADR && previous2.opcode in OpcodesThatLoad) {
|
||||
if(previous.reg2==previous2.reg1) {
|
||||
chunk.instructions[idx-2] = previous2.copy(reg1=previous.reg1)
|
||||
chunk.instructions.removeAt(idx-1)
|
||||
changed=true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunks
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
@@ -383,7 +382,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
when (ins.opcode) {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MULS, Opcode.MOD -> {
|
||||
if (ins.immediate == 1) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
@@ -403,15 +402,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
}
|
||||
Opcode.AND -> {
|
||||
if (ins.immediate == 0) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||
changed = true
|
||||
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
when (ins.immediate) {
|
||||
0 -> {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||
changed = true
|
||||
}
|
||||
255 if ins.type == IRDataType.BYTE -> {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
65535 if ins.type == IRDataType.WORD -> {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
Opcode.OR -> {
|
||||
@@ -477,4 +480,23 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun simplifyConstantReturns(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// use a RETURNI when a RETURNR is just returning a constant that was loaded into a register just before
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(ins.opcode==Opcode.RETURNR) {
|
||||
if(idx>0) {
|
||||
val insBefore = chunk.instructions[idx-1]
|
||||
if(insBefore.opcode == Opcode.LOAD && insBefore.immediate!=null) {
|
||||
val constvalue = insBefore.immediate!!
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.RETURNI, ins.type, immediate = constvalue)
|
||||
chunk.instructions.removeAt(idx-1)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ class IRUnusedCodeRemover(
|
||||
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
|
||||
val prefix = "$blockLabel."
|
||||
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
|
||||
|
||||
// check if there are symbols referenced elsewhere that we should not prune (even though the rest of the block is empty)
|
||||
|
||||
blockVars.forEach { stVar ->
|
||||
irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
|
||||
chunk.instructions.forEach { ins ->
|
||||
@@ -47,7 +50,50 @@ class IRUnusedCodeRemover(
|
||||
}
|
||||
}
|
||||
|
||||
val blockStructs = irprog.st.allStructDefs().filter { it.name.startsWith(prefix) }
|
||||
blockStructs.forEach { struct ->
|
||||
irprog.st.allStructInstances().forEach { instance ->
|
||||
if(instance.structName == struct.name)
|
||||
return // a struct instance is declared using this struct type
|
||||
}
|
||||
irprog.st.allVariables().forEach { variable ->
|
||||
if(variable.dt.isPointer || variable.dt.isStructInstance)
|
||||
if(struct.name == variable.dt.subType!!.scopedNameString)
|
||||
return // a variable exists with the struct as (pointer) type
|
||||
}
|
||||
}
|
||||
|
||||
irprog.st.removeTree(blockLabel)
|
||||
removeBlockInits(irprog, blockLabel)
|
||||
}
|
||||
|
||||
private fun removeBlockInits(code: IRProgram, blockLabel: String) {
|
||||
val instructions = code.globalInits.instructions
|
||||
instructions.toTypedArray().forEach {ins ->
|
||||
if(ins.labelSymbol?.startsWith(blockLabel)==true) {
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
|
||||
// remove stray loads
|
||||
instructions.toTypedArray().forEach { ins ->
|
||||
if(ins.opcode in arrayOf(Opcode.LOAD, Opcode.LOADR, Opcode.LOADM)) {
|
||||
if(ins.reg1!=0) {
|
||||
if(instructions.count { it.reg1==ins.reg1 || it.reg2==ins.reg1 } <2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
else if(ins.fpReg1!=0) {
|
||||
if (instructions.count { it.fpReg1 == ins.fpReg1 || it.fpReg2 == ins.fpReg1 } < 2) {
|
||||
if(ins.labelSymbol!=null)
|
||||
code.st.removeIfExists(ins.labelSymbol!!)
|
||||
instructions.remove(ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeUnusedSubroutines(): Int {
|
||||
@@ -173,7 +219,7 @@ class IRUnusedCodeRemover(
|
||||
if(chunk!=null)
|
||||
new+=chunk
|
||||
else
|
||||
allLabeledChunks[label]?.let { new += it }
|
||||
allLabeledChunks[label]?.let { c -> new += c }
|
||||
}
|
||||
else
|
||||
new += instr.branchTarget!!
|
||||
@@ -226,7 +272,7 @@ class IRUnusedCodeRemover(
|
||||
chunk.instructions.forEach {
|
||||
if(it.labelSymbol!=null) {
|
||||
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
||||
allLabeledChunks[chunkName]?.let { c -> linkedChunks += c }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.intermediate.IRDataType
|
||||
|
||||
internal class RegisterPool {
|
||||
// reserve 0,1,2 for return values of subroutine calls and syscalls in IR assembly code
|
||||
private var firstFree: Int=3
|
||||
private var firstFreeFloat: Int=3
|
||||
// everything from 99000 onwards is reserved for special purposes:
|
||||
// 99000 - 99099 : WORD registers for syscall arguments and response value(s)
|
||||
// 99100 - 99199 : BYTE registers for syscall arguments and response value(s)
|
||||
|
||||
fun peekNext() = firstFree
|
||||
fun peekNextFloat() = firstFreeFloat
|
||||
private var nextRegister: Int=1
|
||||
private val registerTypes: MutableMap<Int, IRDataType> = mutableMapOf()
|
||||
|
||||
fun nextFree(): Int {
|
||||
val result = firstFree
|
||||
firstFree++
|
||||
return result
|
||||
fun getTypes(): Map<Int, IRDataType> = registerTypes
|
||||
|
||||
init {
|
||||
for(i in 99000..99099)
|
||||
registerTypes[i] = IRDataType.WORD
|
||||
for(i in 99100..99199)
|
||||
registerTypes[i] = IRDataType.BYTE
|
||||
}
|
||||
|
||||
fun nextFreeFloat(): Int {
|
||||
val result = firstFreeFloat
|
||||
firstFreeFloat++
|
||||
fun next(type: IRDataType): Int {
|
||||
if(nextRegister>=99000)
|
||||
throw AssemblyError("register pool depleted")
|
||||
val result = nextRegister
|
||||
nextRegister++
|
||||
registerTypes[result] = type
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
val st = IRSymbolTable()
|
||||
if (sourceSt != null) {
|
||||
sourceSt.flat.forEach {
|
||||
when(it.value.type) {
|
||||
StNodeType.STATICVAR -> st.add(convert(it.value as StStaticVariable))
|
||||
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
|
||||
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
|
||||
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
|
||||
StNodeType.STRUCTINSTANCE -> {
|
||||
val instance = it.value as StStructInstance
|
||||
val struct = sourceSt.lookup(instance.structName) as StStruct
|
||||
st.add(convert(instance, struct.fields))
|
||||
}
|
||||
StNodeType.STRUCT -> st.add(convert(it.value as StStruct))
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
st.validate()
|
||||
|
||||
st.allVariables().forEach { variable ->
|
||||
variable.onetimeInitializationArrayValue?.let {
|
||||
it.forEach { arrayElt ->
|
||||
val addrOfSymbol = arrayElt.addressOfSymbol
|
||||
if (addrOfSymbol != null) {
|
||||
require(addrOfSymbol.contains('.')) {
|
||||
"pointer var in array should be properly scoped: $addrOfSymbol in ${variable.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
|
||||
private fun convert(struct: StStruct): IRStStructDef =
|
||||
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
|
||||
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement {
|
||||
return if (elt.boolean != null)
|
||||
IRStArrayElement(elt.boolean, null, null)
|
||||
else if(elt.number!=null)
|
||||
IRStArrayElement(null, elt.number, null)
|
||||
else {
|
||||
val symbol = elt.addressOfSymbol ?: (StStructInstanceBlockName + "." + (elt.structInstance ?: elt.structInstanceUninitialized))
|
||||
IRStArrayElement(null, null, symbol)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
|
||||
if('.' in variable.name) {
|
||||
return IRStStaticVariable(variable.name,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
variable.initializationStringValue,
|
||||
variable.initializationArrayValue?.map { convertArrayElt(it) },
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align,
|
||||
variable.dirty)
|
||||
} else {
|
||||
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
|
||||
if(array==null)
|
||||
return null
|
||||
val newArray = mutableListOf<IRStArrayElement>()
|
||||
array.forEach {
|
||||
if(it.addressOfSymbol!=null) {
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?:
|
||||
throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
val scopedName = variable.scopedNameString
|
||||
return IRStStaticVariable(scopedName,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
variable.initializationStringValue,
|
||||
fixupAddressOfInArray(variable.initializationArrayValue),
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align,
|
||||
variable.dirty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StMemVar): IRStMemVar {
|
||||
if('.' in variable.name) {
|
||||
return IRStMemVar(
|
||||
variable.name,
|
||||
variable.dt,
|
||||
variable.address,
|
||||
variable.length
|
||||
)
|
||||
} else {
|
||||
val scopedName = try {
|
||||
variable.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
variable.name
|
||||
}
|
||||
return IRStMemVar(scopedName, variable.dt, variable.address, variable.length)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(constant: StConstant): IRStConstant {
|
||||
val dt = DataType.forDt(constant.dt)
|
||||
val scopedName = if('.' in constant.name) {
|
||||
constant.name
|
||||
} else {
|
||||
try {
|
||||
constant.scopedNameString
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
constant.name
|
||||
}
|
||||
}
|
||||
return IRStConstant(scopedName, dt, constant.value)
|
||||
}
|
||||
|
||||
|
||||
private fun convert(mem: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in mem.name)
|
||||
IRStMemorySlab(mem.name, mem.size, mem.align)
|
||||
else
|
||||
IRStMemorySlab("$StMemorySlabBlockName.${mem.name}", mem.size, mem.align)
|
||||
}
|
||||
|
||||
|
||||
private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType, String>>): IRStStructInstance {
|
||||
val values = fields.zip(instance.initialValues).map { (field, value) ->
|
||||
val elt = convertArrayElt(value)
|
||||
IRStructInitValue(field.first.base, elt)
|
||||
}
|
||||
return if('.' in instance.name)
|
||||
IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
else
|
||||
IRStStructInstance("${StStructInstanceBlockName}.${instance.name}", instance.structName, values, instance.size)
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package prog8.codegen.vm
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.code.core.ICodeGeneratorBackend
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
@@ -19,12 +19,18 @@ class VmCodeGen: ICodeGeneratorBackend {
|
||||
): IAssemblyProgram {
|
||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||
val irProgram = irCodeGen.generate()
|
||||
|
||||
irProgram.verifyRegisterTypes(irCodeGen.registerTypes())
|
||||
|
||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
|
||||
internal class VmAssemblyProgram(
|
||||
override val name: String,
|
||||
internal val irProgram: IRProgram
|
||||
): IAssemblyProgram {
|
||||
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
// the VM reads the IR file from disk.
|
||||
|
||||
@@ -3,9 +3,11 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub?.dt) {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
@@ -19,8 +21,8 @@ internal object DummyMemsizer : IMemSizer {
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: SubType): Int {
|
||||
return memorySize(DataType.forDt(dt.dt), null)
|
||||
override fun memorySize(dt: BaseDataType): Int {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -9,7 +9,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||
require(chunks.first().label=="main.start")
|
||||
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
|
||||
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
||||
val sub = IRSubroutine("main.start", emptyList(), emptyList(), Position.DUMMY)
|
||||
chunks.forEach { sub += it }
|
||||
block += sub
|
||||
val target = VMTarget()
|
||||
@@ -21,8 +21,9 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
CompilationOptions.AllZeropageAllowed,
|
||||
floats = false,
|
||||
noSysInit = true,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
val prog = IRProgram("test", IRSymbolTable(), options, target)
|
||||
@@ -52,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().single().instructions.size shouldBe 1
|
||||
}
|
||||
|
||||
test("remove jmp to label below") {
|
||||
test("remove jmp to label below but keep labels") {
|
||||
val c1 = IRCodeChunk("main.start", null)
|
||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
||||
val c2 = IRCodeChunk("label", null)
|
||||
@@ -68,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().size shouldBe 3
|
||||
irProg.chunks()[0].label shouldBe "main.start"
|
||||
irProg.chunks()[1].label shouldBe "label2"
|
||||
irProg.chunks()[2].label shouldBe "label3"
|
||||
irProg.chunks()[0].isEmpty() shouldBe true
|
||||
irProg.chunks()[1].isEmpty() shouldBe false
|
||||
irProg.chunks()[2].isEmpty() shouldBe true
|
||||
val chunks = irProg.chunks()
|
||||
chunks.size shouldBe 4
|
||||
chunks[0].label shouldBe "main.start"
|
||||
chunks[1].label shouldBe "label"
|
||||
chunks[2].label shouldBe "label2"
|
||||
chunks[3].label shouldBe "label3"
|
||||
chunks[0].isEmpty() shouldBe true
|
||||
chunks[1].isEmpty() shouldBe true
|
||||
chunks[2].isEmpty() shouldBe false
|
||||
chunks[3].isEmpty() shouldBe true
|
||||
val instr = irProg.chunks().flatMap { it.instructions }
|
||||
instr.size shouldBe 2
|
||||
instr[0].opcode shouldBe Opcode.JUMP
|
||||
|
||||
Loaded 100 of 407 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user