mirror of
https://github.com/irmen/prog8.git
synced 2025-06-14 11:23:37 +00:00
Compare commits
1761 Commits
Author | SHA1 | Date | |
---|---|---|---|
5fd83f2757 | |||
c80df4140b | |||
53e1729e2f | |||
ab2d1122a9 | |||
5190594c8a | |||
c858ceeb58 | |||
f0f52b9166 | |||
00c6f74481 | |||
2177ba0ed2 | |||
3483515346 | |||
75a06d2a40 | |||
53ac11983b | |||
69f4a4d4f8 | |||
222bcb808f | |||
686483f51a | |||
8df3da11e3 | |||
84dafda0e4 | |||
b909facfe5 | |||
7780d94de1 | |||
f2c440e466 | |||
4937e004b5 | |||
4cb383dccb | |||
c8a4b6f23c | |||
857724c7e6 | |||
a9b0400d13 | |||
2d1e5bbc7e | |||
60627ce756 | |||
7961a09d16 | |||
613efcacc7 | |||
7e8db16e18 | |||
1fbbed7e23 | |||
984272beb4 | |||
b9ce94bb68 | |||
f4c4ee78d9 | |||
793596614e | |||
136280100c | |||
29f1e4d2c9 | |||
72a7e61fd0 | |||
381cfca67f | |||
f40620aa25 | |||
57a9fed42b | |||
18d820da94 | |||
26e66f046f | |||
4270c04856 | |||
74456d1135 | |||
62dc824bc0 | |||
1605791f1b | |||
37a46aa2cf | |||
1d2d217b94 | |||
23961f695d | |||
730b208617 | |||
f09c04eeac | |||
be73739c62 | |||
eea3fb48a8 | |||
b4fa72c058 | |||
b0a865b0f1 | |||
7f49731618 | |||
3410aea788 | |||
bc0a133bb1 | |||
7e287a5359 | |||
1110bd0851 | |||
1b576f826d | |||
fe17566370 | |||
e3c00669c1 | |||
33d17afc32 | |||
2388359a99 | |||
2df0c9503c | |||
61fa3bc77c | |||
03ac9b6956 | |||
dfbef8495d | |||
7b17c49d8f | |||
4b3f31c2ee | |||
9ccc65bf8f | |||
f9e22add03 | |||
846951cda7 | |||
97836e18b2 | |||
7b69df4db2 | |||
3767b4bbe7 | |||
d7d2eefa4f | |||
6737f28d1e | |||
3da9404c2d | |||
4d5bd0fa32 | |||
1137da37c3 | |||
495a18805c | |||
a226b82d0b | |||
0b5ddcdc9b | |||
82da8f4946 | |||
5ff481ce3c | |||
f21dcaa6fb | |||
2c940de598 | |||
ce75b776bb | |||
7d22b9b9f9 | |||
6cb8b3b5cd | |||
2bf4017f2b | |||
08d2f8568b | |||
ac5f45d2d4 | |||
3cc7ad7d20 | |||
d4513364fb | |||
9684f4e42a | |||
f4186981fd | |||
141689e697 | |||
743c8b44a2 | |||
5e1459564a | |||
69a8813a3d | |||
17175df835 | |||
6b32535cb6 | |||
7f8fe75ab2 | |||
2815a14bb5 | |||
f4dfa60790 | |||
35e88dd529 | |||
4d5094a517 | |||
dd5abae721 | |||
8f2fb20934 | |||
44143f481a | |||
440abf4998 | |||
3c10427e04 | |||
74555a32ed | |||
85956b5828 | |||
41e40cad03 | |||
df2d5c6585 | |||
1a111b706e | |||
f696fce187 | |||
82d3d81bb2 | |||
4668932bac | |||
e6c41eac93 | |||
f0cff661df | |||
82d20dea39 | |||
804bb06859 | |||
5afa7e53f8 | |||
7f15b7b716 | |||
552e0c2248 | |||
e5b9e1f5e7 | |||
502bf90007 | |||
40bf117497 | |||
4011dce31b | |||
9e881e32f8 | |||
14aad2358f | |||
637a8899c5 | |||
cf0e395921 | |||
6ef438ce50 | |||
46e4b977a4 | |||
9626c5dead | |||
aea364e43d | |||
06defd0cb0 | |||
0f80897c50 | |||
57bb1c2c0d | |||
7b35b414e8 | |||
761aac7a23 | |||
15a02d7664 | |||
16ed68c1ec | |||
e63cf660c6 | |||
aaff484306 | |||
3281d9a215 | |||
0fcd61e00f | |||
c4523ea470 | |||
0447b3e4cc | |||
4d27c2901b | |||
855e18b31c | |||
d790878af6 | |||
85b244df2f | |||
6070afa6b6 | |||
975594703d | |||
6b8c3ef614 | |||
9b22f05381 | |||
ca3a990f9e | |||
557f4f689f | |||
66574d058a | |||
1c7c67060d | |||
9827ee97ad | |||
71a9a84211 | |||
367a2a4cee | |||
4f7465ba44 | |||
f891fc698c | |||
36bec62c9a | |||
dd5a2c8315 | |||
56bff46481 | |||
b83a0adb19 | |||
92ffefe656 | |||
51b2e41879 | |||
ef43bc9208 | |||
33733a4001 | |||
e5a1b37981 | |||
30aa72dc8e | |||
2c2d474059 | |||
c55ac0450f | |||
2d26b9c994 | |||
f38fe092ee | |||
7a33eb163b | |||
5db0408b9f | |||
3557d38ce0 | |||
7de4e9e66a | |||
73838ccb8b | |||
0509de76d5 | |||
f4b3d19059 | |||
f37fb82d53 | |||
dbe98f3fa5 | |||
371d4768e6 | |||
562d8386ec | |||
1625e4eb85 | |||
2365a076ac | |||
9898791771 | |||
a1c658274d | |||
be9998b48b | |||
e8f308f654 | |||
07132a2c42 | |||
9c4582e283 | |||
0204002d9b | |||
b3107cfad0 | |||
91ae68c07e | |||
06b3bf27b5 | |||
fbef63e150 | |||
bb8ee9bb3e | |||
25677a4126 | |||
3aeca0a770 | |||
4bd4733e52 | |||
9acec4d952 | |||
8388adcd1d | |||
5988ba76b5 | |||
1a06e7a16e | |||
7241cef7a5 | |||
5145296486 | |||
2cbf2d2226 | |||
754664aefa | |||
af99173cd7 | |||
fd1f30f92b | |||
d9ab2f8b90 | |||
bd6c60cf8a | |||
f0c150d93b | |||
c2986eaf47 | |||
ef0c4797bb | |||
ac02a99934 | |||
fb67d1155f | |||
eb46852bb9 | |||
007d8d2811 | |||
ebe04fc114 | |||
d7dd7f70c0 | |||
f2cb89a128 | |||
b8fade23de | |||
3b97a17648 | |||
0d06e3ff22 | |||
c914f7bbcf | |||
1b451180c1 | |||
ed061b362b | |||
e1026584c8 | |||
4c615e4fac | |||
7c9d48833b | |||
b60b195aec | |||
db76c8d7f4 | |||
de92740e87 | |||
522bf91c30 | |||
48d3abc1fe | |||
3f6f25e06f | |||
34ba07ee3b | |||
ac37319d20 | |||
b2c6274f74 | |||
402884b5ce | |||
23c99002c0 | |||
ee115b3337 | |||
82f5a141ed | |||
0567168ea9 | |||
c80a15846d | |||
5e194536a8 | |||
43c5ab8ecc | |||
cd295228ef | |||
6c42221620 | |||
0d73a7cd07 | |||
39d5b7edb0 | |||
6fa50a699f | |||
ddaef3e5d5 | |||
c3e9d4a9f8 | |||
7530fb67c8 | |||
19bb56df47 | |||
b0073ac933 | |||
137a89da15 | |||
44da7a302f | |||
4096aae8d4 | |||
d3e026d82a | |||
fa5ecd6495 | |||
af209ad50e | |||
7b89228fa7 | |||
d31a88206c | |||
cd4ed8765b | |||
b6f780d70d | |||
b071a58ca7 | |||
ce554f7718 | |||
99b1cec2e1 | |||
46911a8905 | |||
4eb61529f6 | |||
81abf29bec | |||
85897ef8cd | |||
b824c0b125 | |||
6367c6d116 | |||
a7736d88a9 | |||
049dbf5a78 | |||
4ac92caeb5 | |||
7af3da2a97 | |||
95a62fcdd1 | |||
7872d20554 | |||
a598eb7e98 | |||
c786acc39b | |||
07d8052a57 | |||
db9edb477e | |||
9bd3a6758a | |||
2cb1560bbd | |||
006059438f | |||
84ea3b9788 | |||
b667abde10 | |||
aa10997171 | |||
7880ac1909 | |||
f53848b4b9 | |||
82f2f38680 | |||
dcc2549574 | |||
cfe4753913 | |||
fcb1a7e4d4 | |||
ce76a7dfa5 | |||
7c1de81861 | |||
eddad20acc | |||
7daad57862 | |||
1468049fe9 | |||
3b91e59a79 | |||
3496a30528 | |||
32bad5df15 | |||
3f58eca1be | |||
2350843d1d | |||
a2588a178c | |||
e5292696c4 | |||
34b2a65ccb | |||
3aa3659bc7 | |||
b8117394c0 | |||
fd2bbd2b59 | |||
127c470746 | |||
f2844bdf1a | |||
c5bfef4264 | |||
73863acc12 | |||
19e99204b9 | |||
13f5b94c3e | |||
3a2498401d | |||
53b20ba625 | |||
e7f6f0950f | |||
9fbe1b38a5 | |||
078485134d | |||
67b1766e32 | |||
d4b69ac79c | |||
e61a2d7083 | |||
c03f6604af | |||
572bb38ddb | |||
42c5c0cb9f | |||
e145d2255e | |||
442fa07dd4 | |||
31ae9e1243 | |||
d7f83f8df2 | |||
29e2d4e0c8 | |||
2732d2c844 | |||
c4a037b277 | |||
0e614ad6fc | |||
ca1a8cd617 | |||
ba96a637be | |||
c2cac772e3 | |||
6b7216f4ec | |||
e4fb5946dd | |||
ca61248861 | |||
68d7b4649e | |||
0416aacbbd | |||
bc731e6f8e | |||
ae5d7705bb | |||
b9bd541532 | |||
83639c2535 | |||
25d80f4df1 | |||
74f918d911 | |||
a20efa56eb | |||
f4d83075be | |||
254592c383 | |||
ee23ac0537 | |||
a48cf0bb24 | |||
dae59238cd | |||
8736da1a21 | |||
09a1de69e7 | |||
63d67bc6cb | |||
7099245204 | |||
4d097d2139 | |||
6485bf9ad9 | |||
b7c5b1bfc7 | |||
2b7546e827 | |||
3549ccf4b3 | |||
e2f5752d9a | |||
1a59019fc8 | |||
7bac7bdc3e | |||
19fe58dbac | |||
0a5b30e21c | |||
664818fd29 | |||
d5214e2505 | |||
d906fcea0e | |||
29c8e8b740 | |||
71fec4c555 | |||
5ee36c897d | |||
4aba0c7405 | |||
ed7479c854 | |||
8d3d5f726a | |||
a9a7068818 | |||
1bde7c7718 | |||
17068130bb | |||
81a91d62cb | |||
2575263438 | |||
7f0e25cb50 | |||
a1e4e9c50f | |||
98eff2701b | |||
8b84f87217 | |||
306a1b7bc2 | |||
481214c46e | |||
a5961cbeab | |||
3bf335e0a0 | |||
68f696d165 | |||
1170aed026 | |||
bf1b2066b6 | |||
4c080afb76 | |||
ee1c43ca91 | |||
1c2e6f9e4c | |||
dd379430d9 | |||
42033ebd35 | |||
a086d6e009 | |||
c70bbdab26 | |||
3d956ef554 | |||
329f491c30 | |||
e93701f50e | |||
e680de05ea | |||
56fec674c5 | |||
54d92a027a | |||
319ac3a641 | |||
0a03c46351 | |||
ae1b62e147 | |||
8d567f6b06 | |||
b1ef09675b | |||
2b7b925090 | |||
e0454e95db | |||
91e421d961 | |||
c853afe769 | |||
1a64cb38d5 | |||
ccebd22856 | |||
a1f3b82333 | |||
3dda29781e | |||
a9d297ee31 | |||
e5ff61f201 | |||
d116eb7655 | |||
bc726c6334 | |||
123473dfc8 | |||
d9eccd4fba | |||
5b890847e5 | |||
64c85b9617 | |||
3e3b0bcd8b | |||
4c1eb1b12a | |||
530d03d284 | |||
619fa9b65e | |||
0032235933 | |||
61d1f1ea87 | |||
238d27acdc | |||
2f62271453 | |||
75d5117a2d | |||
b4700af2f5 | |||
374e2b311d | |||
49036abbaf | |||
38ccbac97c | |||
6b4896b8f5 | |||
d582d1cc42 | |||
9e2b8a2aa9 | |||
6fdc733941 | |||
422b390c48 | |||
67a9d1285c | |||
8e26e38ecc | |||
02e12d8575 | |||
fe2954ce08 | |||
1fe4439395 | |||
2ff04d2abd | |||
3f30d3aa89 | |||
129e17b33a | |||
bf2d8c3f4b | |||
b29f04ce01 | |||
d185ebad48 | |||
605df7c91c | |||
ec60cad8bb | |||
6aa0f5a392 | |||
4cae2c56ec | |||
d840975054 | |||
1b14da6c03 | |||
292640b17a | |||
112a7b09f2 | |||
863ec9ce8a | |||
2eb346a205 | |||
8092355acb | |||
e7ef2ed31b | |||
af4de6d2fc | |||
69f73dd779 | |||
9706b46012 | |||
6d75dd3bb8 | |||
bd295ffc99 | |||
07ce3e3c9d | |||
cbc3e37a89 | |||
3626828ceb | |||
24b77fb5a5 | |||
1505fe686a | |||
0991131fa8 | |||
2e928bd3c2 | |||
ca868ae19e | |||
3e286dd14c | |||
11247d52b1 | |||
1dbc902513 | |||
330e691b78 | |||
6780d4f562 | |||
b30b8b7368 | |||
3df182b8c3 | |||
7f21d89fea | |||
2b267b4ba1 | |||
ef64881528 | |||
9a6bd760bd | |||
00b9766aea | |||
6381d2b6ac | |||
d2ab5f230d | |||
824b41d457 | |||
b5523c7077 | |||
eb3594b18c | |||
852d85d010 | |||
5e0aef04fe | |||
a00c693f93 | |||
c943da1448 | |||
b630fae580 | |||
38e40084f1 | |||
bf23ad78e6 | |||
ded1d19737 | |||
496a3b0d2c | |||
6922333755 | |||
a00c39e9cf | |||
1c1da8e38e | |||
50a306f492 | |||
6995ee2d17 | |||
6c60ea9cac | |||
2431ed811a | |||
6bd205c02a | |||
62ec77e148 | |||
9120e1de88 | |||
60e169bd87 | |||
e4bca5fe47 | |||
a1729b65ab | |||
2950d26c8e | |||
4f8d4a9585 | |||
d787795759 | |||
cf74e73e27 | |||
2770254fd9 | |||
de04bd8cfa | |||
076a547f91 | |||
dffd0a2706 | |||
6c66f86103 | |||
26502c949a | |||
8dfe510883 | |||
96ba9f5902 | |||
3a6ba0ab71 | |||
32d894d6b6 | |||
543efa4299 | |||
eba0708099 | |||
51e6bf0d45 | |||
07b5c44a54 | |||
9fe32c1c34 | |||
0e0278c84a | |||
dea775a9cd | |||
7e3e18a5c7 | |||
8e3ebc84f0 | |||
e6079dfd71 | |||
2b435fe6a5 | |||
4e640b11fd | |||
8b1e1e68fa | |||
fd11927708 | |||
cd500fee8c | |||
1bd32c0f19 | |||
7aefca3de0 | |||
f275ed96ea | |||
d14dac3872 | |||
b0213b0565 | |||
c677f0a875 | |||
6e65cb2c0a | |||
e65c5402d7 | |||
334f86480a | |||
0e62f5b759 | |||
edf9a500d3 | |||
001d01fdaf | |||
a95677564e | |||
4aca8bb8df | |||
5540482888 | |||
00d735249b | |||
b5289511ba | |||
b6ded8501f | |||
781915d2cf | |||
f4cef3eaf2 | |||
d23c2eed86 | |||
15695a304e | |||
6319269976 | |||
0ed3d951a7 | |||
2aa39757b4 | |||
39d32a3600 | |||
219d17de34 | |||
9bb5b454e4 | |||
2412f8c531 | |||
8701d684e6 | |||
b543cc34cd | |||
791dbbab9b | |||
ac0b1da3fc | |||
2f97aedc3c | |||
ab544ee965 | |||
fa527f8624 | |||
92ee0aefee | |||
99759ae853 | |||
81930312ff | |||
194fbcdd91 | |||
1e3930aae2 | |||
62dda4d891 | |||
2b870fb9f7 | |||
53f0318187 | |||
5e6e711f33 | |||
78af2cd4dc | |||
02cb237623 | |||
cc0f19653e | |||
4fff150c7b | |||
f6136891cc | |||
1e22170302 | |||
bdda6f502a | |||
1bbd77fddb | |||
321fdd10d1 | |||
9867dfcdeb | |||
7c09ac632c | |||
3502f65332 | |||
628390c3b5 | |||
bc37097df2 | |||
7d98275763 | |||
d6ffb549f6 | |||
bcd0db984d | |||
d9244f22c2 | |||
c97d76dbf2 | |||
9e05e97d7f | |||
1070dedd7c | |||
ccd1516637 | |||
f1f51a01c6 | |||
be75b8dbe5 | |||
02fae0e722 | |||
e35b739579 | |||
34aa6cc8a2 | |||
d7a6b20028 | |||
eb2d5bb1f8 | |||
cefef3d1be | |||
cc96ab7a9b | |||
49ea31c0a4 | |||
f1478d776b | |||
40e4cfb686 | |||
76f459ee95 | |||
c478718019 | |||
c27248a58b | |||
51bc539468 | |||
2395863e7e | |||
69c459c8ac | |||
c8855b2b10 | |||
a910c0fddb | |||
fd55611cac | |||
52f6be2bb0 | |||
857f930dc2 | |||
dd2c436dc6 | |||
9f047ba752 | |||
9d4ec4a9b2 | |||
cdc6d9aa65 | |||
997bc21feb | |||
975af4764d | |||
bf69219f98 | |||
f34f9329f1 | |||
90271d0dcd | |||
195cd7597d | |||
4a81406262 | |||
f9fd426843 | |||
e612056ecd | |||
6f0103398b | |||
afb60db382 | |||
5731b876ff | |||
055f917a2e | |||
4ed7fb771c | |||
c328e9018c | |||
b270f6f713 | |||
5c13918f11 | |||
40cc216557 | |||
1481f92cb0 | |||
76d54fbe5c | |||
9f72779cdc | |||
3dcef89a74 | |||
46373717b6 | |||
7277c08fa6 | |||
04e75455c4 | |||
8ac17ae14e | |||
cb5d6ddf80 | |||
e0794db33a | |||
b128b79132 | |||
79e6d4b8dd | |||
b9ddde0f12 | |||
a0ec37b35b | |||
506ac8014c | |||
72b4198301 | |||
24eee0cb34 | |||
9fc0c3f849 | |||
db314ed903 | |||
1ef9b8be61 | |||
79782ad547 | |||
4b6d045df1 | |||
b4d1d545a8 | |||
f61682cdc7 | |||
d61420f1c6 | |||
3d09d605e1 | |||
025dde264a | |||
87cee7a0fd | |||
61784a03bb | |||
9d9ca0f08d | |||
58f37513e7 | |||
ee7f9d457d | |||
bec2224c3d | |||
4305984168 | |||
07dd64958f | |||
76101d7f8d | |||
7d6a0ab256 | |||
4309a0dc68 | |||
dde6919446 | |||
54fc9c91ac | |||
41658c97a3 | |||
45c9cc97d9 | |||
6fa7debee5 | |||
ee9f662016 | |||
3550e1214c | |||
8dcb43ad1c | |||
e6a1442296 | |||
cb65480c6c | |||
3e7c7ab497 | |||
f0930d8a18 | |||
5a846bdeb5 | |||
baf9dfb46c | |||
edd3a22848 | |||
583428b19c | |||
08d44ae553 | |||
b3b2541c1e | |||
8e927e0b73 | |||
8e3e996f4a | |||
b6fa361bcc | |||
ca83092aed | |||
3cda92331e | |||
c989abe265 | |||
89230ade7a | |||
b4931c9a1f | |||
ddfcf45d40 | |||
ee12236d53 | |||
df6698c98f | |||
c3b82f2cfa | |||
64c89f1c8f | |||
e09b65ea94 | |||
c81952c356 | |||
f80e462d25 | |||
51f32677b7 | |||
4b366358c4 | |||
3378586098 | |||
6777d952c1 | |||
6c8b18ddbd | |||
69780ecde9 | |||
9e2c52e1ec | |||
6cb0e6a936 | |||
dd82e550d5 | |||
cdcda27d07 | |||
ffffcdd50a | |||
d37d62574c | |||
f2380457d6 | |||
efa42d5d96 | |||
e17c18b653 | |||
7607d3d64a | |||
d7d7147d43 | |||
b40e1eabb9 | |||
3b8e18004c | |||
4c03950c28 | |||
170a0183f8 | |||
c62ff16f8b | |||
ab495fe6e1 | |||
c2a8dc23d0 | |||
6734ae3c88 | |||
4c1c595f14 | |||
9002c67639 | |||
b91aabd3c0 | |||
3307f673f6 | |||
07b00bec61 | |||
e0d2b60d8b | |||
45bfecee73 | |||
80e3a11268 | |||
38a6c6a866 | |||
8f224afed9 | |||
48a4c46a6c | |||
7d08380c7f | |||
b3b3cf3807 | |||
f0f6150e18 | |||
dc600cc3ed | |||
ae648b8a0a | |||
583af3bd4f | |||
d65cfbf093 | |||
118aed2e31 | |||
85abf4d123 | |||
44b8291540 | |||
d6444bba66 | |||
5a2f8fdfe1 | |||
bba4f84503 | |||
684e081399 | |||
96c700ee46 | |||
5f15794c3b | |||
a40b3134f4 | |||
c70b4daf87 | |||
928611eb20 | |||
f1d55c688a | |||
d22df22f7d | |||
061e1be0a4 | |||
950bc4b937 | |||
dcb81e6bea | |||
daaa83ee7d | |||
b7c1450121 | |||
787f52d1f8 | |||
50213f146a | |||
7f2aea60c9 | |||
168621f7c2 | |||
8b630798d8 | |||
52e8a44517 | |||
59f33658ad | |||
e0315bffdc | |||
cd28d0c0e0 | |||
0baa2c8b23 | |||
4977d1fbd5 | |||
3b7a92f1b4 | |||
f6920172dd | |||
93bfc8f5f4 | |||
39b7655264 | |||
8b75ceb412 | |||
c39fc4010d | |||
8df778a515 | |||
5134ea76bf | |||
3ba37df29d | |||
e221d674d9 | |||
251f947293 | |||
41e1e1cbb0 | |||
da1bc351d2 | |||
43c0afdea0 | |||
add5bfa2ec | |||
34babfb5de | |||
4f6c45c86c | |||
e6220a464c | |||
8dcd49934a | |||
bedc3bdb56 | |||
83ceb0fde9 | |||
1d299c56e0 | |||
0d735c2ccc | |||
4094f89d4a | |||
cf1e8b194a | |||
74e5644f55 | |||
b5dc5fc615 | |||
7a7270d769 | |||
7549ddcd2b | |||
08f0303178 | |||
0d7a291b81 | |||
2265ae9600 | |||
cba502e87a | |||
ac94236614 | |||
ddf1be2a13 | |||
b7694686c2 | |||
63332c0530 | |||
8a504f8eee | |||
106fc5daa4 | |||
7accb73993 | |||
e9aa6a0956 | |||
df20467e03 | |||
ecbd9d739e | |||
8af17c295a | |||
329b28cad1 | |||
452c29574d | |||
5bedc1b333 | |||
0bf6d2f72c | |||
c09b8af491 | |||
260bcd3a55 | |||
6b5211ad12 | |||
a92ec14989 | |||
b3348eb22b | |||
bec5a261e5 | |||
4b53641e1d | |||
00071d53d5 | |||
6902834568 | |||
fa2d87f3dd | |||
44019d1a61 | |||
6f74fb49bd | |||
a303b39cf0 | |||
3e63a29c59 | |||
261c0fc9b6 | |||
895b30f7e5 | |||
b985604e22 | |||
f7953e4ef3 | |||
63483d1f0e | |||
8b981f03bf | |||
d0d0910bf2 | |||
57ac820767 | |||
b8bda867b6 | |||
05d3a2450c | |||
d40788adfa | |||
83fbf86b1c | |||
e876008427 | |||
2b43353eb4 | |||
a74403c347 | |||
2f4c6c8697 | |||
238d8197f5 | |||
53a600d87b | |||
2a0ffaf45d | |||
936b046ed9 | |||
378dcfe351 | |||
0a330b9288 | |||
a88b40d6c1 | |||
09f25ffbd9 | |||
ab1232d742 | |||
a7f56fe0fc | |||
58a9452c36 | |||
6d8c4f403f | |||
88b80fed90 | |||
acdbd0c391 | |||
d9a8cfed8c | |||
122796fbba | |||
510ca042c9 | |||
125f6205f2 | |||
8136f3df5c | |||
38d06a7e94 | |||
49db10539a | |||
8efe4c6267 | |||
04d8bb8fbf | |||
08aa13c90c | |||
d1febc0208 | |||
5980e58ac6 | |||
e1dc283d4b | |||
8be234973c | |||
7def8ff2cd | |||
340b1c2e42 | |||
7e0f7ba438 | |||
fefd9b52a8 | |||
afd155ac4f | |||
ee724eb4f1 | |||
2f1f20ea11 | |||
063bcf17d8 | |||
72509eef44 | |||
2da28864e9 | |||
4278f64682 | |||
59ae3c3fcd | |||
7fa21fbdff | |||
e95af7498e | |||
79c75adac1 | |||
d212f69d89 | |||
edf5e69d39 | |||
574eb0d174 | |||
8bd4914e2f | |||
5ebaaff64b | |||
5c9e0c9f51 | |||
8132edbb08 | |||
d29ce78c86 | |||
94bc9d7a69 | |||
e8faec0932 | |||
69ca4fe304 | |||
cd99fe46fd | |||
4825b4dc68 | |||
8d0607ef58 | |||
225295a7d8 | |||
4cd74daf53 | |||
6eb9118197 | |||
d0bd2f522c | |||
661c757236 | |||
aaa20093ef | |||
1eecdd6fa3 | |||
800b5b2a43 | |||
9d17421c66 | |||
0edd50e956 | |||
288d4f08b3 | |||
526e4b8bdc | |||
e0c5ccc16b | |||
ebc2c614d7 | |||
29f5a85158 | |||
8af2380a47 | |||
431f2a2088 | |||
e05ea887f6 | |||
95c0425151 | |||
47cbc7b1f9 | |||
e7b75d591c | |||
99f7d469f4 | |||
8a6ef17fbf | |||
5f337a0bd9 | |||
87862f772a | |||
3ab641aa21 | |||
3efa8da8e0 | |||
3e28ed4fe4 | |||
44949460ed | |||
83cc19ad6f | |||
66bb98c479 | |||
ff3f985658 | |||
2ba6c9ccbe | |||
3eaf111e7d | |||
30da26b9a9 | |||
e35ad0cc8f | |||
1a36302cf1 | |||
82a28bb555 | |||
c1ce0be451 | |||
c0a5f8fef0 | |||
702cf304d0 | |||
4dee8b6048 | |||
ec665e0cc1 | |||
aec3b82476 | |||
e83796b5b9 | |||
8eb69d6eda | |||
74b5124a42 | |||
b9706a180b | |||
8aeb8a9bb7 | |||
8f2e166a22 | |||
fdd91170dc | |||
c40ddb061b | |||
353d6cfc55 | |||
f37564c49c | |||
157484d94b | |||
7626c9fff7 | |||
1f55f9fc49 | |||
2554bc7ef8 | |||
7cb4100419 | |||
2d3b7eb878 | |||
4d01a78731 | |||
a03e36828a | |||
260fb65b06 | |||
9fb8526136 | |||
26fc5ff5e2 | |||
5060f0bb19 | |||
beaf6d449b | |||
4d68b508a2 | |||
cd825e386d | |||
095c8b2309 | |||
8b6eb74c58 | |||
aba437e5a2 | |||
efe3ed499b | |||
5595564a1f | |||
439761cb67 | |||
bee6c65293 | |||
10145b946b | |||
ebf4b50059 | |||
07cce3b3fc | |||
f2c19afd95 | |||
d159e70e1c | |||
ac693a2541 | |||
1e988116ce | |||
ec9e722927 | |||
4cd5e8c378 | |||
b759d5e06a | |||
1469033c1e | |||
c15fd75df7 | |||
73524e01a6 | |||
9e54e11113 | |||
01ac5f29db | |||
67a2241e32 | |||
72b6dc3de7 | |||
6f5b645995 | |||
458ad1de57 | |||
216f48b7c1 | |||
b2d1757e5a | |||
6e53eb9d5c | |||
e5ee5be9c5 | |||
bd237b2b95 | |||
d31cf766eb | |||
56d530ff04 | |||
0bbb2240f2 | |||
1c8e4dba73 | |||
4a9956c4a4 | |||
59c0e6ae32 | |||
94c30fc21e | |||
8bb3b3be20 | |||
85e3c2c5a2 | |||
4be381c597 | |||
6ff5470cf1 | |||
151dcfdef9 | |||
c282b4cb9f | |||
c426f4626c | |||
0e3c92626e | |||
5099525e24 | |||
e22b4cbb67 | |||
2b48828179 | |||
3e181362dd | |||
71fd98e39e | |||
71cd8b6d51 | |||
ad75fcbf7e | |||
f8b04a6357 | |||
d8fcbb78d3 | |||
8408bf3789 | |||
3e1185658e | |||
d778cdcd61 | |||
90b303fc03 | |||
eb86b1270d | |||
a1f0cc878b | |||
f2e2720b15 | |||
ec8cfe1591 | |||
22eac159e5 | |||
956b0c3fa7 | |||
a6427e0949 | |||
22031f39b0 | |||
c4673d3a67 | |||
e83e021541 | |||
c1f2ecd413 | |||
46fbe01df9 | |||
8647a8290e | |||
bac51f4b31 | |||
582aab180a | |||
5fb714fcb2 | |||
3994de77d0 | |||
24c8d1f1f4 | |||
110f877dcc | |||
9cd3a9f8e8 | |||
1464050bf5 | |||
95e9e1b550 | |||
bda1c1c1eb | |||
d020a7974a | |||
a51fad3aab | |||
3cd32778bb | |||
8d67056f84 | |||
e986973b5e | |||
448c934cba | |||
96ef7ba55d | |||
4372de1e7e | |||
af0fb88adf | |||
066233eee8 | |||
b6f85d10b0 | |||
6f75413c09 | |||
d45fe4ce74 | |||
e828c013e6 | |||
988459f744 | |||
7c701bdf3f | |||
446fc35d5c | |||
bec9cc7047 | |||
961380acb6 | |||
84c0685a60 | |||
629222f103 | |||
8c448e5bc2 | |||
b5fa6c2d0a | |||
680b2df08a | |||
09bd47f98b | |||
7f69f9ce4f | |||
4179b4e543 | |||
66364554c4 | |||
43f2448789 | |||
130cee1e70 | |||
b976360248 | |||
225bfc4164 | |||
d7ceda4d82 | |||
14d091e60a | |||
2809668ef4 | |||
bafb86e00b | |||
f5db31b8ff | |||
e1d0dbed0c | |||
1d1fe364d0 | |||
2b9316c4ff | |||
c50cbbb526 | |||
b93d9ecd7e | |||
96243db88b | |||
4daf75a8cc | |||
8c63d7cf5b | |||
6f78a32e64 | |||
af6731c9c8 | |||
25cf0d2b94 | |||
9389791d91 | |||
aa8191d0a1 | |||
0d5c78e875 | |||
e8679ae03b | |||
d1d224b7fc | |||
df995f7bc9 | |||
af39502450 | |||
ffa38955d6 | |||
8d82fb6d8f | |||
306770331a | |||
d3f433c8cf | |||
cf49cbd1f8 | |||
8a99e75299 | |||
2dbf849c82 | |||
ba3dce0b4c | |||
ca9588380a | |||
ae2619602d | |||
de06353194 | |||
3ff3f5e1cc | |||
4b747859b3 | |||
2201765366 | |||
dfa1d5e398 | |||
ce9a90f626 | |||
2deb18beb2 | |||
0f7454059c | |||
f9ba09ac4d | |||
4e74873eae | |||
f0cd03d14f | |||
f2b069c562 | |||
bc89306dc1 | |||
bf4da1655b | |||
d819aa270f | |||
e6d945f835 | |||
4fe408f1fd | |||
c376e42092 | |||
63a653cdf0 | |||
5d900800f2 | |||
def06dbc0b | |||
9b66a597bb | |||
f1ee3b4e60 | |||
6395e39d63 | |||
2a6d9d7e31 | |||
32a7cd31da | |||
dd4a56cb5f | |||
d110d1cb5f | |||
48858019b7 | |||
aff6b1fca5 | |||
d260182ef3 | |||
e39a38b0d9 | |||
82d7179c92 | |||
f42746ba06 | |||
1f69deaccd | |||
ea8b7ab193 | |||
9938959026 | |||
d5e5485d2e | |||
97b9c8f320 | |||
35aebbc209 | |||
81f7419f70 | |||
2f951bd54d | |||
18f5963b09 | |||
836509c1d1 | |||
949d536e42 | |||
f69b17e165 | |||
49a0584c54 | |||
e21aa2c8f0 | |||
40071b1431 | |||
02e29e6990 | |||
e19de0901e | |||
137d506e42 | |||
90c4a26d52 | |||
f378a8997b | |||
1377bed988 | |||
8f9f947c42 | |||
37f6c2858f | |||
13d7f239ab | |||
a6f3c84e28 | |||
fe4e0e9835 | |||
809917f13b | |||
2b35498370 | |||
f45eabdd9e | |||
438f3ee8d2 | |||
4bea31f051 | |||
5eae7a2b93 | |||
364ef3e55c | |||
e61818f194 | |||
0f9ce319d4 | |||
5d90871789 | |||
88a9e09918 | |||
c50ecf6055 | |||
a18de75da9 | |||
e112dfd910 | |||
9154d8bd37 | |||
0b55372b3b | |||
3ad7fb010f | |||
3f64d1bb5a | |||
a6f564ad88 | |||
d97da3bb7b | |||
a77d3c92ad | |||
6d17e5307c | |||
c2205e473a | |||
4ffb194847 | |||
744cd6ec42 | |||
f08fc18ab5 | |||
462af76770 | |||
9cec554f7c | |||
08b25e610d | |||
e896d5a1a6 | |||
b939562062 | |||
256781bba5 | |||
19705196d6 | |||
3ce692bb10 | |||
78bdbde3ae | |||
8d8c066447 | |||
5da9379c37 | |||
032d20ff37 | |||
d19b17cbfe | |||
4a4f8ff5db | |||
60a9209a14 | |||
0f9e167df3 | |||
2e2b8c498e | |||
144199730f | |||
4bb4eab3b2 | |||
cf9151f669 | |||
aef4598cec | |||
3ada0fdf84 | |||
a5d97b326e | |||
2640015fb1 | |||
6cd42ddafe | |||
1f17c22132 | |||
5c62f612cc | |||
b9ca1c2e2c | |||
93b2ff2e52 | |||
3991d23a69 | |||
1be139759c | |||
d0674ad688 | |||
ffb47458ff | |||
84ec1be8a4 | |||
f4dafec645 | |||
97ce72521d | |||
d2f0e74879 | |||
d9e3895c45 | |||
5075901830 | |||
f1193bb5a0 | |||
d3dc279105 | |||
acc942f690 | |||
e947067dcf | |||
bd9ebf4603 | |||
f41192a52a | |||
ff54d6abd7 | |||
f40bcc219f | |||
679965410a | |||
c6e13ae2a3 | |||
20cdcc673b | |||
89f46222d9 | |||
b27cbfac5e | |||
31c946aeeb | |||
bfc8a26381 | |||
9d98746501 | |||
63b03ba70c | |||
70bab76b36 | |||
15d24d4308 | |||
9ec62eb045 | |||
12f841e30d | |||
335599ed22 | |||
0b717f9e76 | |||
e941f6ecca | |||
ef7744dbda | |||
c83a61c460 | |||
335684caf7 | |||
8d6220ce51 | |||
39ea5c5f99 | |||
b03597ac13 | |||
58f323c087 | |||
513a68584c | |||
88d5c68b32 | |||
14f9382cf9 | |||
cffb582568 | |||
e1812ce16c | |||
7a3163f59a | |||
6f3b2749b0 | |||
c144d4e501 | |||
edfd9d55ba | |||
774897260e | |||
65ba91411d | |||
9cbb8e1a64 | |||
53e9ad5088 | |||
cf6ea63fa6 | |||
1de0ebb7bc | |||
77c1376d6d | |||
353f1954a5 | |||
8bf3406cf8 | |||
936bf9a05c | |||
4487499663 | |||
3976cc26a2 | |||
e6ff87ecd0 | |||
c0887b5f08 | |||
f14dda4eca | |||
bd7f75c130 | |||
fbe3ce008b | |||
7ac6c8f2d1 | |||
fdfbb7bdf0 | |||
1c16bbb742 | |||
9735527062 | |||
402827497e | |||
f81aa0d867 | |||
d32a970101 | |||
cd651aa416 | |||
8a3189123a | |||
b37231d0f5 | |||
3c55719bf1 | |||
af8279a9b9 | |||
c38508c262 | |||
b0e8738ab8 | |||
cae480768e | |||
a70276c190 | |||
0c461ffe2e | |||
237511f2d6 | |||
cdcb652033 | |||
71e678b382 | |||
3050156325 | |||
4bfdbad2e4 | |||
06137ecdc4 | |||
d89f5b0df8 | |||
b6e2b36692 | |||
a6d789cfbc | |||
c07907e7bd | |||
7d8496c874 | |||
164ac56db1 | |||
fdddb8ca64 | |||
a9d4b8b0fa | |||
ec7b9f54c2 | |||
307558a7e7 | |||
febf423eab | |||
a999c23014 | |||
69f1ade595 | |||
b166576e54 | |||
ee2ba5f398 | |||
cb9825484d | |||
76cda82e23 | |||
37b61d9e6b | |||
52f0222a6d | |||
75ccac2f2c | |||
5c771a91f7 | |||
a242ad10e6 | |||
b5086b6a8f | |||
3e47dad12a | |||
235610f40c | |||
6b59559c65 | |||
23e954f716 | |||
983c899cad | |||
c2f9385965 | |||
ceb2c9e4f8 | |||
68a7f9c665 | |||
ffd8d9c7c1 | |||
c66fc8630c | |||
9ca1c66f2b | |||
33647a29d0 | |||
02b12cc762 | |||
3280993e2a | |||
3723c22054 | |||
0a2c4ea0c4 | |||
58a83c0439 | |||
d665489054 | |||
9200992024 | |||
6408cc46a8 | |||
961bcdb7ae | |||
edee70cf31 | |||
1978a9815a | |||
f5e6db9d66 | |||
a94bc40ab0 | |||
534b5ced8f | |||
5ebd9b54e4 | |||
cc4e272526 | |||
295e199bfa | |||
df3371b0f0 | |||
e4fe1d2b8d | |||
b8b9244ffa | |||
3be3989e1c | |||
ed54cf680a | |||
95e76058d3 | |||
a6bee6a860 | |||
d22780ee44 | |||
f8b0b9575d | |||
4274fd168e | |||
be7f5957f3 | |||
f2e5d987a9 | |||
f01173d8db | |||
15e8e0bf6d | |||
2c59cbdece | |||
b73da4ed02 | |||
267adb4612 | |||
05c73fa8bc | |||
bfe9f442e6 | |||
0deadb694b | |||
bed34378be | |||
5927cf2d43 | |||
fffe36e358 | |||
fac2a2d7cb | |||
0af5582ca7 | |||
582d31263c | |||
4108a528e1 | |||
ab7d7c2907 | |||
152888ee93 | |||
22f8f4f359 | |||
5f3a9e189a | |||
b734dc44fd | |||
fab224f509 | |||
2f05ebb966 | |||
a335ba519a | |||
8805693ed2 | |||
f2bb238e9b | |||
131fe670a4 | |||
11e9539416 | |||
3881ebe429 | |||
29d1b8802e | |||
bcc75732e9 | |||
50a85ee6b0 | |||
2c7424fd43 | |||
7426587c38 | |||
1f39749a5e | |||
ca63051c71 | |||
6dd44aaf0d | |||
f89457ba68 | |||
efef205fcf | |||
0c561d8528 | |||
8bfa2c4c02 | |||
f0d4c3aba9 | |||
3a99115070 | |||
7232134931 | |||
954e911eb3 | |||
63c073c93f | |||
78feef9d59 | |||
4fbdd6d570 | |||
4929c198ba | |||
9409f17372 | |||
43781c02d0 | |||
824f06e17f | |||
21dbc6da97 | |||
270ea54ff7 | |||
771ac7aba7 | |||
97d36243f2 | |||
511b47bac4 | |||
f265199fbe | |||
a191ec71a4 | |||
82dce2dd53 | |||
29ac160811 | |||
5e50ea14f8 | |||
40e6091506 | |||
0ee4d420b1 | |||
66acce9e8e | |||
6c23ae14ab | |||
6f000d0d26 | |||
9d7eb3be5a | |||
835555171e | |||
68ce4a1bf0 | |||
a995867deb | |||
6bd99d63b4 | |||
baf5d3041a | |||
a326ffa00a | |||
d28dd92b47 | |||
1de328b2e8 | |||
51bb902162 | |||
4fd14f1366 | |||
91d9559f79 | |||
3245a9b157 | |||
2b28493bba | |||
1382728bd2 | |||
0422ad080a | |||
64d682bfde | |||
b182f7e693 | |||
e6be428589 | |||
85c7f8314b | |||
796d07a7f8 | |||
2af86a10b2 | |||
7fbe486dff | |||
87e5a9859a | |||
b036e5ed72 | |||
5f1ec80ae0 | |||
fbecedaf41 | |||
aa36acd65a | |||
8d1a4588d3 | |||
66d2af4453 | |||
ef6c731bb3 | |||
98a638a2f3 | |||
96d8a7f0d7 | |||
3162b10392 | |||
e2358de27c | |||
7facb4f372 | |||
ee90fed489 | |||
4796c56c35 | |||
e2cb031386 | |||
a0bc97b90c | |||
fd240899bd | |||
885b22df40 | |||
11de3db25f | |||
14a13da7ec | |||
875a71c786 | |||
0ff5b79353 | |||
8c4d276810 | |||
3dd38c0ac8 | |||
b8816a0e2f | |||
a01a9e76f9 | |||
357d704aec | |||
868df1865c | |||
654d74da1e | |||
59939c727a | |||
fbcf190324 | |||
b9922a90cc | |||
66e0b07428 | |||
01e617ae8f | |||
52769decd4 | |||
165eec4054 | |||
8c2e602cc7 | |||
b68f141568 | |||
b5d1e8653d | |||
f6d4c90dea | |||
b5b24636ae | |||
9dedbbf47c | |||
c493c3e5c6 | |||
61d4ca1d24 | |||
2cf9af4a6e | |||
bdcd10512f | |||
fec8db6a75 | |||
b400010426 | |||
28109a39ac | |||
651f0ec445 | |||
e61d3df380 | |||
15710207b2 | |||
adfddddac6 | |||
e46982f652 | |||
900c2aea23 | |||
42f8e98cab | |||
bed0e33b4f | |||
8d6542905d | |||
39798a1a4f | |||
befe4b8e9f | |||
772e48105e | |||
9afe451b8d | |||
89d469e77e | |||
59a43889a5 | |||
7caa0daffc | |||
5e854c2cf8 | |||
9edc92ec29 | |||
1d178080a3 | |||
aa94300bdd | |||
2d768c3f28 | |||
b79af624ae | |||
38208a7c9e | |||
8eff51904e | |||
c717f4573d | |||
984d251a6d | |||
8c3b43f3ed | |||
0f1485f30b | |||
eb94c678bd | |||
50d792a121 | |||
f0d4654917 | |||
4ce93b5d9d | |||
fb0d7a1908 | |||
bb7b063757 | |||
c495f54bbb | |||
1cc1f2d91d | |||
d837cc11f9 | |||
cbb7083307 | |||
d4a17dfad1 | |||
59f8b91e25 | |||
80113f9208 | |||
27f987f0ae | |||
3ae2597261 | |||
248e7b808c | |||
a983a896f2 | |||
68df1730f5 | |||
d62ab93b24 | |||
47297f7e31 | |||
b64d611e02 | |||
9fb9bcfebd | |||
dff9c5f53e | |||
d4a77321d2 | |||
2665618fa6 | |||
b5c5560af8 | |||
065587525e | |||
58e5d5c071 | |||
b44e76db57 | |||
2ce6bc5946 | |||
fe5b225732 | |||
d499e40a4b | |||
62a66d89c6 | |||
e1b26ae287 | |||
1c151f4a3f | |||
8917926996 | |||
b54a9b9831 | |||
f08906dba1 | |||
a6bba824d3 | |||
fd84152a2b | |||
3466106119 | |||
c79b587eea | |||
4862fb7db1 | |||
2136db0e61 | |||
2f0c0f6fcd | |||
7ddc01f883 | |||
126c2162f1 | |||
094c8ab94c | |||
efe2723874 | |||
bccfeb2fa2 | |||
d498d5445c | |||
5095d090cc | |||
6544fcdc36 | |||
e834924857 | |||
2c3b8a9819 | |||
309c82fc9e | |||
0f91ce6441 | |||
f29ec3b4e1 | |||
cc1fc869cf | |||
0431d3cddc | |||
a1cd202cd2 | |||
b842493cf0 | |||
4718f09cb7 | |||
e9c357a885 | |||
fb00ff74d1 | |||
b740b079db | |||
6394841041 | |||
3f4050c647 | |||
82f01d84c2 | |||
299ea72d70 | |||
50aa286d3a | |||
6f7322150f | |||
cc9965cc96 | |||
ae90a957c6 | |||
8cec032e7d | |||
3732ab1e62 | |||
fba149ee28 | |||
4661cba974 | |||
025be8cb7c | |||
3aea32551b | |||
8e8c112ff0 | |||
b0dda08e74 | |||
2c25df122a | |||
7cb5702b37 | |||
b7502c7eaa | |||
fed020825a | |||
1c411897df | |||
f94e241fb2 | |||
757cbfd1ba | |||
3de80319db | |||
f9617d777a | |||
9961a404ae | |||
776c844d02 | |||
03782a37a2 | |||
173663380b | |||
c6fdd65c63 | |||
d9546f9dc7 | |||
2a6b0f5db7 | |||
b4e1b42cec | |||
a8898a5993 | |||
e03c68b632 | |||
a0074de12b | |||
411bedcc46 | |||
07d8caf884 | |||
c0e83ef8df | |||
4dbf4b2005 | |||
61af72b906 | |||
17be722e2b | |||
16d7927d2f | |||
55a7a5d9d5 | |||
78d7849197 | |||
d5b12fb01d | |||
31f4e378aa | |||
8a26b7b248 | |||
87c28cfdbc | |||
1f5420010d | |||
a089c48378 | |||
3e5deda46c | |||
7500c6efd0 | |||
717b5f3b07 | |||
9f6fa60bf1 | |||
1e9586f635 | |||
44f9d5e69e | |||
7c9b8f7d43 | |||
845a99d623 | |||
3d7a4bf81a | |||
d4b3e35bd2 | |||
a59f7c75dc | |||
44fe2369d6 | |||
aaaab2cfcf | |||
9a3dab20dc | |||
20379b5927 | |||
34dcce67e4 | |||
0c7f107d01 | |||
1f89571aa5 | |||
7eed1ebbf8 | |||
12cb7d7abe | |||
c9b16dcbd9 | |||
dcab6d00bb | |||
a85743f241 | |||
14cabde5cf | |||
cc078503e3 | |||
2a0c3377f9 | |||
16454f5560 | |||
c1343a78f1 | |||
9d0c65c682 | |||
9e6408244f |
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,7 +1,8 @@
|
||||
.idea/workspace.xml
|
||||
/build/
|
||||
/dist/
|
||||
/output/
|
||||
.idea/discord.xml
|
||||
build/
|
||||
dist/
|
||||
output/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
@ -11,9 +12,9 @@
|
||||
*.vice-mon-list
|
||||
docs/build
|
||||
out/
|
||||
**/*.interp
|
||||
**/*.tokens
|
||||
|
||||
parser/**/*.interp
|
||||
parser/**/*.tokens
|
||||
parser/**/*.java
|
||||
*.py[cod]
|
||||
*.egg
|
||||
*.egg-info
|
||||
@ -24,10 +25,10 @@ __pycache__/
|
||||
parser.out
|
||||
parsetab.py
|
||||
.pytest_cache/
|
||||
compiler/src/prog8_kotlin.jar
|
||||
compiler/src/compiled_java
|
||||
.attach_pid*
|
||||
compiler/lib/
|
||||
|
||||
.gradle
|
||||
build/
|
||||
/prog8compiler.jar
|
||||
sd*.img
|
||||
*.d64
|
||||
|
2
.idea/.gitignore
generated
vendored
Normal file
2
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
8
.idea/codeInsightSettings.xml
generated
Normal file
8
.idea/codeInsightSettings.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaProjectCodeInsightSettings">
|
||||
<excluded-names>
|
||||
<name>kotlin.Result</name>
|
||||
</excluded-names>
|
||||
</component>
|
||||
</project>
|
7
.idea/compiler.xml
generated
Normal file
7
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="BUILD_PROCESS_HEAP_SIZE" value="1200" />
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
7
.idea/inspectionProfiles/Project_Default.xml
generated
7
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,6 +1,13 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<Languages>
|
||||
<language minSize="70" name="Kotlin" />
|
||||
<language isEnabled="false" name="Groovy" />
|
||||
</Languages>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="processCode" value="false" />
|
||||
<option name="processLiterals" value="true" />
|
||||
|
6
.idea/kotlinc.xml
generated
6
.idea/kotlinc.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Kotlin2JvmCompilerArguments">
|
||||
<option name="jvmTarget" value="1.8" />
|
||||
</component>
|
||||
</project>
|
9
.idea/libraries/antlr_4_7_2_complete.xml
generated
9
.idea/libraries/antlr_4_7_2_complete.xml
generated
@ -1,9 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr-4.7.2-complete">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-4.7.2-complete.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
19
.idea/libraries/antlr_antlr4.xml
generated
Normal file
19
.idea/libraries/antlr_antlr4.xml
generated
Normal file
@ -0,0 +1,19 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr.antlr4" type="repository">
|
||||
<properties maven-id="org.antlr:antlr4:4.9.2">
|
||||
<exclude>
|
||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.9.2/antlr4-4.9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.9.2/antlr4-runtime-4.9.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.2/antlr-runtime-3.5.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3/ST4-4.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
9
.idea/libraries/antlr_runtime_4_7_2.xml
generated
9
.idea/libraries/antlr_runtime_4_7_2.xml
generated
@ -1,9 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr-runtime-4.7.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-runtime-4.7.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
Normal file
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
Normal file
@ -0,0 +1,25 @@
|
||||
<component name="libraryTable">
|
||||
<library name="github.hypfvieh.dbus.java" type="repository">
|
||||
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.1/dbus-java-3.3.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.6/jnr-unixsocket-0.38.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.2/jnr-ffi-2.2.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1-native.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.1/asm-9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.1/asm-commons-9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.1/asm-analysis-9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.1/asm-tree-9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.1/asm-util-9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.1/jnr-constants-0.10.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.4/jnr-enxio-0.32.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.5/jnr-posix-3.1.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/glassfish_javax_json.xml
generated
Normal file
10
.idea/libraries/glassfish_javax_json.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="libraryTable">
|
||||
<library name="glassfish.javax.json" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.glassfish:javax.json:1.1.4" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/hamcrest.xml
generated
Normal file
10
.idea/libraries/hamcrest.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="libraryTable">
|
||||
<library name="hamcrest" type="repository">
|
||||
<properties maven-id="org.hamcrest:hamcrest:2.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
22
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
22
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
Normal file
@ -0,0 +1,22 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:4.6.3" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/4.6.3/kotest-assertions-core-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.0/kotlin-stdlib-jdk8-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.5.0/kotlin-stdlib-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.0/kotlin-stdlib-jdk7-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/4.6.3/kotest-assertions-shared-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.9/java-diff-utils-4.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.5.0/kotlinx-coroutines-jdk8-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.5.0/kotlin-reflect-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.0/kotlin-stdlib-common-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/4.6.3/kotest-common-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/4.6.3/kotest-assertions-api-jvm-4.6.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
24
.idea/libraries/io_kotest_property_jvm.xml
generated
Normal file
24
.idea/libraries/io_kotest_property_jvm.xml
generated
Normal file
@ -0,0 +1,24 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.property.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:4.6.3" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/4.6.3/kotest-property-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.0/kotlin-stdlib-jdk8-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.5.0/kotlin-stdlib-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.0/kotlin-stdlib-jdk7-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/4.6.3/kotest-common-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/4.6.3/kotest-assertions-shared-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/4.6.3/kotest-assertions-api-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.5.0/kotlinx-coroutines-jdk8-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.9/java-diff-utils-4.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/mifmif/generex/1.0.2/generex-1.0.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/dk/brics/automaton/automaton/1.11-8/automaton-1.11-8.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.5.0/kotlin-reflect-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.0/kotlin-stdlib-common-1.5.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
56
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
56
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
Normal file
@ -0,0 +1,56 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:4.6.3" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/4.6.3/kotest-runner-junit5-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/4.6.3/kotest-framework-api-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/4.6.3/kotest-assertions-shared-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.9/java-diff-utils-4.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/4.6.3/kotest-common-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/4.6.3/kotest-framework-engine-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.105/classgraph-4.8.105.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-script-util/1.5.0/kotlin-script-util-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/trove4j/1.0.20181211/trove4j-1.0.20181211.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-daemon-client/1.5.0/kotlin-daemon-client-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.3.8/kotlinx-coroutines-core-1.3.8.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-scripting-jvm/1.5.0/kotlin-scripting-jvm-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-scripting-common/1.5.0/kotlin-scripting-common-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/4.6.3/kotest-framework-discovery-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/4.6.3/kotest-assertions-core-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.5.0/kotlinx-coroutines-jdk8-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/4.6.3/kotest-assertions-api-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/4.6.3/kotest-extensions-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.9.3/mockk-1.9.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.9.3/mockk-common-1.9.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.9.3/mockk-dsl-1.9.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.9.3/mockk-dsl-jvm-1.9.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.9.3/mockk-agent-jvm-1.9.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.9.3/mockk-agent-api-1.9.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.9.3/mockk-agent-common-1.9.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.0.1/objenesis-3.0.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.9.10/byte-buddy-1.9.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.9.10/byte-buddy-agent-1.9.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/4.6.3/kotest-framework-concurrency-jvm-4.6.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.0/kotlinx-coroutines-core-jvm-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.0/kotlin-stdlib-common-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.6.2/junit-platform-engine-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.6.2/junit-platform-commons-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.6.2/junit-platform-suite-api-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.6.2/junit-platform-launcher-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.6.2/junit-jupiter-api-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.0/kotlin-stdlib-jdk8-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.5.0/kotlin-stdlib-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.0/kotlin-stdlib-jdk7-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-script-runtime/1.5.0/kotlin-script-runtime-1.5.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.5.0/kotlin-reflect-1.5.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
Normal file
10
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.3" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.3/kotlinx-cli-jvm-0.3.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
17
.idea/libraries/junit_jupiter.xml
generated
Normal file
17
.idea/libraries/junit_jupiter.xml
generated
Normal file
@ -0,0 +1,17 @@
|
||||
<component name="libraryTable">
|
||||
<library name="junit.jupiter" type="repository">
|
||||
<properties maven-id="org.junit.jupiter:junit-jupiter:5.7.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter/5.7.2/junit-jupiter-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.7.2/junit-jupiter-params-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.7.2/junit-jupiter-engine-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
15
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
Normal file
15
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
Normal file
@ -0,0 +1,15 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.12/kotlin-result-jvm-1.1.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.10/kotlin-stdlib-jdk8-1.5.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.5.10/kotlin-stdlib-1.5.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.10/kotlin-stdlib-jdk7-1.5.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.10/kotlin-stdlib-common-1.5.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
11
.idea/libraries/slf4j_simple.xml
generated
Normal file
11
.idea/libraries/slf4j_simple.xml
generated
Normal file
@ -0,0 +1,11 @@
|
||||
<component name="libraryTable">
|
||||
<library name="slf4j.simple" type="repository">
|
||||
<properties maven-id="org.slf4j:slf4j-simple:1.7.30" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.30/slf4j-simple-1.7.30.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
13
.idea/libraries/takes.xml
generated
Normal file
13
.idea/libraries/takes.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="libraryTable">
|
||||
<library name="takes" type="repository">
|
||||
<properties maven-id="org.takes:takes:1.19" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.19/takes-1.19.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.42/cactoos-0.42.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/libraries/unittest_libs.xml
generated
10
.idea/libraries/unittest_libs.xml
generated
@ -1,10 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="unittest-libs">
|
||||
<CLASSES>
|
||||
<root url="file://$PROJECT_DIR$/compiler/lib" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/compiler/lib" recursive="false" />
|
||||
</library>
|
||||
</component>
|
29
.idea/markdown-navigator-enh.xml
generated
Normal file
29
.idea/markdown-navigator-enh.xml
generated
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownEnhProjectSettings">
|
||||
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
|
||||
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imagePathType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
|
||||
<LinkMapSettings>
|
||||
<textMaps />
|
||||
</LinkMapSettings>
|
||||
</component>
|
||||
<component name="MarkdownNavigatorHistory">
|
||||
<PasteImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
|
||||
<highlightList />
|
||||
<directories />
|
||||
<filenames />
|
||||
</PasteImageHistory>
|
||||
<CopyImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
|
||||
<highlightList />
|
||||
<directories />
|
||||
<filenames />
|
||||
</CopyImageHistory>
|
||||
<PasteLinkHistory onPasteImageTargetRef="3" onPasteTargetRef="1" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteWikiElement="2" onPasteReferenceElement="2" hideInapplicableOperations="false" preserveLinkFormat="false" useHeadingForLinkText="false" linkFormat="0" saveAsDefaultOnOK="false" />
|
||||
<TableToJsonHistory>
|
||||
<entries />
|
||||
</TableToJsonHistory>
|
||||
<TableSortHistory>
|
||||
<entries />
|
||||
</TableSortHistory>
|
||||
</component>
|
||||
</project>
|
57
.idea/markdown-navigator.xml
generated
Normal file
57
.idea/markdown-navigator.xml
generated
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="FlexmarkProjectSettings">
|
||||
<FlexmarkHtmlSettings flexmarkSpecExampleRendering="0" flexmarkSpecExampleRenderHtml="false">
|
||||
<flexmarkSectionLanguages>
|
||||
<option name="1" value="Markdown" />
|
||||
<option name="2" value="HTML" />
|
||||
<option name="3" value="flexmark-ast:1" />
|
||||
</flexmarkSectionLanguages>
|
||||
</FlexmarkHtmlSettings>
|
||||
</component>
|
||||
<component name="MarkdownProjectSettings">
|
||||
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true" lastLayoutSetsDefault="false">
|
||||
<PanelProvider>
|
||||
<provider providerId="com.vladsch.md.nav.editor.javafx.html.panel" providerName="JavaFX WebView" />
|
||||
</PanelProvider>
|
||||
</PreviewSettings>
|
||||
<ParserSettings gitHubSyntaxChange="false" correctedInvalidSettings="false" emojiShortcuts="1" emojiImages="0">
|
||||
<PegdownExtensions>
|
||||
<option name="ANCHORLINKS" value="true" />
|
||||
<option name="ATXHEADERSPACE" value="true" />
|
||||
<option name="FENCED_CODE_BLOCKS" value="true" />
|
||||
<option name="INTELLIJ_DUMMY_IDENTIFIER" value="true" />
|
||||
<option name="RELAXEDHRULES" value="true" />
|
||||
<option name="STRIKETHROUGH" value="true" />
|
||||
<option name="TABLES" value="true" />
|
||||
<option name="TASKLISTITEMS" value="true" />
|
||||
</PegdownExtensions>
|
||||
<ParserOptions>
|
||||
<option name="COMMONMARK_LISTS" value="true" />
|
||||
<option name="EMOJI_SHORTCUTS" value="true" />
|
||||
<option name="GFM_TABLE_RENDERING" value="true" />
|
||||
<option name="PRODUCTION_SPEC_PARSER" value="true" />
|
||||
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
|
||||
</ParserOptions>
|
||||
</ParserSettings>
|
||||
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" addPageHeader="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0">
|
||||
<GeneratorProvider>
|
||||
<provider providerId="com.vladsch.md.nav.editor.javafx.html.generator" providerName="JavaFx HTML Generator" />
|
||||
</GeneratorProvider>
|
||||
<headerTop />
|
||||
<headerBottom />
|
||||
<bodyTop />
|
||||
<bodyBottom />
|
||||
</HtmlSettings>
|
||||
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
|
||||
<StylesheetProvider>
|
||||
<provider providerId="com.vladsch.md.nav.editor.javafx.html.css" providerName="Default JavaFx Stylesheet" />
|
||||
</StylesheetProvider>
|
||||
<ScriptProviders>
|
||||
<provider providerId="com.vladsch.md.nav.editor.hljs.html.script" providerName="HighlightJS Script" />
|
||||
</ScriptProviders>
|
||||
<cssText />
|
||||
<cssUriHistory />
|
||||
</CssSettings>
|
||||
</component>
|
||||
</project>
|
21
.idea/misc.xml
generated
21
.idea/misc.xml
generated
@ -1,6 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
||||
<component name="ANTLRGenerationPreferences">
|
||||
<option name="perGrammarGenerationSettings">
|
||||
<list>
|
||||
<PerGrammarGenerationSettings>
|
||||
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" />
|
||||
<option name="autoGen" value="true" />
|
||||
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
|
||||
<option name="libDir" value="" />
|
||||
<option name="encoding" value="" />
|
||||
<option name="pkg" value="" />
|
||||
<option name="language" value="" />
|
||||
<option name="generateListener" value="false" />
|
||||
</PerGrammarGenerationSettings>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
6
.idea/modules.xml
generated
6
.idea/modules.xml
generated
@ -2,9 +2,15 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGeneration/codeGeneration.iml" filepath="$PROJECT_DIR$/codeGeneration/codeGeneration.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" filepath="$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@ -3,4 +3,4 @@
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
29
.readthedocs.yaml
Normal file
29
.readthedocs.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
tools:
|
||||
python: "3.9"
|
||||
# You can also specify other tool versions:
|
||||
# nodejs: "16"
|
||||
# rust: "1.55"
|
||||
# golang: "1.17"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
|
||||
# Optionally declare the Python requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
11
.travis.yml
11
.travis.yml
@ -1,11 +0,0 @@
|
||||
language: java
|
||||
# jdk: openjdk8
|
||||
# dist: xenial
|
||||
# sudo: false
|
||||
|
||||
before_install:
|
||||
- chmod +x gradlew
|
||||
|
||||
script:
|
||||
- ./gradlew test
|
||||
|
32
CompilerDevelopment.md
Normal file
32
CompilerDevelopment.md
Normal file
@ -0,0 +1,32 @@
|
||||
#### Just a few remarks upfront:
|
||||
* There is the (gradle/IDEA) module `parser`: that's the parser generated by ANTLR4, in Java. The only file to be edited here is the grammar, `prog8.g4`.
|
||||
* Then we have the module `compilerAst` - in Kotlin - which uses `parser` and adds AST nodes. Here we put our additions to the generated thing, *including any tests of the parsing stage*.
|
||||
- the name is a bit misleading, as this module isn't (or, resp. shouldn't be; see below) about *compiling*, only the parsing stage
|
||||
- also, the tree that comes out isn't much of an *abstraction*, but rather still more or less a parse tree (this might very well change).
|
||||
- **However, let's not *yet* rename the module.** We'll find a good name during refactoring.
|
||||
|
||||
#### Problems with `compilerAst`:
|
||||
* `ModuleImporter.kt`, doing (Prog8-) module resolution. That's not the parser's job.
|
||||
* `ParsingFailedError` (in `ModuleParsing.kt`): this exception (it is actually *not* a `java.lang.Error`...) is thrown in a number of places, where other exceptions would make more sense. For example: not finding a file should just yield a `NoSuchFileException`, not this one. The other problem with it is that it does not provide any additional information about the source of parsing error, in particular a `Position`.
|
||||
* During parsing, character literals are turned into UBYTEs (since there is no basic type e.g. CHAR). That's bad because it depends on a specific character encoding (`IStringEncoding` in `compilerAst/src/prog8/ast/AstToplevel.kt`) of/for some target platform. Note that *strings* are indeed encoded later, in the `compiler` module.
|
||||
* The same argument applies to `IMemSizer`, and - not entirely sure about that - `IBuiltinFunctions`.
|
||||
|
||||
#### Steps to take, in conceptual (!) order:
|
||||
1. introduce an abstraction `SourceCode` that encapsulates the origin and actual loading of Prog8 source code
|
||||
- from the local file system (use case: user programs)
|
||||
- from resources (prog8lib)
|
||||
- from plain strings (for testing)
|
||||
2. add subclass `ParseError : ParsingFailedError` which adds information about the *source of parsing error* (`SourceCode` and `Position`). We cannot just replace `ParsingFailedError` right away because it is so widely used (even in the `compiler` module). Therefore we'll just subclass for the time being, add more and more tests requiring the new one to be thrown (or, resp., NOT to be thrown), and gradually transition.
|
||||
3. introduce a minimal interface to the outside, input: `SourceCode`, output: a tree with a `Module` node as the root
|
||||
- this will be the Kotlin singleton `Prog8Parser` with the main method `parseModule`
|
||||
- plus, optionally, method's for registering/unregistering a listener with the parser
|
||||
- the *only* exception ever thrown / reported to listeners (TBD) will be `ParseError`
|
||||
- anything related to the lexer, error strategies, character/token streams is hidden from the outside
|
||||
- to make a clear distinction between the *generated* parser (and lexer) vs. `Prog8Parser`, and to discourage directly using the generated stuff, we'll rename the existing `prog8Parser`/`prog8Lexer` to `Prog8ANTLRParser` and `Prog8ANTLRLexer` and move them to package `prog8.parser.generated`
|
||||
4. introduce AST node `CharLiteral` and keep them until after identifier resolution and type checking; insert there an AST transformation step that turns them in UBYTE constants (literals)
|
||||
5. remove uses of `IStringEncoding` from module `compilerAst` - none should be necessary anymore
|
||||
6. move `IStringEncoding` to module `compiler`
|
||||
7. same with `ModuleImporter`, then rewrite that (addressing #46)
|
||||
8. refactor AST nodes and grammar: less generated parse tree nodes (`XyzContext`), less intermediary stuff (private classes in `Antrl2Kotlin.kt`), more compact code. Also: nicer names such as simply `StringLiteral` instead of `StringLiteralValue`
|
||||
9. re-think `IStringEncoding` to address #38
|
||||
|
7
LICENSE
7
LICENSE
@ -1,3 +1,10 @@
|
||||
|
||||
This sofware license is for Prog8 the compiler + associated libraries.
|
||||
The software generated by running the compiler is excluded from this.
|
||||
|
||||
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
|
134
README.md
134
README.md
@ -1,59 +1,79 @@
|
||||
[](https://saythanks.io/to/irmen)
|
||||
[](https://travis-ci.org/irmen/prog8)
|
||||
[](https://prog8.readthedocs.io/)
|
||||
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/6510 microprocessors
|
||||
===========================================================================
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
============================================================================
|
||||
|
||||
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||
|
||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
||||
|
||||
|
||||
This is a structured programming language for the 8-bit 6502/6510 microprocessor from the late 1970's and 1980's
|
||||
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
||||
as used in many home computers from that era. It is a medium to low level programming language,
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler):
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||
|
||||
- reduction of source code length
|
||||
- easier program understanding (because it's higher level, and way more compact)
|
||||
Documentation
|
||||
-------------
|
||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||
https://prog8.readthedocs.io/
|
||||
|
||||
Software license
|
||||
----------------
|
||||
GNU GPL 3.0, see file LICENSE
|
||||
|
||||
- prog8 (the compiler + libraries) is licensed under GNU GPL 3.0
|
||||
- *exception:* the resulting files created by running the compiler are free to use in whatever way desired.
|
||||
|
||||
|
||||
What does Prog8 provide?
|
||||
------------------------
|
||||
|
||||
- reduction of source code length over raw assembly
|
||||
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
||||
- modularity, symbol scoping, subroutines
|
||||
- subroutines have enforced input- and output parameter definitions
|
||||
- various data types other than just bytes (16-bit words, floats, strings, 16-bit register pairs)
|
||||
- automatic variable allocations, automatic string variables and string sharing
|
||||
- constant folding in expressions (compile-time evaluation)
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- subroutines with input parameters and result values
|
||||
- high-level program optimizations
|
||||
- small program boilerplate/compilersupport overhead
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- conditional branches
|
||||
- automatic type conversions
|
||||
- floating point operations (uses the C64 Basic ROM routines for this)
|
||||
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
|
||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
|
||||
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
|
||||
|
||||
Rapid edit-compile-run-debug cycle:
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
- use modern PC to work on
|
||||
- quick compilation times (less than 1 second)
|
||||
- option to automatically run the program in the Vice emulator
|
||||
- use a modern PC to do the work on, use nice editors and enjoy quick compilation times
|
||||
- can automatically run the program in the Vice emulator after succesful compilation
|
||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||
|
||||
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||
|
||||
It is mainly targeted at the Commodore-64 machine at this time.
|
||||
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
||||
|
||||
Documentation is online at https://prog8.readthedocs.io/
|
||||
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
|
||||
|
||||
Required tools:
|
||||
---------------
|
||||
|
||||
Additional required tools
|
||||
-------------------------
|
||||
|
||||
[64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path.
|
||||
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
|
||||
For other platforms it is very easy to compile it yourself (make ; make install).
|
||||
|
||||
A **Java runtime (jre or jdk), version 8 or newer** is required to run a prepackaged version of the compiler.
|
||||
A **Java runtime (jre or jdk), version 11 or newer** is required to run a prepackaged version of the compiler.
|
||||
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
|
||||
IntelliJ IDEA with the Kotlin plugin).
|
||||
|
||||
It's handy to have a C-64 emulator or a real C-64 to run the programs on. The compiler assumes the presence
|
||||
of the [Vice emulator](http://vice-emu.sourceforge.net/)
|
||||
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
||||
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
||||
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
||||
|
||||
|
||||
Example code
|
||||
@ -61,44 +81,43 @@ Example code
|
||||
|
||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
|
||||
%import c64utils
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
main {
|
||||
|
||||
ubyte[256] sieve
|
||||
ubyte candidate_prime = 2
|
||||
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
|
||||
sub start() {
|
||||
memset(sieve, 256, false)
|
||||
|
||||
c64scr.print("prime numbers up to 255:\n\n")
|
||||
sys.memset(sieve, 256, false) ; clear the sieve
|
||||
txt.print("prime numbers up to 255:\n\n")
|
||||
ubyte amount=0
|
||||
while true {
|
||||
repeat {
|
||||
ubyte prime = find_next_prime()
|
||||
if prime==0
|
||||
break
|
||||
c64scr.print_ub(prime)
|
||||
c64scr.print(", ")
|
||||
txt.print_ub(prime)
|
||||
txt.print(", ")
|
||||
amount++
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("number of primes (expected 54): ")
|
||||
c64scr.print_ub(amount)
|
||||
c64.CHROUT('\n')
|
||||
txt.nl()
|
||||
txt.print("number of primes (expected 54): ")
|
||||
txt.print_ub(amount)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub find_next_prime() -> ubyte {
|
||||
|
||||
while sieve[candidate_prime] {
|
||||
candidate_prime++
|
||||
if candidate_prime==0
|
||||
return 0
|
||||
return 0 ; we wrapped; no more primes
|
||||
}
|
||||
|
||||
|
||||
; found next one, mark the multiples and return it.
|
||||
sieve[candidate_prime] = true
|
||||
uword multiple = candidate_prime
|
||||
|
||||
while multiple < len(sieve) {
|
||||
sieve[lsb(multiple)] = true
|
||||
multiple += candidate_prime
|
||||
@ -108,11 +127,12 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
when compiled an ran on a C-64 you'll get:
|
||||
|
||||

|
||||
|
||||
|
||||
One of the included examples (wizzine.p8) animates a bunch of sprite balloons and looks like this:
|
||||
|
||||

|
||||
@ -124,3 +144,9 @@ Another example (cube3d-sprites.p8) draws the vertices of a rotating 3d cube:
|
||||
If you want to play a video game, a fully working Tetris clone is included in the examples:
|
||||
|
||||

|
||||
|
||||
There are a couple of examples specially made for the CommanderX16 compiler target.
|
||||
For instance here's a well known space ship animated in 3D with hidden line removal,
|
||||
in the CommanderX16 emulator:
|
||||
|
||||

|
||||
|
10
build.gradle
Normal file
10
build.gradle
Normal file
@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
5
clean.sh
5
clean.sh
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
rm *.jar *.asm *.prg *.vm.txt *.vice-mon-list
|
||||
rm -r build
|
||||
|
52
codeGeneration/build.gradle
Normal file
52
codeGeneration/build.gradle
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "io.kotest" version "0.3.8"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':compilerInterfaces')
|
||||
implementation project(':compilerAst')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:4.6.3'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/test"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
// Always run tests, even when nothing changed.
|
||||
dependsOn 'cleanTest'
|
||||
|
||||
// Show test results.
|
||||
testLogging {
|
||||
events "skipped", "failed"
|
||||
}
|
||||
}
|
19
codeGeneration/codeGeneration.iml
Normal file
19
codeGeneration/codeGeneration.iml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="compilerInterfaces" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,3 @@
|
||||
package prog8.compiler.target
|
||||
|
||||
class AssemblyError(msg: String) : RuntimeException(msg)
|
35
codeGeneration/src/prog8/compiler/target/C64Target.kt
Normal file
35
codeGeneration/src/prog8/compiler/target/C64Target.kt
Normal file
@ -0,0 +1,35 @@
|
||||
package prog8.compiler.target
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.ast.base.ByteDatatypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.PassByReferenceDatatypes
|
||||
import prog8.ast.base.WordDatatypes
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.cbm.Petscii
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
|
||||
|
||||
object C64Target: ICompilationTarget {
|
||||
override val name = "c64"
|
||||
override val machine = C64MachineDefinition
|
||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||
val coded = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||
return coded.fold(
|
||||
failure = { throw it },
|
||||
success = { it }
|
||||
)
|
||||
}
|
||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
||||
else -> -9999999
|
||||
}
|
||||
}
|
||||
}
|
36
codeGeneration/src/prog8/compiler/target/Cx16Target.kt
Normal file
36
codeGeneration/src/prog8/compiler/target/Cx16Target.kt
Normal file
@ -0,0 +1,36 @@
|
||||
package prog8.compiler.target
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.ast.base.ByteDatatypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.PassByReferenceDatatypes
|
||||
import prog8.ast.base.WordDatatypes
|
||||
import prog8.compiler.target.cbm.Petscii
|
||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
|
||||
|
||||
object Cx16Target: ICompilationTarget {
|
||||
override val name = "cx16"
|
||||
override val machine = CX16MachineDefinition
|
||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||
val coded= if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||
return coded.fold(
|
||||
failure = { throw it },
|
||||
success = { it }
|
||||
)
|
||||
}
|
||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
||||
else -> -9999999
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,206 @@
|
||||
package prog8.compiler.target.c64
|
||||
|
||||
import prog8.compiler.target.cbm.viceMonListPostfix
|
||||
import prog8.compilerinterface.*
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
|
||||
object C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
// 5-byte cbm MFLPT format limitations:
|
||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||
override val FLOAT_MEM_SIZE = 5
|
||||
override val POINTER_MEM_SIZE = 2
|
||||
override val BASIC_LOAD_ADDRESS = 0x0801
|
||||
override val RAW_LOAD_ADDRESS = 0xc000
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
override val ESTACK_LO = 0xce00 // $ce00-$ceff inclusive
|
||||
override val ESTACK_HI = 0xcf00 // $ce00-$ceff inclusive
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "${programNameWithPath}.$viceMonListPostfix",
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(x: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun isRegularRAMaddress(address: Int): Boolean = address<0xa000 || address in 0xc000..0xcfff
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
}
|
||||
|
||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
|
||||
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
||||
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
|
||||
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
||||
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
|
||||
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
|
||||
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
|
||||
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
|
||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
||||
|
||||
|
||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x03 // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfb // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfd // temp storage 2 for a word $fb+$fc
|
||||
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x04..0xf9)
|
||||
free.add(0xff)
|
||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
free.addAll(listOf(
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||
0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||
0x22, 0x23, 0x24, 0x25,
|
||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||
// 0x90-0xfa is 'kernal work storage area'
|
||||
))
|
||||
}
|
||||
|
||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
// remove the zeropage locations used for floating point operations from the free list
|
||||
free.removeAll(listOf(
|
||||
0x22, 0x23, 0x24, 0x25,
|
||||
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||
))
|
||||
}
|
||||
|
||||
if(options.zeropage!= ZeropageType.DONTUSE) {
|
||||
// add the free Zp addresses
|
||||
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
||||
0xb0, 0xb1, 0xbe, 0xbf, 0xf9))
|
||||
} else {
|
||||
// don't use the zeropage at all
|
||||
free.clear()
|
||||
}
|
||||
}
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
}
|
||||
|
||||
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short):
|
||||
IMachineFloat {
|
||||
|
||||
companion object {
|
||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||
fun fromNumber(num: Number): Mflpt5 {
|
||||
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format
|
||||
// and https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||
|
||||
val flt = num.toDouble()
|
||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||
if (flt == 0.0)
|
||||
return zero
|
||||
|
||||
val sign = if (flt < 0.0) 0x80L else 0x00L
|
||||
var exponent = 128 + 32 // 128 is cbm's bias, 32 is this algo's bias
|
||||
var mantissa = flt.absoluteValue
|
||||
|
||||
// if mantissa is too large, shift right and adjust exponent
|
||||
while (mantissa >= 0x100000000) {
|
||||
mantissa /= 2.0
|
||||
exponent++
|
||||
}
|
||||
// if mantissa is too small, shift left and adjust exponent
|
||||
while (mantissa < 0x80000000) {
|
||||
mantissa *= 2.0
|
||||
exponent--
|
||||
}
|
||||
|
||||
return when {
|
||||
exponent < 0 -> zero // underflow, use zero instead
|
||||
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
||||
exponent == 0 -> zero
|
||||
else -> {
|
||||
val mantLong = mantissa.toLong()
|
||||
Mflpt5(
|
||||
exponent.toShort(),
|
||||
(mantLong.and(0x7f000000L) ushr 24).or(sign).toShort(),
|
||||
(mantLong.and(0x00ff0000L) ushr 16).toShort(),
|
||||
(mantLong.and(0x0000ff00L) ushr 8).toShort(),
|
||||
(mantLong.and(0x000000ffL)).toShort())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toDouble(): Double {
|
||||
if (this == zero) return 0.0
|
||||
val exp = b0 - 128
|
||||
val sign = (b1.toInt() and 0x80) > 0
|
||||
val number = 0x80000000L.or(b1.toLong() shl 24).or(b2.toLong() shl 16).or(b3.toLong() shl 8).or(b4.toLong())
|
||||
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||
return if (sign) -result else result
|
||||
}
|
||||
|
||||
override fun makeFloatFillAsm(): String {
|
||||
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
||||
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
||||
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
||||
val b3 = "$" + b3.toString(16).padStart(2, '0')
|
||||
val b4 = "$" + b4.toString(16).padStart(2, '0')
|
||||
return "$b0, $b1, $b2, $b3, $b4"
|
||||
}
|
||||
}
|
||||
}
|
103
codeGeneration/src/prog8/compiler/target/cbm/AssemblyProgram.kt
Normal file
103
codeGeneration/src/prog8/compiler/target/cbm/AssemblyProgram.kt
Normal file
@ -0,0 +1,103 @@
|
||||
package prog8.compiler.target.cbm
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.mapError
|
||||
import prog8.compilerinterface.CompilationOptions
|
||||
import prog8.compilerinterface.IAssemblyProgram
|
||||
import prog8.compilerinterface.OutputType
|
||||
import prog8.compilerinterface.generatedLabelPrefix
|
||||
import prog8.parser.SourceCode
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
|
||||
internal const val viceMonListPostfix = "vice-mon-list"
|
||||
|
||||
class AssemblyProgram(
|
||||
override val valid: Boolean,
|
||||
override val name: String,
|
||||
outputDir: Path,
|
||||
private val compTarget: String) : IAssemblyProgram {
|
||||
|
||||
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||
private val prgFile = outputDir.resolve("$name.prg")
|
||||
private val binFile = outputDir.resolve("$name.bin")
|
||||
private val viceMonListFile = outputDir.resolve("$name.$viceMonListPostfix")
|
||||
|
||||
override fun assemble(quiet: Boolean, options: CompilationOptions): Int {
|
||||
// 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", "-Wno-shadow", // "-Werror",
|
||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
||||
|
||||
if(quiet)
|
||||
command.add("--quiet")
|
||||
|
||||
val outFile = when (options.output) {
|
||||
OutputType.PRG -> {
|
||||
command.add("--cbm-prg")
|
||||
println("\nCreating prg for target $compTarget.")
|
||||
prgFile
|
||||
}
|
||||
OutputType.RAW -> {
|
||||
command.add("--nostart")
|
||||
println("\nCreating raw binary for target $compTarget.")
|
||||
binFile
|
||||
}
|
||||
}
|
||||
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||
|
||||
val proc = ProcessBuilder(command).inheritIO().start()
|
||||
val result = proc.waitFor()
|
||||
if (result == 0) {
|
||||
removeGeneratedLabelsFromMonlist()
|
||||
generateBreakpointList()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun removeGeneratedLabelsFromMonlist() {
|
||||
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
|
||||
val lines = viceMonListFile.toFile().readLines()
|
||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||
for (line in lines) {
|
||||
if(pattern.matchEntire(line)==null)
|
||||
it.write(line+"\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateBreakpointList() {
|
||||
// builds list of breakpoints, appends to monitor list file
|
||||
val breakpoints = mutableListOf<String>()
|
||||
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them
|
||||
for (line in viceMonListFile.toFile().readLines()) {
|
||||
val match = pattern.matchEntire(line)
|
||||
if (match != null)
|
||||
breakpoints.add("break \$" + match.groupValues[1])
|
||||
}
|
||||
val num = breakpoints.size
|
||||
breakpoints.add(0, "; vice monitor breakpoint list now follows")
|
||||
breakpoints.add(1, "; $num breakpoints have been defined")
|
||||
breakpoints.add(2, "del")
|
||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
|
||||
return com.github.michaelbull.result.runCatching {
|
||||
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").readText()
|
||||
}.mapError { NoSuchFileException(File(filename)) }
|
||||
} else {
|
||||
val sib = Path(source.origin).resolveSibling(filename)
|
||||
if (sib.isRegularFile())
|
||||
Ok(SourceCode.File(sib).readText())
|
||||
else
|
||||
Ok(SourceCode.File(Path(filename)).readText())
|
||||
}
|
||||
}
|
1180
codeGeneration/src/prog8/compiler/target/cbm/Petscii.kt
Normal file
1180
codeGeneration/src/prog8/compiler/target/cbm/Petscii.kt
Normal file
File diff suppressed because it is too large
Load Diff
3192
codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt
Normal file
3192
codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,367 @@
|
||||
package prog8.compiler.target.cpu6502.codegen
|
||||
|
||||
|
||||
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
||||
|
||||
|
||||
fun optimizeAssembly(lines: MutableList<String>): Int {
|
||||
|
||||
var numberOfOptimizations = 0
|
||||
|
||||
var linesByFour = getLinesBy(lines, 4)
|
||||
|
||||
var mods = optimizeUselessStackByteWrites(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeIncDec(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeCmpSequence(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeStoreLoadSame(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods= optimizeJsrRts(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
var linesByFourteen = getLinesBy(lines, 14)
|
||||
mods = optimizeSameAssignments(linesByFourteen)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFourteen = getLinesBy(lines, 14)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
// TODO more assembly optimizations
|
||||
|
||||
return numberOfOptimizations
|
||||
}
|
||||
|
||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||
|
||||
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||
if(modification.remove)
|
||||
lines.removeAt(modification.lineIndex)
|
||||
else
|
||||
lines[modification.lineIndex] = modification.replacement!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
|
||||
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// when statement (on bytes) generates a sequence of:
|
||||
// lda $ce01,x
|
||||
// cmp #$20
|
||||
// beq check_prog8_s72choice_32
|
||||
// lda $ce01,x
|
||||
// cmp #$21
|
||||
// beq check_prog8_s73choice_33
|
||||
// the repeated lda can be removed
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||
lines[1].value.trim().startsWith("cmp ") &&
|
||||
lines[2].value.trim().startsWith("beq ") &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||
// this is a lot harder for word values because the instruction sequence varies.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||
lines[1].value.trim()=="dex" &&
|
||||
lines[2].value.trim()=="inx" &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
|
||||
// optimize sequential assignments of the isSameAs value to various targets (bytes, words, floats)
|
||||
// the float one is the one that requires 2*7=14 lines of code to check...
|
||||
// @todo a better place to do this is in the Compiler instead and transform the Ast, or the AsmGen, and never even create the inefficient asm in the first place...
|
||||
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFourteen) {
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
val fifth = lines[4].value.trimStart()
|
||||
val sixth = lines[5].value.trimStart()
|
||||
val seventh = lines[6].value.trimStart()
|
||||
val eighth = lines[7].value.trimStart()
|
||||
|
||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("sta") && eighth.startsWith("sty")) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = fifth.substring(4)
|
||||
val fourthvalue = sixth.substring(4)
|
||||
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||
// lda/ldy sta/sty twice the isSameAs word --> remove second lda/ldy pair (fifth and sixth lines)
|
||||
mods.add(Modification(lines[4].index, true, null))
|
||||
mods.add(Modification(lines[5].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
if(first.startsWith("lda") && second.startsWith("sta") && third.startsWith("lda") && fourth.startsWith("sta")) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = third.substring(4)
|
||||
if(firstvalue==secondvalue) {
|
||||
// lda value / sta ? / lda isSameAs-value / sta ? -> remove second lda (third line)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
|
||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
||||
(seventh.startsWith("jsr floats.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
||||
|
||||
val nineth = lines[8].value.trimStart()
|
||||
val tenth = lines[9].value.trimStart()
|
||||
val eleventh = lines[10].value.trimStart()
|
||||
val twelveth = lines[11].value.trimStart()
|
||||
val thirteenth = lines[12].value.trimStart()
|
||||
val fourteenth = lines[13].value.trimStart()
|
||||
|
||||
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
||||
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
||||
(fourteenth.startsWith("jsr floats.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
||||
|
||||
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||
// identical float init
|
||||
mods.add(Modification(lines[7].index, true, null))
|
||||
mods.add(Modification(lines[8].index, true, null))
|
||||
mods.add(Modification(lines[9].index, true, null))
|
||||
mods.add(Modification(lines[10].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var overlappingMods = false
|
||||
/*
|
||||
sta prog8_lib.retval_intermX ; remove
|
||||
sty prog8_lib.retval_intermY ; remove
|
||||
lda prog8_lib.retval_intermX ; remove
|
||||
ldy prog8_lib.retval_intermY ; remove
|
||||
sta A1
|
||||
sty A2
|
||||
*/
|
||||
if(first.startsWith("st") && second.startsWith("st")
|
||||
&& third.startsWith("ld") && fourth.startsWith("ld")
|
||||
&& fifth.startsWith("st") && sixth.startsWith("st")) {
|
||||
val reg1 = first[2]
|
||||
val reg2 = second[2]
|
||||
val reg3 = third[2]
|
||||
val reg4 = fourth[2]
|
||||
val reg5 = fifth[2]
|
||||
val reg6 = sixth[2]
|
||||
if (reg1 == reg3 && reg1 == reg5 && reg2 == reg4 && reg2 == reg6) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
val fourthvalue = fourth.substring(4)
|
||||
if(firstvalue.contains("prog8_lib.retval_interm") && secondvalue.contains("prog8_lib.retval_interm")
|
||||
&& firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
overlappingMods = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
sta A1
|
||||
sty A2
|
||||
lda A1 ; can be removed
|
||||
ldy A2 ; can be removed if not followed by a branch instuction
|
||||
*/
|
||||
if(!overlappingMods && first.startsWith("st") && second.startsWith("st")
|
||||
&& third.startsWith("ld") && fourth.startsWith("ld")) {
|
||||
val reg1 = first[2]
|
||||
val reg2 = second[2]
|
||||
val reg3 = third[2]
|
||||
val reg4 = fourth[2]
|
||||
if(reg1==reg3 && reg2==reg4) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
val fourthvalue = fourth.substring(4)
|
||||
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
if(!fifth.startsWith('b'))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
sta A1
|
||||
sty A2
|
||||
lda A1 ; can be removed if not followed by a branch instruction
|
||||
*/
|
||||
if(!overlappingMods && first.startsWith("st") && second.startsWith("st")
|
||||
&& third.startsWith("ld") && !fourth.startsWith("b")) {
|
||||
val reg1 = first[2]
|
||||
val reg3 = third[2]
|
||||
if(reg1==reg3) {
|
||||
val firstvalue = first.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
if(firstvalue==thirdvalue) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
sta A1
|
||||
<other st/ld instruction not involving A1>
|
||||
sta A1 ; can be removed
|
||||
*/
|
||||
if(!overlappingMods && first.startsWith("st") && third.startsWith("st")) {
|
||||
val reg1 = first[2]
|
||||
val reg3 = third[2]
|
||||
if(reg1==reg3 && (second.startsWith("ld") || second.startsWith("st"))) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
if(firstvalue==thirdvalue && firstvalue!=secondvalue) {
|
||||
overlappingMods = true
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
sta A1
|
||||
ldy A1 ; make tay
|
||||
sta A1 ; remove
|
||||
*/
|
||||
if(!overlappingMods && first.startsWith("sta") && second.startsWith("ld")
|
||||
&& third.startsWith("sta") && second.length>4) {
|
||||
val firstvalue = first.substring(4)
|
||||
val secondvalue = second.substring(4)
|
||||
val thirdvalue = third.substring(4)
|
||||
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
|
||||
overlappingMods = true
|
||||
val reg2 = second[2]
|
||||
mods.add(Modification(lines[1].index, false, " ta$reg2"))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||
// TODO this is not true if X is not a regular RAM memory address (but instead mapped I/O or ROM) but how does this code know?
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
val first = lines[1].value.trimStart()
|
||||
val second = lines[2].value.trimStart()
|
||||
|
||||
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
|
||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
|
||||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
|
||||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("stx ") && second.startsWith("ldx "))
|
||||
) {
|
||||
val third = lines[3].value.trimStart()
|
||||
val attemptRemove =
|
||||
if(third.startsWith("b")) {
|
||||
// a branch instruction follows, we can only remove the load instruction if
|
||||
// another load instruction of the same register precedes the store instruction
|
||||
// (otherwise wrong cpu flags are used)
|
||||
val loadinstruction = second.substring(0, 3)
|
||||
lines[0].value.trimStart().startsWith(loadinstruction)
|
||||
}
|
||||
else {
|
||||
// no branch instruction follows, we can remove the load instruction
|
||||
true
|
||||
}
|
||||
|
||||
if(attemptRemove) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc)
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
val first = lines[0].value
|
||||
val second = lines[1].value
|
||||
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|
||||
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|
||||
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|
||||
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)) {
|
||||
mods.add(Modification(lines[0].index, true, null))
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (lines in linesByFour) {
|
||||
val first = lines[0].value
|
||||
val second = lines[1].value
|
||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,792 @@
|
||||
package prog8.compiler.target.cpu6502.codegen
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.toHex
|
||||
import prog8.compiler.target.AssemblyError
|
||||
import prog8.compilerinterface.BuiltinFunctions
|
||||
import prog8.compilerinterface.CpuType
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
|
||||
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||
internal fun translateExpression(expression:Expression) {
|
||||
if (this.asmgen.options.slowCodegenWarnings) {
|
||||
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position)
|
||||
}
|
||||
translateExpressionInternal(expression)
|
||||
}
|
||||
|
||||
|
||||
// the rest of the methods are all PRIVATE
|
||||
|
||||
|
||||
private fun translateExpressionInternal(expression: Expression) {
|
||||
|
||||
when(expression) {
|
||||
is PrefixExpression -> translateExpression(expression)
|
||||
is BinaryExpression -> translateExpression(expression)
|
||||
is ArrayIndexedExpression -> translateExpression(expression)
|
||||
is TypecastExpression -> translateExpression(expression)
|
||||
is AddressOf -> translateExpression(expression)
|
||||
is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
|
||||
is NumericLiteralValue -> translateExpression(expression)
|
||||
is IdentifierReference -> translateExpression(expression)
|
||||
is FunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateFunctionCallResultOntoStack(call: FunctionCall) {
|
||||
// only for use in nested expression evaluation
|
||||
|
||||
val sub = call.target.targetStatement(program)
|
||||
if(sub is BuiltinFunctionStatementPlaceholder) {
|
||||
val builtinFunc = BuiltinFunctions.getValue(sub.name)
|
||||
asmgen.translateBuiltinFunctionCallExpression(call, builtinFunc, true, null)
|
||||
} else {
|
||||
sub as Subroutine
|
||||
asmgen.saveXbeforeCall(call)
|
||||
asmgen.translateFunctionCall(call)
|
||||
if(sub.regXasResult()) {
|
||||
// store the return value in X somewhere that we can acces again below
|
||||
asmgen.out(" stx P8ZP_SCRATCH_REG")
|
||||
}
|
||||
asmgen.restoreXafterCall(call)
|
||||
|
||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||
for ((_, reg) in returns) {
|
||||
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
|
||||
if (reg.registerOrPair != null) {
|
||||
when (reg.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
|
||||
RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
|
||||
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
RegisterOrPair.R2,
|
||||
RegisterOrPair.R3,
|
||||
RegisterOrPair.R4,
|
||||
RegisterOrPair.R5,
|
||||
RegisterOrPair.R6,
|
||||
RegisterOrPair.R7,
|
||||
RegisterOrPair.R8,
|
||||
RegisterOrPair.R9,
|
||||
RegisterOrPair.R10,
|
||||
RegisterOrPair.R11,
|
||||
RegisterOrPair.R12,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
asmgen.out(
|
||||
"""
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
}
|
||||
}
|
||||
} else when(reg.statusflag) {
|
||||
Statusflag.Pc -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
adc #0
|
||||
sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pz -> {
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pv -> {
|
||||
asmgen.out("""
|
||||
bvs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pn -> {
|
||||
asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(typecast: TypecastExpression) {
|
||||
translateExpressionInternal(typecast.expression)
|
||||
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when(typecast.type) {
|
||||
DataType.BYTE, DataType.UBYTE -> {}
|
||||
DataType.WORD, DataType.UWORD -> {}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(typecast.type) {
|
||||
DataType.BYTE, DataType.UBYTE -> {}
|
||||
DataType.WORD, DataType.UWORD -> {}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
|
||||
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
|
||||
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
|
||||
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
|
||||
DataType.FLOAT -> {}
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.STR -> {
|
||||
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
|
||||
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
||||
}
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: AddressOf) {
|
||||
val name = asmgen.asmVariableName(expr.identifier)
|
||||
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: NumericLiteralValue) {
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||
lda #<${expr.number.toHex()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda #>${expr.number.toHex()}
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
DataType.FLOAT -> {
|
||||
val floatConst = asmgen.getFloatAsmConst(expr.number.toDouble())
|
||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: IdentifierReference) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
when(expr.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
||||
}
|
||||
in IterableDatatypes -> {
|
||||
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: BinaryExpression) {
|
||||
// Uses evalstack to evaluate the given expression.
|
||||
// TODO we're slowly reducing the number of places where this is called and instead replace that by more efficient assignment-form code (using temp var or register for instance).
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
val rightIDt = expr.right.inferType(program)
|
||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||
throw AssemblyError("can't infer type of both expression operands")
|
||||
|
||||
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
|
||||
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
|
||||
// see if we can apply some optimized routines
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val leftVal = expr.left.constValue(program)?.number?.toInt()
|
||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||
if (leftVal!=null && leftVal in -4..4) {
|
||||
translateExpressionInternal(expr.right)
|
||||
if(rightDt in ByteDatatypes) {
|
||||
val incdec = if(leftVal<0) "dec" else "inc"
|
||||
repeat(leftVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(leftVal<0) {
|
||||
repeat(leftVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(leftVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
else if (rightVal!=null && rightVal in -4..4)
|
||||
{
|
||||
translateExpressionInternal(expr.left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
val incdec = if(rightVal<0) "dec" else "inc"
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(rightVal<0) {
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(rightVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||
if (rightVal!=null && rightVal in -4..4)
|
||||
{
|
||||
translateExpressionInternal(expr.left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
val incdec = if(rightVal<0) "inc" else "dec"
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(rightVal>0) {
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(rightVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
">>" -> {
|
||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||
if(amount!=null) {
|
||||
translateExpressionInternal(expr.left)
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" lsr a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
|
||||
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount>=16) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||
return
|
||||
}
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_uw_7")
|
||||
left -= 7
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount>=16) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi +
|
||||
lda #0
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
beq ++
|
||||
+ lda #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
+""")
|
||||
return
|
||||
}
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_w_7")
|
||||
left -= 7
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w_$left")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
"<<" -> {
|
||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||
if(amount!=null) {
|
||||
translateExpressionInternal(expr.left)
|
||||
if (leftDt in ByteDatatypes) {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" asl a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_left_w_7")
|
||||
left -= 7
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_left_w_$left")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val leftVar = expr.left as? IdentifierReference
|
||||
val rightVar = expr.right as? IdentifierReference
|
||||
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
|
||||
return translateSquared(leftVar, leftDt)
|
||||
}
|
||||
|
||||
val value = expr.right.constValue(program)
|
||||
if(value!=null) {
|
||||
if(rightDt in IntegerDatatypes) {
|
||||
val amount = value.number.toInt()
|
||||
if(amount==2) {
|
||||
// optimize x*2 common case
|
||||
translateExpressionInternal(expr.left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
||||
}
|
||||
return
|
||||
}
|
||||
when(rightDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(expr.left)
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(expr.left)
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return
|
||||
}
|
||||
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(expr.left)
|
||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||
return
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(expr.left)
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(expr.left)
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return
|
||||
}
|
||||
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(expr.left)
|
||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||
if(rightVal!=null && rightVal==2) {
|
||||
translateExpressionInternal(expr.left)
|
||||
when(leftDt) {
|
||||
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||
DataType.BYTE -> asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x")
|
||||
DataType.UWORD -> asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
||||
DataType.WORD -> asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
||||
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
||||
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
||||
|
||||
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in comparisonOperators) {
|
||||
translateCompareStrings(expr.left, expr.operator, expr.right)
|
||||
}
|
||||
else {
|
||||
// the general, non-optimized cases TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
||||
translateExpressionInternal(expr.left)
|
||||
translateExpressionInternal(expr.right)
|
||||
when (leftDt) {
|
||||
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||
else -> throw AssemblyError("non-numerical datatype")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateSquared(variable: IdentifierReference, dt: DataType) {
|
||||
val asmVar = asmgen.asmVariableName(variable)
|
||||
when(dt) {
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" lda $asmVar")
|
||||
asmgen.signExtendAYlsb(dt)
|
||||
asmgen.out(" jsr math.square")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out(" lda $asmVar | ldy $asmVar+1 | jsr math.square")
|
||||
}
|
||||
else -> throw AssemblyError("require integer dt for square")
|
||||
}
|
||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PrefixExpression) {
|
||||
translateExpressionInternal(expr.expression)
|
||||
val itype = expr.inferType(program)
|
||||
if(!itype.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val type = itype.getOr(DataType.UNDEFINED)
|
||||
when(expr.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
when(type) {
|
||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
when(type) {
|
||||
in ByteDatatypes ->
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
eor #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
"not" -> {
|
||||
when(type) {
|
||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.not_byte")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.not_word")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
|
||||
val elementIDt = arrayExpr.inferType(program)
|
||||
if(!elementIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
|
||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
||||
val constIndexNum = arrayExpr.indexer.constIndex()
|
||||
if(constIndexNum!=null) {
|
||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr floats.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird element type")
|
||||
}
|
||||
} else {
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>$arrayVarName
|
||||
clc
|
||||
adc #<$arrayVarName
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.push_float""")
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||
when(operator) {
|
||||
"**" -> throw AssemblyError("** operator requires floats")
|
||||
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
|
||||
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
||||
"%" -> {
|
||||
if(types==DataType.BYTE)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||
}
|
||||
"+" -> asmgen.out("""
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"-" -> asmgen.out("""
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
|
||||
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
|
||||
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
||||
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
|
||||
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
|
||||
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
|
||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
|
||||
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
|
||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
|
||||
"and" -> asmgen.out(" jsr prog8_lib.and_b")
|
||||
"or" -> asmgen.out(" jsr prog8_lib.or_b")
|
||||
"xor" -> asmgen.out(" jsr prog8_lib.xor_b")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
|
||||
when(operator) {
|
||||
"**" -> throw AssemblyError("** operator requires floats")
|
||||
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
||||
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||
"%" -> {
|
||||
if(dt==DataType.WORD)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||
}
|
||||
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||
"<<" -> asmgen.out(" jsr math.shift_left_w")
|
||||
">>" -> {
|
||||
if(dt==DataType.UWORD)
|
||||
asmgen.out(" jsr math.shift_right_uw")
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w")
|
||||
}
|
||||
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
|
||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
|
||||
"and" -> asmgen.out(" jsr prog8_lib.and_w")
|
||||
"or" -> asmgen.out(" jsr prog8_lib.or_w")
|
||||
"xor" -> asmgen.out(" jsr prog8_lib.xor_w")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorFloats(operator: String) {
|
||||
when(operator) {
|
||||
"**" -> asmgen.out(" jsr floats.pow_f")
|
||||
"*" -> asmgen.out(" jsr floats.mul_f")
|
||||
"/" -> asmgen.out(" jsr floats.div_f")
|
||||
"+" -> asmgen.out(" jsr floats.add_f")
|
||||
"-" -> asmgen.out(" jsr floats.sub_f")
|
||||
"<" -> asmgen.out(" jsr floats.less_f")
|
||||
">" -> asmgen.out(" jsr floats.greater_f")
|
||||
"<=" -> asmgen.out(" jsr floats.lesseq_f")
|
||||
">=" -> asmgen.out(" jsr floats.greatereq_f")
|
||||
"==" -> asmgen.out(" jsr floats.equal_f")
|
||||
"!=" -> asmgen.out(" jsr floats.notequal_f")
|
||||
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateCompareStrings(s1: Expression, operator: String, s2: Expression) {
|
||||
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
|
||||
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
|
||||
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||
when(operator) {
|
||||
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
||||
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
||||
"<=" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
">=" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
"<" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
">" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
}
|
||||
asmgen.out(" dex")
|
||||
}
|
||||
}
|
@ -0,0 +1,593 @@
|
||||
package prog8.compiler.target.cpu6502.codegen
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ArrayToElementTypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.RegisterOrPair
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.RangeExpr
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.toHex
|
||||
import prog8.compiler.target.AssemblyError
|
||||
import prog8.compilerinterface.toConstantIntegerRange
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
|
||||
internal fun translate(stmt: ForLoop) {
|
||||
val iterableDt = stmt.iterable.inferType(program)
|
||||
if(!iterableDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
when(stmt.iterable) {
|
||||
is RangeExpr -> {
|
||||
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||
if(range==null) {
|
||||
translateForOverNonconstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), stmt.iterable as RangeExpr)
|
||||
} else {
|
||||
translateForOverConstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), range)
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
translateForOverIterableVar(stmt, iterableDt.getOr(DataType.UNDEFINED), stmt.iterable as IdentifierReference)
|
||||
}
|
||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||
|
||||
if(stepsize < -1) {
|
||||
val limit = range.to.constValue(program)?.number?.toDouble()
|
||||
if(limit==0.0)
|
||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||
}
|
||||
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
if (stepsize==1 || stepsize==-1) {
|
||||
|
||||
// bytes array, step 1 or -1
|
||||
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
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
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bmi $loopLabel
|
||||
beq $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bpl $loopLabel""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
when {
|
||||
|
||||
// words, step 1 or -1
|
||||
|
||||
stepsize == 1 || stepsize == -1 -> {
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
assignLoopvar(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
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)
|
||||
}
|
||||
stepsize > 0 -> {
|
||||
|
||||
// (u)words, step >= 2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
assignLoopvar(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
|
||||
if (iterableDt == DataType.ARRAY_UW) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
sta $varname
|
||||
lda $varname+1
|
||||
adc #>$stepsize
|
||||
sta $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcc $loopLabel
|
||||
bne $endLabel
|
||||
$modifiedLabel2 lda #0 ; modified
|
||||
cmp $varname
|
||||
bcc $endLabel
|
||||
bcs $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
sta $varname
|
||||
lda $varname+1
|
||||
adc #>$stepsize
|
||||
sta $varname+1
|
||||
$modifiedLabel2 lda #0 ; modified
|
||||
cmp $varname
|
||||
$modifiedLabel lda #0 ; modified
|
||||
sbc $varname+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
||||
// (u)words, step <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
assignLoopvar(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
|
||||
if(iterableDt==DataType.ARRAY_UW) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
lda $varname+1
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcc $endLabel
|
||||
bne $loopLabel
|
||||
lda $varname
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
bcs $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
pha
|
||||
lda $varname+1
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
pla
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
lda $varname+1
|
||||
$modifiedLabel sbc #0 ; modified
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val decl = ident.targetVarDecl(program)!!
|
||||
when(iterableDt) {
|
||||
DataType.STR -> {
|
||||
asmgen.out("""
|
||||
lda #<$iterableName
|
||||
ldy #>$iterableName
|
||||
sta $loopLabel+1
|
||||
sty $loopLabel+2
|
||||
$loopLabel lda ${65535.toHex()} ; modified
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
inc $loopLabel+1
|
||||
bne $loopLabel
|
||||
inc $loopLabel+2
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
val length = decl.arraysize!!.constIndex()!!
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||
asmgen.translate(stmt.body)
|
||||
if(length<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$length
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
$indexVar .byte 0""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
val length = decl.arraysize!!.constIndex()!! * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta $loopvarName
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.body)
|
||||
if(length<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #$length
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
$indexVar .byte 0""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
throw AssemblyError("for loop with floating point variables is not supported")
|
||||
}
|
||||
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||
if (range.isEmpty() || range.step==0)
|
||||
throw AssemblyError("empty range or step 0")
|
||||
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
||||
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
||||
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range)
|
||||
}
|
||||
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
|
||||
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
||||
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
|
||||
}
|
||||
|
||||
// not one of the easy cases, generate more complex code...
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
}
|
||||
2 -> {
|
||||
if(range.last==255 || range.last==254) {
|
||||
asmgen.out("""
|
||||
inc $varname
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
inc $varname
|
||||
inc $varname
|
||||
lda $varname
|
||||
cmp #${range.last+2}
|
||||
bne $loopLabel""")
|
||||
}
|
||||
}
|
||||
-2 -> {
|
||||
when (range.last) {
|
||||
0 -> {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
1 -> asmgen.out("""
|
||||
dec $varname
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
else -> asmgen.out("""
|
||||
dec $varname
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #${range.last-2}
|
||||
bne $loopLabel""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// step <= -3 or >= 3
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #${range.last}
|
||||
beq $endLabel
|
||||
clc
|
||||
adc #${range.step}
|
||||
sta $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
// loop over word range via loopvar, step >= 2 or <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
}
|
||||
else -> {
|
||||
// word, step >= 2 or <= -2
|
||||
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
bne +
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
clc
|
||||
adc #<${range.step}
|
||||
sta $varname
|
||||
lda $varname+1
|
||||
adc #>${range.step}
|
||||
sta $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
if (range.last == 255) {
|
||||
asmgen.out("""
|
||||
inc $varname
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
inc $varname
|
||||
lda $varname
|
||||
cmp #${range.last+1}
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
when (range.last) {
|
||||
0 -> {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
beq $endLabel
|
||||
dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #${range.last-1}
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
beq $endLabel
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
||||
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).getOr(DataType.UNDEFINED), stmt.definingSubroutine)
|
||||
}
|
@ -0,0 +1,372 @@
|
||||
package prog8.compiler.target.cpu6502.codegen
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.InlineAssembly
|
||||
import prog8.ast.statements.RegisterOrStatusflag
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.SubroutineParameter
|
||||
import prog8.compiler.target.AssemblyError
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
|
||||
import prog8.compilerinterface.CpuType
|
||||
|
||||
|
||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
|
||||
internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
|
||||
saveXbeforeCall(stmt)
|
||||
translateFunctionCall(stmt)
|
||||
restoreXafterCall(stmt)
|
||||
// just ignore any result values from the function call.
|
||||
}
|
||||
|
||||
internal fun saveXbeforeCall(stmt: IFunctionCall) {
|
||||
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||
if(sub.shouldSaveX()) {
|
||||
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||
if(regSaveOnStack)
|
||||
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
|
||||
else
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreXafterCall(stmt: IFunctionCall) {
|
||||
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||
if(sub.shouldSaveX()) {
|
||||
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||
if(regSaveOnStack)
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
|
||||
else
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
||||
// Output only the code to set up the parameters and perform the actual call
|
||||
// NOTE: does NOT output the code to deal with the result values!
|
||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||
|
||||
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||
val subName = asmgen.asmSymbolName(stmt.target)
|
||||
if(stmt.args.isNotEmpty()) {
|
||||
|
||||
if(sub.asmParameterRegisters.isEmpty()) {
|
||||
// via variables
|
||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||
argumentViaVariable(sub, arg.first, arg.second)
|
||||
}
|
||||
} else {
|
||||
require(sub.isAsmSubroutine)
|
||||
if(sub.parameters.size==1) {
|
||||
// just a single parameter, no risk of clobbering registers
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
|
||||
} else {
|
||||
|
||||
fun isNoClobberRisk(expr: Expression): Boolean {
|
||||
if(expr is AddressOf ||
|
||||
expr is NumericLiteralValue ||
|
||||
expr is StringLiteralValue ||
|
||||
expr is ArrayLiteralValue ||
|
||||
expr is IdentifierReference)
|
||||
return true
|
||||
|
||||
if(expr is FunctionCall) {
|
||||
if(expr.target.nameInSource==listOf("lsb") || expr.target.nameInSource==listOf("msb"))
|
||||
return isNoClobberRisk(expr.args[0])
|
||||
if(expr.target.nameInSource==listOf("mkword"))
|
||||
return isNoClobberRisk(expr.args[0]) && isNoClobberRisk(expr.args[1])
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
when {
|
||||
stmt.args.all {isNoClobberRisk(it)} -> {
|
||||
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
||||
// register assignment order: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag.
|
||||
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
||||
val (cx16virtualRegs, args2) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
||||
val (cpuRegs, statusRegs) = args2.partition { it.second.registerOrPair!=null }
|
||||
for(arg in cx16virtualRegs)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
for(arg in cpuRegs)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
for(arg in statusRegs)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
}
|
||||
else -> {
|
||||
// Risk of clobbering due to complex expression args. Evaluate first, then assign registers.
|
||||
registerArgsViaStackEvaluation(stmt, sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!sub.inline || !asmgen.options.optimize) {
|
||||
asmgen.out(" jsr $subName")
|
||||
} else {
|
||||
// inline the subroutine.
|
||||
// we do this by copying the subroutine's statements at the call site.
|
||||
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
|
||||
// (this condition has been enforced by an ast check earlier)
|
||||
|
||||
// note: for now, this is only reliably supported for asmsubs.
|
||||
if(!sub.isAsmSubroutine)
|
||||
throw AssemblyError("can only reliably inline asmsub routines at this time")
|
||||
|
||||
asmgen.out(" \t; inlined routine follows: ${sub.name}")
|
||||
val assembly = sub.statements.single() as InlineAssembly
|
||||
asmgen.translate(assembly)
|
||||
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||
}
|
||||
|
||||
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||
}
|
||||
|
||||
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||
// this is called when one or more of the arguments are 'complex' and
|
||||
// cannot be assigned to a register easily or risk clobbering other registers.
|
||||
// TODO find another way to prepare the arguments, without using the eval stack
|
||||
|
||||
if(sub.parameters.isEmpty())
|
||||
return
|
||||
|
||||
|
||||
// 1. load all arguments reversed onto the stack: first arg goes last (is on top).
|
||||
|
||||
for (arg in stmt.args.reversed())
|
||||
asmgen.translateExpression(arg)
|
||||
|
||||
// TODO here's an alternative to the above, but for now generates bigger code due to intermediate register steps:
|
||||
// for (arg in stmt.args.reversed()) {
|
||||
// // note this stuff below is needed to (eventually) avoid calling asmgen.translateExpression()
|
||||
// // TODO also This STILL requires the translateNormalAssignment() to be fixed to avoid stack eval for expressions...
|
||||
// val dt = arg.inferType(program).getOr(DataType.UNDEFINED)
|
||||
// asmgen.assignExpressionTo(arg, AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, dt, sub))
|
||||
// }
|
||||
|
||||
var argForCarry: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
|
||||
asmgen.out(" inx") // align estack pointer
|
||||
|
||||
for(argi in stmt.args.zip(sub.asmParameterRegisters).withIndex()) {
|
||||
val plusIdxStr = if(argi.index==0) "" else "+${argi.index}"
|
||||
when {
|
||||
argi.value.second.statusflag == Statusflag.Pc -> {
|
||||
require(argForCarry == null)
|
||||
argForCarry = argi
|
||||
}
|
||||
argi.value.second.statusflag != null -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||
argi.value.second.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
|
||||
require(argForXregister==null)
|
||||
argForXregister = argi
|
||||
}
|
||||
argi.value.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
|
||||
require(argForAregister == null)
|
||||
argForAregister = argi
|
||||
}
|
||||
argi.value.second.registerOrPair == RegisterOrPair.Y -> {
|
||||
asmgen.out(" ldy P8ESTACK_LO$plusIdxStr,x")
|
||||
}
|
||||
argi.value.second.registerOrPair in Cx16VirtualRegisters -> {
|
||||
// immediately output code to load the virtual register, to avoid clobbering the A register later
|
||||
when (sub.parameters[argi.index].type) {
|
||||
in ByteDatatypes -> {
|
||||
// only load the lsb of the virtual register
|
||||
asmgen.out(
|
||||
"""
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||
""")
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1")
|
||||
else
|
||||
asmgen.out(
|
||||
" lda #0 | sta cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1")
|
||||
}
|
||||
in WordDatatypes, in IterableDatatypes ->
|
||||
asmgen.out(
|
||||
"""
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||
lda P8ESTACK_HI$plusIdxStr,x
|
||||
sta cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1
|
||||
""")
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird argument")
|
||||
}
|
||||
}
|
||||
|
||||
if(argForCarry!=null) {
|
||||
val plusIdxStr = if(argForCarry.index==0) "" else "+${argForCarry.index}"
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+ php""") // push the status flags
|
||||
}
|
||||
|
||||
if(argForAregister!=null) {
|
||||
val plusIdxStr = if(argForAregister.index==0) "" else "+${argForAregister.index}"
|
||||
when(argForAregister.value.second.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda P8ESTACK_LO$plusIdxStr,x")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda P8ESTACK_LO$plusIdxStr,x | ldy P8ESTACK_HI$plusIdxStr,x")
|
||||
else -> throw AssemblyError("weird arg")
|
||||
}
|
||||
}
|
||||
|
||||
if(argForXregister!=null) {
|
||||
val plusIdxStr = if(argForXregister.index==0) "" else "+${argForXregister.index}"
|
||||
|
||||
if(argForAregister!=null)
|
||||
asmgen.out(" pha")
|
||||
when(argForXregister.value.second.registerOrPair) {
|
||||
RegisterOrPair.X -> asmgen.out(" lda P8ESTACK_LO$plusIdxStr,x | tax")
|
||||
RegisterOrPair.AX -> asmgen.out(" ldy P8ESTACK_LO$plusIdxStr,x | lda P8ESTACK_HI$plusIdxStr,x | tax | tya")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy P8ESTACK_HI$plusIdxStr,x | lda P8ESTACK_LO$plusIdxStr,x | tax")
|
||||
else -> throw AssemblyError("weird arg")
|
||||
}
|
||||
if(argForAregister!=null)
|
||||
asmgen.out(" pla")
|
||||
} else {
|
||||
repeat(sub.parameters.size - 1) { asmgen.out(" inx") } // unwind stack
|
||||
}
|
||||
|
||||
if(argForCarry!=null)
|
||||
asmgen.out(" plp") // set the carry flag back to correct value
|
||||
}
|
||||
|
||||
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||
// pass parameter via a regular variable (not via registers)
|
||||
val valueIDt = value.inferType(program)
|
||||
if(!valueIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val valueDt = valueIDt.getOr(DataType.UNDEFINED)
|
||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
val varName = asmgen.asmVariableName(sub.scopedname+"."+parameter.value.name)
|
||||
asmgen.assignExpressionToVariable(value, varName, parameter.value.type, sub)
|
||||
}
|
||||
|
||||
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||
// pass argument via a register parameter
|
||||
val valueIDt = value.inferType(program)
|
||||
if(!valueIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val valueDt = valueIDt.getOr(DataType.UNDEFINED)
|
||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||
val statusflag = paramRegister.statusflag
|
||||
val register = paramRegister.registerOrPair
|
||||
val requiredDt = parameter.value.type
|
||||
if(requiredDt!=valueDt) {
|
||||
if(valueDt largerThan requiredDt)
|
||||
throw AssemblyError("can only convert byte values to word param types")
|
||||
}
|
||||
if (statusflag!=null) {
|
||||
if(requiredDt!=valueDt)
|
||||
throw AssemblyError("for statusflag, byte value is required")
|
||||
if (statusflag == Statusflag.Pc) {
|
||||
// this param needs to be set last, right before the jsr
|
||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||
when(value) {
|
||||
is NumericLiteralValue -> {
|
||||
val carrySet = value.number.toInt() != 0
|
||||
asmgen.out(if(carrySet) " sec" else " clc")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmVariableName(value)
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda $sourceName
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+ pla
|
||||
""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+""")
|
||||
}
|
||||
}
|
||||
} else throw AssemblyError("can only use Carry as status flag parameter")
|
||||
}
|
||||
else {
|
||||
// via register or register pair
|
||||
register!!
|
||||
if(requiredDt largerThan valueDt) {
|
||||
// we need to sign extend the source, do this via temporary word variable
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
|
||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt)
|
||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
|
||||
} else {
|
||||
val target: AsmAssignTarget =
|
||||
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register)
|
||||
else
|
||||
AsmAssignTarget.fromRegisters(register, false, sub, program, asmgen)
|
||||
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||
if(value is IdentifierReference) {
|
||||
val addr = AddressOf(value, Position.DUMMY)
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
}
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
}
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
if(argType isAssignableTo paramType)
|
||||
return true
|
||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||
return true
|
||||
if(argType in WordDatatypes && paramType in WordDatatypes)
|
||||
return true
|
||||
|
||||
// we have a special rule for some types.
|
||||
// strings are assignable to UWORD, for example, and vice versa
|
||||
if(argType==DataType.STR && paramType==DataType.UWORD)
|
||||
return true
|
||||
if(argType==DataType.UWORD && paramType == DataType.STR)
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package prog8.compiler.target.cpu6502.codegen
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.PostIncrDecr
|
||||
import prog8.ast.toHex
|
||||
import prog8.compiler.target.AssemblyError
|
||||
|
||||
|
||||
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
internal fun translate(stmt: PostIncrDecr) {
|
||||
val incr = stmt.operator=="++"
|
||||
val targetIdent = stmt.target.identifier
|
||||
val targetMemory = stmt.target.memoryAddress
|
||||
val targetArrayIdx = stmt.target.arrayindexed
|
||||
val scope = stmt.definingSubroutine
|
||||
when {
|
||||
targetIdent!=null -> {
|
||||
val what = asmgen.asmVariableName(targetIdent)
|
||||
when (stmt.target.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $what | bne + | inc $what+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $what
|
||||
bne +
|
||||
dec $what+1
|
||||
+ dec $what
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$what | ldy #>$what")
|
||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("need numeric type")
|
||||
}
|
||||
}
|
||||
targetMemory!=null -> {
|
||||
when (val addressExpr = targetMemory.addressExpression) {
|
||||
is NumericLiteralValue -> {
|
||||
val what = addressExpr.number.toHex()
|
||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val what = asmgen.asmVariableName(addressExpr)
|
||||
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(addressExpr, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
}
|
||||
}
|
||||
targetArrayIdx!=null -> {
|
||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
||||
val elementDt = targetArrayIdx.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val constIndex = targetArrayIdx.indexer.constIndex()
|
||||
if(constIndex!=null) {
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname+$indexValue
|
||||
bne +
|
||||
dec $asmArrayvarname+$indexValue+1
|
||||
+ dec $asmArrayvarname+$indexValue
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("need numeric type")
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||
asmgen.out(" tax")
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname,x
|
||||
bne +
|
||||
dec $asmArrayvarname+1,x
|
||||
+ dec $asmArrayvarname,x
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
ldy #>$asmArrayvarname
|
||||
clc
|
||||
adc #<$asmArrayvarname
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.inc_var_f""")
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
package prog8.compiler.target.cpu6502.codegen.assignment
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compilerinterface.IMemSizer
|
||||
import prog8.compiler.target.AssemblyError
|
||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
LITERALNUMBER,
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK, // value is already present on stack
|
||||
EXPRESSION, // expression in ast-form, still to be evaluated
|
||||
}
|
||||
|
||||
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
private val program: Program,
|
||||
private val asmgen: AsmGen,
|
||||
val datatype: DataType,
|
||||
val scope: Subroutine?,
|
||||
private val variableAsmName: String? = null,
|
||||
val array: ArrayIndexedExpression? = null,
|
||||
val memory: DirectMemoryWrite? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val origAstTarget: AssignTarget? = null
|
||||
)
|
||||
{
|
||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.arrayvar)
|
||||
}
|
||||
|
||||
lateinit var origAssign: AsmAssignment
|
||||
|
||||
init {
|
||||
if(register!=null && datatype !in NumericDatatypes)
|
||||
throw AssemblyError("register must be integer or float type")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
|
||||
val idt = inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val dt = idt.getOr(DataType.UNDEFINED)
|
||||
when {
|
||||
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this)
|
||||
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
|
||||
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
||||
when(registers) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
||||
RegisterOrPair.FAC1,
|
||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
RegisterOrPair.R2,
|
||||
RegisterOrPair.R3,
|
||||
RegisterOrPair.R4,
|
||||
RegisterOrPair.R5,
|
||||
RegisterOrPair.R6,
|
||||
RegisterOrPair.R7,
|
||||
RegisterOrPair.R8,
|
||||
RegisterOrPair.R9,
|
||||
RegisterOrPair.R10,
|
||||
RegisterOrPair.R11,
|
||||
RegisterOrPair.R12,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
private val program: Program,
|
||||
private val asmgen: AsmGen,
|
||||
val datatype: DataType,
|
||||
private val variableAsmName: String? = null,
|
||||
val array: ArrayIndexedExpression? = null,
|
||||
val memory: DirectMemoryRead? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val number: NumericLiteralValue? = null,
|
||||
val expression: Expression? = null
|
||||
)
|
||||
{
|
||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
||||
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
else
|
||||
asmgen.asmVariableName(array.arrayvar)
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
|
||||
|
||||
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
||||
val cv = value.constValue(program)
|
||||
if(cv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||
|
||||
return when(value) {
|
||||
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, value.type, number = cv)
|
||||
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||
is IdentifierReference -> {
|
||||
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val varName=asmgen.asmVariableName(value)
|
||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||
if(dt == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
||||
val regStr = varName.lowercase().substring(5)
|
||||
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
|
||||
} else {
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
|
||||
}
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
||||
}
|
||||
is FunctionCall -> {
|
||||
when (val sub = value.target.targetStatement(program)) {
|
||||
is Subroutine -> {
|
||||
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
|
||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val returnType = value.inferType(program)
|
||||
if(!returnType.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOr(DataType.UNDEFINED), expression = value)
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird call")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val dt = value.inferType(program)
|
||||
if(!dt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.getOr(DataType.UNDEFINED), expression = value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun adjustSignedUnsigned(target: AsmAssignTarget): AsmAssignSource {
|
||||
// allow some signed/unsigned relaxations
|
||||
|
||||
fun withAdjustedDt(newType: DataType) =
|
||||
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
|
||||
|
||||
if(target.datatype!=datatype) {
|
||||
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
||||
return withAdjustedDt(target.datatype)
|
||||
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
|
||||
return withAdjustedDt(target.datatype)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class AsmAssignment(val source: AsmAssignSource,
|
||||
val target: AsmAssignTarget,
|
||||
val isAugmentable: Boolean,
|
||||
memsizer: IMemSizer,
|
||||
val position: Position) {
|
||||
|
||||
init {
|
||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
|
||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||
"source dt size must be less or equal to target dt size at $position"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,124 @@
|
||||
package prog8.compiler.target.cx16
|
||||
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.cbm.viceMonListPostfix
|
||||
import prog8.compilerinterface.*
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
object CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU65c02
|
||||
|
||||
// 5-byte cbm MFLPT format limitations:
|
||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||
override val FLOAT_MEM_SIZE = 5
|
||||
override val POINTER_MEM_SIZE = 2
|
||||
override val BASIC_LOAD_ADDRESS = 0x0801
|
||||
override val RAW_LOAD_ADDRESS = 0x8000
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
override val ESTACK_LO = 0x0400 // $0400-$04ff inclusive
|
||||
override val ESTACK_HI = 0x0500 // $0500-$05ff inclusive
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
val emulatorName: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
when(selectedEmulator) {
|
||||
1 -> {
|
||||
emulatorName = "x16emu"
|
||||
extraArgs = emptyList()
|
||||
}
|
||||
2 -> {
|
||||
emulatorName = "box16"
|
||||
extraArgs = listOf("-sym", "${programNameWithPath}.$viceMonListPostfix")
|
||||
}
|
||||
else -> {
|
||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for(emulator in listOf(emulatorName)) {
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(x: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun isRegularRAMaddress(address: Int): Boolean = address < 0x9f00 || address in 0xa000..0xbfff
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
}
|
||||
|
||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
|
||||
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
||||
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
|
||||
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
||||
"inc", "inx", "iny", "jmp", "jsr",
|
||||
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
|
||||
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
|
||||
"sec", "sed", "sei",
|
||||
"sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
|
||||
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
|
||||
"rmb", "smb", "stp", "wai")
|
||||
|
||||
|
||||
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x7a // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x7b // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0x7c // temp storage 1 for a word $7c+$7d
|
||||
override val SCRATCH_W2 = 0x7e // temp storage 2 for a word $7e+$7f
|
||||
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
|
||||
|
||||
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x22..0xff)
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x22..0x7f)
|
||||
free.addAll(0xa9..0xff)
|
||||
}
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0x22..0x7f)
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||
}
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
}
|
||||
}
|
173
codeGeneration/test/AsmGenSymbolsTests.kt
Normal file
173
codeGeneration/test/AsmGenSymbolsTests.kt
Normal file
@ -0,0 +1,173 @@
|
||||
package prog8tests.asmgen
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.StringSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.AddressOf
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||
import prog8.compilerinterface.*
|
||||
import prog8.parser.SourceCode
|
||||
import prog8tests.asmgen.helpers.DummyFunctions
|
||||
import prog8tests.asmgen.helpers.DummyMemsizer
|
||||
import prog8tests.asmgen.helpers.DummyStringEncoder
|
||||
import prog8tests.asmgen.helpers.ErrorReporterForTests
|
||||
import java.nio.file.Path
|
||||
|
||||
class AsmGenSymbolsTests: StringSpec({
|
||||
fun createTestProgram(): Program {
|
||||
/*
|
||||
main {
|
||||
|
||||
label_outside:
|
||||
uword var_outside
|
||||
|
||||
sub start () {
|
||||
uword localvar = 1234
|
||||
uword tgt
|
||||
|
||||
locallabel:
|
||||
tgt = localvar
|
||||
tgt = &locallabel
|
||||
tgt = &var_outside
|
||||
tgt = &label_outside
|
||||
tgt = &main.start.localvar
|
||||
tgt = &main.start.locallabel
|
||||
tgt = &main.var_outside
|
||||
tgt = &main.label_outside
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
val varInSub = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteralValue.optimalInteger(1234, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val var2InSub = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, false, Position.DUMMY)
|
||||
val labelInSub = Label("locallabel", Position.DUMMY)
|
||||
|
||||
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
val assign1 = Assignment(tgt, IdentifierReference(listOf("localvar"), Position.DUMMY), Position.DUMMY)
|
||||
val assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val assign5 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","localvar"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val assign7 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","var_outside"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
|
||||
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
|
||||
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements, Position.DUMMY)
|
||||
val labelInBlock = Label("label_outside", Position.DUMMY)
|
||||
val varInBlock = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY)
|
||||
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
|
||||
|
||||
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
|
||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||
.addModule(module)
|
||||
module.linkIntoProgram(program)
|
||||
return program
|
||||
}
|
||||
|
||||
fun createTestAsmGen(program: Program): AsmGen {
|
||||
val errors = ErrorReporterForTests()
|
||||
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target)
|
||||
val zp = C64MachineDefinition.C64Zeropage(options)
|
||||
val asmgen = AsmGen(program, errors, zp, options, C64Target, Path.of(""))
|
||||
return asmgen
|
||||
}
|
||||
|
||||
"symbol and variable names from strings" {
|
||||
val program = createTestProgram()
|
||||
val asmgen = createTestAsmGen(program)
|
||||
asmgen.asmSymbolName("name") shouldBe "name"
|
||||
asmgen.asmSymbolName("name") shouldBe "name"
|
||||
asmgen.asmSymbolName("<name>") shouldBe "prog8_name"
|
||||
asmgen.asmSymbolName(RegisterOrPair.R15) shouldBe "cx16.r15"
|
||||
asmgen.asmSymbolName(listOf("a", "b", "name")) shouldBe "a.b.name"
|
||||
asmgen.asmVariableName("name") shouldBe "name"
|
||||
asmgen.asmVariableName("<name>") shouldBe "prog8_name"
|
||||
asmgen.asmVariableName(listOf("a", "b", "name")) shouldBe "a.b.name"
|
||||
}
|
||||
|
||||
"symbol and variable names from variable identifiers" {
|
||||
val program = createTestProgram()
|
||||
val asmgen = createTestAsmGen(program)
|
||||
val sub = program.entrypoint
|
||||
|
||||
// local variable
|
||||
val localvarIdent = sub.statements.filterIsInstance<Assignment>().first { it.value is IdentifierReference }.value as IdentifierReference
|
||||
asmgen.asmSymbolName(localvarIdent) shouldBe "localvar"
|
||||
asmgen.asmVariableName(localvarIdent) shouldBe "localvar"
|
||||
val localvarIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main", "start", "localvar") }.value as AddressOf).identifier
|
||||
asmgen.asmSymbolName(localvarIdentScoped) shouldBe "main.start.localvar"
|
||||
asmgen.asmVariableName(localvarIdentScoped) shouldBe "main.start.localvar"
|
||||
|
||||
// variable from outer scope (note that for Variables, no scoping prefix symbols are required,
|
||||
// because they're not outputted as locally scoped symbols for the assembler
|
||||
val scopedVarIdent = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("var_outside") }.value as AddressOf).identifier
|
||||
asmgen.asmSymbolName(scopedVarIdent) shouldBe "main.var_outside"
|
||||
asmgen.asmVariableName(scopedVarIdent) shouldBe "var_outside"
|
||||
val scopedVarIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main", "var_outside") }.value as AddressOf).identifier
|
||||
asmgen.asmSymbolName(scopedVarIdentScoped) shouldBe "main.var_outside"
|
||||
asmgen.asmVariableName(scopedVarIdentScoped) shouldBe "main.var_outside"
|
||||
}
|
||||
|
||||
"symbol and variable names from label identifiers" {
|
||||
val program = createTestProgram()
|
||||
val asmgen = createTestAsmGen(program)
|
||||
val sub = program.entrypoint
|
||||
|
||||
// local label
|
||||
val localLabelIdent = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("locallabel") }.value as AddressOf).identifier
|
||||
asmgen.asmSymbolName(localLabelIdent) shouldBe "_locallabel"
|
||||
withClue("as a variable it uses different naming rules (no underscore prefix)") {
|
||||
asmgen.asmVariableName(localLabelIdent) shouldBe "locallabel"
|
||||
}
|
||||
val localLabelIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main","start","locallabel") }.value as AddressOf).identifier
|
||||
asmgen.asmSymbolName(localLabelIdentScoped) shouldBe "main.start._locallabel"
|
||||
withClue("as a variable it uses different naming rules (no underscore prefix)") {
|
||||
asmgen.asmVariableName(localLabelIdentScoped) shouldBe "main.start.locallabel"
|
||||
}
|
||||
|
||||
// label from outer scope needs sope prefixes because it is outputted as a locally scoped symbol for the assembler
|
||||
val scopedLabelIdent = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("label_outside") }.value as AddressOf).identifier
|
||||
asmgen.asmSymbolName(scopedLabelIdent) shouldBe "main._label_outside"
|
||||
withClue("as a variable it uses different naming rules (no underscore prefix)") {
|
||||
asmgen.asmVariableName(scopedLabelIdent) shouldBe "label_outside"
|
||||
}
|
||||
val scopedLabelIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main","label_outside") }.value as AddressOf).identifier
|
||||
asmgen.asmSymbolName(scopedLabelIdentScoped) shouldBe "main._label_outside"
|
||||
withClue("as a variable it uses different naming rules (no underscore prefix)") {
|
||||
asmgen.asmVariableName(scopedLabelIdentScoped) shouldBe "main.label_outside"
|
||||
}
|
||||
}
|
||||
|
||||
"asm names for hooks to zp temp vars" {
|
||||
/*
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
prog8_lib.P8ZP_SCRATCH_REG = 1
|
||||
prog8_lib.P8ZP_SCRATCH_B1 = 1
|
||||
prog8_lib.P8ZP_SCRATCH_W1 = 1
|
||||
prog8_lib.P8ZP_SCRATCH_W2 = 1
|
||||
*/
|
||||
val program = createTestProgram()
|
||||
val asmgen = createTestAsmGen(program)
|
||||
asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_REG") shouldBe "P8ZP_SCRATCH_REG"
|
||||
asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_W2") shouldBe "P8ZP_SCRATCH_W2"
|
||||
asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_REG")) shouldBe "P8ZP_SCRATCH_REG"
|
||||
asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_W2")) shouldBe "P8ZP_SCRATCH_W2"
|
||||
val id1 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_REG"), Position.DUMMY)
|
||||
id1.linkParents(program.toplevelModule)
|
||||
val id2 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_W2"), Position.DUMMY)
|
||||
id2.linkParents(program.toplevelModule)
|
||||
asmgen.asmSymbolName(id1) shouldBe "P8ZP_SCRATCH_REG"
|
||||
asmgen.asmSymbolName(id2) shouldBe "P8ZP_SCRATCH_W2"
|
||||
}
|
||||
})
|
8
codeGeneration/test/ProjectConfig.kt
Normal file
8
codeGeneration/test/ProjectConfig.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package prog8tests.asmgen
|
||||
|
||||
import io.kotest.core.config.AbstractProjectConfig
|
||||
import kotlin.math.max
|
||||
|
||||
object ProjectConfig : AbstractProjectConfig() {
|
||||
override val parallelism = max(2, Runtime.getRuntime().availableProcessors() / 2)
|
||||
}
|
37
codeGeneration/test/helpers/Dummies.kt
Normal file
37
codeGeneration/test/helpers/Dummies.kt
Normal file
@ -0,0 +1,37 @@
|
||||
package prog8tests.asmgen.helpers
|
||||
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.compilerinterface.IMemSizer
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.compilerinterface.IStringEncoding
|
||||
|
||||
|
||||
internal val DummyFunctions = object : IBuiltinFunctions {
|
||||
override val names: Set<String> = emptySet()
|
||||
override val purefunctionNames: Set<String> = emptySet()
|
||||
override fun constValue(
|
||||
name: String,
|
||||
args: List<Expression>,
|
||||
position: Position,
|
||||
): NumericLiteralValue? = null
|
||||
|
||||
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
||||
}
|
||||
|
||||
internal val DummyMemsizer = object : IMemSizer {
|
||||
override fun memorySize(dt: DataType): Int = 0
|
||||
}
|
||||
|
||||
internal val DummyStringEncoder = object : IStringEncoding {
|
||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String {
|
||||
return ""
|
||||
}
|
||||
}
|
30
codeGeneration/test/helpers/ErrorReporterForTests.kt
Normal file
30
codeGeneration/test/helpers/ErrorReporterForTests.kt
Normal file
@ -0,0 +1,30 @@
|
||||
package prog8tests.asmgen.helpers
|
||||
|
||||
import prog8.ast.base.Position
|
||||
import prog8.compilerinterface.IErrorReporter
|
||||
|
||||
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true): IErrorReporter {
|
||||
|
||||
|
||||
val errors = mutableListOf<String>()
|
||||
val warnings = mutableListOf<String>()
|
||||
|
||||
override fun err(msg: String, position: Position) {
|
||||
errors.add("${position.toClickableStr()} $msg")
|
||||
}
|
||||
|
||||
override fun warn(msg: String, position: Position) {
|
||||
warnings.add("${position.toClickableStr()} $msg")
|
||||
}
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
|
||||
override fun report() {
|
||||
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||
if(throwExceptionAtReportIfErrors)
|
||||
finalizeNumErrors(errors.size, warnings.size)
|
||||
errors.clear()
|
||||
warnings.clear()
|
||||
}
|
||||
}
|
32
codeOptimizers/build.gradle
Normal file
32
codeOptimizers/build.gradle
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':compilerInterfaces')
|
||||
implementation project(':compilerAst')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
16
codeOptimizers/codeOptimizers.iml
Normal file
16
codeOptimizers/codeOptimizers.iml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="compilerInterfaces" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
</component>
|
||||
</module>
|
2
codeOptimizers/readme-tests.txt
Normal file
2
codeOptimizers/readme-tests.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Unittests for things in this module are located in the Compiler module instead,
|
||||
for convenience sake, and to not spread the test cases around too much.
|
105
codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt
Normal file
105
codeOptimizers/src/prog8/optimizer/BinExprSplitter.kt
Normal file
@ -0,0 +1,105 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.expressions.augmentAssignmentOperators
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compilerinterface.CompilationOptions
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
import prog8.compilerinterface.isInRegularRAMof
|
||||
|
||||
|
||||
class BinExprSplitter(private val program: Program, private val options: CompilationOptions, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(assignment.value.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
||||
return noModifications
|
||||
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if (binExpr != null) {
|
||||
|
||||
/*
|
||||
|
||||
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
||||
by attempting to splitting it up into individual simple steps.
|
||||
We only consider a binary expression *one* level deep (so the operands must not be a combined expression)
|
||||
|
||||
|
||||
X = BinExpr X = LeftExpr
|
||||
<operator> followed by
|
||||
/ \ IF 'X' not used X = BinExpr
|
||||
/ \ IN expression ==> <operator>
|
||||
/ \ / \
|
||||
LeftExpr. RightExpr. / \
|
||||
X RightExpr.
|
||||
|
||||
|
||||
*/
|
||||
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target)) {
|
||||
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
|
||||
return noModifications
|
||||
|
||||
if(binExpr.right.isSimple && !assignment.isAugmentable) {
|
||||
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, binExpr.left.position)
|
||||
val targetExpr = assignment.target.toExpression()
|
||||
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
|
||||
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO further unraveling of binary expression trees into flat statements.
|
||||
// however this should probably be done in a more generic way to also work on
|
||||
// the expressiontrees that are not used in an assignment statement...
|
||||
}
|
||||
|
||||
val typecast = assignment.value as? TypecastExpression
|
||||
if(typecast!=null) {
|
||||
val origExpr = typecast.expression as? BinaryExpression
|
||||
if(origExpr!=null) {
|
||||
// it's a typecast of a binary expression.
|
||||
// we can see if we can unwrap the binary expression by working on a new temporary variable
|
||||
// (that has the type of the expression), and then finally doing the typecast.
|
||||
// Once it's outside the typecast, the regular splitting can commence.
|
||||
val tempDt = origExpr.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val tempVar = when(tempDt) {
|
||||
DataType.UBYTE -> listOf("prog8_lib", "retval_interm_ub")
|
||||
DataType.BYTE -> listOf("prog8_lib", "retval_interm_b")
|
||||
DataType.UWORD -> listOf("prog8_lib", "retval_interm_uw")
|
||||
DataType.WORD -> listOf("prog8_lib", "retval_interm_w")
|
||||
DataType.FLOAT -> listOf("floats", "tempvar_swap_float")
|
||||
else -> throw FatalAstException("invalid dt $tempDt")
|
||||
}
|
||||
val assignTempVar = Assignment(
|
||||
AssignTarget(IdentifierReference(tempVar, typecast.position), null, null, typecast.position),
|
||||
typecast.expression, typecast.position
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(assignment, assignTempVar, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(typecast.expression, IdentifierReference(tempVar, typecast.position), typecast)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun isSimpleTarget(target: AssignTarget) =
|
||||
if (target.identifier!=null || target.memoryAddress!=null)
|
||||
target.isInRegularRAMof(compTarget.machine)
|
||||
else
|
||||
false
|
||||
|
||||
}
|
278
codeOptimizers/src/prog8/optimizer/ConstExprEvaluator.kt
Normal file
278
codeOptimizers/src/prog8/optimizer/ConstExprEvaluator.kt
Normal file
@ -0,0 +1,278 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
class ConstExprEvaluator {
|
||||
|
||||
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
|
||||
try {
|
||||
return when(operator) {
|
||||
"+" -> plus(left, right)
|
||||
"-" -> minus(left, right)
|
||||
"*" -> multiply(left, right)
|
||||
"/" -> divide(left, right)
|
||||
"%" -> remainder(left, right)
|
||||
"**" -> power(left, right)
|
||||
"&" -> bitwiseand(left, right)
|
||||
"|" -> bitwiseor(left, right)
|
||||
"^" -> bitwisexor(left, right)
|
||||
"and" -> logicaland(left, right)
|
||||
"or" -> logicalor(left, right)
|
||||
"xor" -> logicalxor(left, right)
|
||||
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
|
||||
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
|
||||
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
|
||||
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
|
||||
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
|
||||
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
|
||||
"<<" -> shiftedleft(left, right)
|
||||
">>" -> shiftedright(left, right)
|
||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||
}
|
||||
} catch (ax: FatalAstException) {
|
||||
throw ExpressionError(ax.message, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
||||
val result =
|
||||
if(left.type== DataType.UBYTE || left.type== DataType.UWORD)
|
||||
left.number.toInt().ushr(amount.number.toInt())
|
||||
else
|
||||
left.number.toInt().shr(amount.number.toInt())
|
||||
return NumericLiteralValue(left.type, result, left.position)
|
||||
}
|
||||
|
||||
private fun shiftedleft(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left << $amount", left.position)
|
||||
val result = left.number.toInt().shl(amount.number.toInt())
|
||||
return NumericLiteralValue(left.type, result, left.position)
|
||||
}
|
||||
|
||||
private fun logicalxor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot compute $left locical-bitxor $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toDouble() != 0.0), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toInt() != 0), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toDouble() != 0.0) xor (right.number.toDouble() != 0.0), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logicalor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot compute $left locical-or $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toDouble() != 0.0, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toInt() != 0, left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 || right.number.toDouble() != 0.0, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logicaland(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot compute $left locical-and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toDouble() != 0.0, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toInt() != 0, left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toDouble() != 0.0 && right.number.toDouble() != 0.0, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bitwisexor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toShort(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() xor right.number.toInt(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toShort(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() and right.number.toInt(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
}
|
||||
|
||||
private fun power(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot calculate $left ** $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number.toDouble()), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toInt()), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble().pow(right.number.toDouble()), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun plus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot add $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() + right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun minus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot subtract $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() - right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun multiply(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot multiply ${left.type} and ${right.type}"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toDouble() * right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun divideByZeroError(pos: Position): Unit =
|
||||
throw ExpressionError("division by zero", pos)
|
||||
|
||||
private fun divide(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot divide $left by $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
val result: Int = left.number.toInt() / right.number.toInt()
|
||||
NumericLiteralValue.optimalInteger(result, left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||
NumericLiteralValue(DataType.FLOAT, left.number.toInt() / right.number.toDouble(), left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toInt(), left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() / right.number.toDouble(), left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun remainder(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
val error = "cannot compute remainder of $left by $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||
NumericLiteralValue(DataType.FLOAT, left.number.toInt() % right.number.toDouble(), left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
DataType.FLOAT -> when (right.type) {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toInt(), left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||
NumericLiteralValue(DataType.FLOAT, left.number.toDouble() % right.number.toDouble(), left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
}
|
502
codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt
Normal file
502
codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt
Normal file
@ -0,0 +1,502 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
|
||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
// @( &thing ) --> thing
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
return if(addrOf!=null)
|
||||
listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||
else
|
||||
noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||
// Try to turn a unary prefix expression into a single constant value.
|
||||
// Compile-time constant sub expressions will be evaluated on the spot.
|
||||
// For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
||||
val subexpr = expr.expression
|
||||
if (subexpr is NumericLiteralValue) {
|
||||
// accept prefixed literal values (such as -3, not true)
|
||||
return when (expr.operator) {
|
||||
"+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent))
|
||||
"-" -> when (subexpr.type) {
|
||||
in IntegerDatatypes -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue.optimalInteger(-subexpr.number.toInt(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
||||
}
|
||||
"~" -> when (subexpr.type) {
|
||||
DataType.BYTE -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue(DataType.BYTE, subexpr.number.toInt().inv(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue(DataType.UBYTE, subexpr.number.toInt().inv() and 255, subexpr.position),
|
||||
parent))
|
||||
}
|
||||
DataType.WORD -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue(DataType.WORD, subexpr.number.toInt().inv(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue(DataType.UWORD, subexpr.number.toInt().inv() and 65535, subexpr.position),
|
||||
parent))
|
||||
}
|
||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||
}
|
||||
"not" -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position),
|
||||
parent))
|
||||
}
|
||||
else -> throw ExpressionError(expr.operator, subexpr.position)
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to constfold a binary expression.
|
||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
|
||||
*
|
||||
* More complex stuff: reordering to group constants:
|
||||
* If one of our operands is a Constant,
|
||||
* and the other operand is a Binary expression,
|
||||
* and one of ITS operands is a Constant,
|
||||
* and ITS other operand is NOT a Constant,
|
||||
* ...it may be possible to rewrite the expression to group the two Constants together,
|
||||
* to allow them to be const-folded away.
|
||||
*
|
||||
* examples include:
|
||||
* (X / c1) * c2 -> X / (c2/c1)
|
||||
* (X + c1) - c2 -> X + (c1-c2)
|
||||
*/
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val leftconst = expr.left.constValue(program)
|
||||
val rightconst = expr.right.constValue(program)
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
|
||||
if(expr.operator == "**" && leftconst!=null) {
|
||||
// optimize various simple cases of ** :
|
||||
// optimize away 1 ** x into just 1 and 0 ** x into just 0
|
||||
// optimize 2 ** x into (1<<x) if both operands are integer.
|
||||
val leftDt = leftconst.inferType(program).getOr(DataType.UNDEFINED)
|
||||
when (leftconst.number.toDouble()) {
|
||||
0.0 -> {
|
||||
val value = NumericLiteralValue(leftDt, 0, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||
}
|
||||
1.0 -> {
|
||||
val value = NumericLiteralValue(leftDt, 1, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||
}
|
||||
2.0 -> {
|
||||
if(rightconst!=null) {
|
||||
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||
} else {
|
||||
val rightDt = expr.right.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val targetDt =
|
||||
when (parent) {
|
||||
is Assignment -> parent.target.inferType(program).getOr(DataType.UNDEFINED)
|
||||
is VarDecl -> parent.datatype
|
||||
else -> leftDt
|
||||
}
|
||||
val one = NumericLiteralValue(targetDt, 1, expr.position)
|
||||
val shift = BinaryExpression(one, "<<", expr.right, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, shift, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.inferType(program) istype DataType.FLOAT) {
|
||||
val subExpr: BinaryExpression? = when {
|
||||
leftconst != null -> expr.right as? BinaryExpression
|
||||
rightconst != null -> expr.left as? BinaryExpression
|
||||
else -> null
|
||||
}
|
||||
if (subExpr != null) {
|
||||
val subleftconst = subExpr.left.constValue(program)
|
||||
val subrightconst = subExpr.right.constValue(program)
|
||||
if ((subleftconst != null && subrightconst == null) || (subleftconst == null && subrightconst != null)) {
|
||||
// try reordering.
|
||||
val change = groupTwoFloatConstsTogether(
|
||||
expr, subExpr,
|
||||
leftconst != null, rightconst != null,
|
||||
subleftconst != null, subrightconst != null
|
||||
)
|
||||
if (change != null)
|
||||
modifications += change
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const fold when both operands are a const
|
||||
if(leftconst != null && rightconst != null) {
|
||||
val evaluator = ConstExprEvaluator()
|
||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
||||
}
|
||||
|
||||
return modifications
|
||||
}
|
||||
|
||||
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||
// because constant folding can result in arrays that are now suddenly capable
|
||||
// of telling the type of all their elements (for instance, when they contained -2 which
|
||||
// was a prefix expression earlier), we recalculate the array's datatype.
|
||||
if(array.type.isKnown)
|
||||
return noModifications
|
||||
|
||||
// if the array literalvalue is inside an array vardecl, take the type from that
|
||||
// otherwise infer it from the elements of the array
|
||||
val vardeclType = (array.parent as? VarDecl)?.datatype
|
||||
if(vardeclType!=null) {
|
||||
val newArray = array.cast(vardeclType)
|
||||
if (newArray != null && newArray != array)
|
||||
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||
} else {
|
||||
val arrayDt = array.guessDatatype(program)
|
||||
if (arrayDt.isKnown) {
|
||||
val newArray = array.cast(arrayDt.getOr(DataType.UNDEFINED))
|
||||
if (newArray != null && newArray != array)
|
||||
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
// the args of a fuction are constfolded via recursion already.
|
||||
val constvalue = functionCall.constValue(program)
|
||||
return if(constvalue!=null)
|
||||
listOf(IAstModification.ReplaceNode(functionCall, constvalue, parent))
|
||||
else
|
||||
noModifications
|
||||
}
|
||||
|
||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr? {
|
||||
val fromCast = rangeFrom.cast(targetDt)
|
||||
val toCast = rangeTo.cast(targetDt)
|
||||
if(!fromCast.isValid || !toCast.isValid)
|
||||
return null
|
||||
|
||||
val newStep =
|
||||
if(stepLiteral!=null) {
|
||||
val stepCast = stepLiteral.cast(targetDt)
|
||||
if(stepCast.isValid)
|
||||
stepCast.valueOrZero()
|
||||
else
|
||||
range.step
|
||||
} else {
|
||||
range.step
|
||||
}
|
||||
|
||||
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
|
||||
}
|
||||
|
||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||
val iterableRange = forLoop.iterable as? RangeExpr ?: return noModifications
|
||||
val rangeFrom = iterableRange.from as? NumericLiteralValue
|
||||
val rangeTo = iterableRange.to as? NumericLiteralValue
|
||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||
|
||||
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar)
|
||||
|
||||
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
||||
when(loopvar.datatype) {
|
||||
DataType.UBYTE -> {
|
||||
if(rangeFrom.type!= DataType.UBYTE) {
|
||||
// attempt to translate the iterable into ubyte values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(rangeFrom.type!= DataType.BYTE) {
|
||||
// attempt to translate the iterable into byte values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(rangeFrom.type!= DataType.UWORD) {
|
||||
// attempt to translate the iterable into uword values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(rangeFrom.type!= DataType.WORD) {
|
||||
// attempt to translate the iterable into word values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val numval = decl.value as? NumericLiteralValue
|
||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||
val valueDt = numval.inferType(program)
|
||||
if(valueDt isnot decl.datatype) {
|
||||
val cast = numval.cast(decl.datatype)
|
||||
if(cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private class ShuffleOperands(val expr: BinaryExpression,
|
||||
val exprOperator: String?,
|
||||
val subExpr: BinaryExpression,
|
||||
val newExprLeft: Expression?,
|
||||
val newExprRight: Expression?,
|
||||
val newSubexprLeft: Expression?,
|
||||
val newSubexprRight: Expression?
|
||||
): IAstModification {
|
||||
override fun perform() {
|
||||
if(exprOperator!=null) expr.operator = exprOperator
|
||||
if(newExprLeft!=null) expr.left = newExprLeft
|
||||
if(newExprRight!=null) expr.right = newExprRight
|
||||
if(newSubexprLeft!=null) subExpr.left = newSubexprLeft
|
||||
if(newSubexprRight!=null) subExpr.right = newSubexprRight
|
||||
}
|
||||
}
|
||||
|
||||
private fun groupTwoFloatConstsTogether(expr: BinaryExpression,
|
||||
subExpr: BinaryExpression,
|
||||
leftIsConst: Boolean,
|
||||
rightIsConst: Boolean,
|
||||
subleftIsConst: Boolean,
|
||||
subrightIsConst: Boolean): IAstModification?
|
||||
{
|
||||
// NOTE: THIS IS ONLY VALID ON FLOATING POINT CONSTANTS
|
||||
|
||||
// todo: this implements only a small set of possible reorderings at this time
|
||||
if(expr.operator==subExpr.operator) {
|
||||
// both operators are the same.
|
||||
|
||||
// If associative, we can simply shuffle the const operands around to optimize.
|
||||
if(expr.operator in associativeOperators) {
|
||||
return if(leftIsConst) {
|
||||
if(subleftIsConst)
|
||||
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
||||
else
|
||||
ShuffleOperands(expr, null, subExpr, subExpr.left, null, expr.left, null)
|
||||
} else {
|
||||
if(subleftIsConst)
|
||||
ShuffleOperands(expr, null, subExpr, null, subExpr.right, null, expr.right)
|
||||
else
|
||||
ShuffleOperands(expr, null, subExpr, null, subExpr.left, expr.right, null)
|
||||
}
|
||||
}
|
||||
|
||||
// If - or /, we simetimes must reorder more, and flip operators (- -> +, / -> *)
|
||||
if(expr.operator=="-" || expr.operator=="/") {
|
||||
if(leftIsConst) {
|
||||
return if (subleftIsConst) {
|
||||
ShuffleOperands(expr, if (expr.operator == "-") "+" else "*", subExpr, subExpr.right, null, expr.left, subExpr.left)
|
||||
} else {
|
||||
IAstModification.ReplaceNode(expr,
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||
expr.operator, subExpr.left, expr.position),
|
||||
expr.parent)
|
||||
}
|
||||
} else {
|
||||
return if(subleftIsConst) {
|
||||
return ShuffleOperands(expr, null, subExpr, null, subExpr.right, null, expr.right)
|
||||
} else {
|
||||
IAstModification.ReplaceNode(expr,
|
||||
BinaryExpression(
|
||||
subExpr.left, expr.operator,
|
||||
BinaryExpression(expr.right, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||
expr.position),
|
||||
expr.parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(expr.operator=="/" && subExpr.operator=="*") {
|
||||
if(leftIsConst) {
|
||||
val change = if(subleftIsConst) {
|
||||
// C1/(C2*V) -> (C1/C2)/V
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "/", subExpr.left, subExpr.position),
|
||||
"/",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// C1/(V*C2) -> (C1/C2)/V
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "/", subExpr.right, subExpr.position),
|
||||
"/",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
} else {
|
||||
val change = if(subleftIsConst) {
|
||||
// (C1*V)/C2 -> (C1/C2)*V
|
||||
BinaryExpression(
|
||||
BinaryExpression(subExpr.left, "/", expr.right, subExpr.position),
|
||||
"*",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// (V*C1)/C2 -> (C1/C2)*V
|
||||
BinaryExpression(
|
||||
BinaryExpression(subExpr.right, "/", expr.right, subExpr.position),
|
||||
"*",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="*" && subExpr.operator=="/") {
|
||||
if(leftIsConst) {
|
||||
val change = if(subleftIsConst) {
|
||||
// C1*(C2/V) -> (C1*C2)/V
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "*", subExpr.left, subExpr.position),
|
||||
"/",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// C1*(V/C2) -> (C1/C2)*V
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "/", subExpr.right, subExpr.position),
|
||||
"*",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
} else {
|
||||
val change = if(subleftIsConst) {
|
||||
// (C1/V)*C2 -> (C1*C2)/V
|
||||
BinaryExpression(
|
||||
BinaryExpression(subExpr.left, "*", expr.right, subExpr.position),
|
||||
"/",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// (V/C1)*C2 -> (C1/C2)*V
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.right, "/", subExpr.right, subExpr.position),
|
||||
"*",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="+" && subExpr.operator=="-") {
|
||||
if(leftIsConst){
|
||||
val change = if(subleftIsConst){
|
||||
// c1+(c2-v) -> (c1+c2)-v
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "+", subExpr.left, subExpr.position),
|
||||
"-",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// c1+(v-c2) -> v+(c1-c2)
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "-", subExpr.right, subExpr.position),
|
||||
"+",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
} else {
|
||||
val change = if(subleftIsConst) {
|
||||
// (c1-v)+c2 -> (c1+c2)-v
|
||||
BinaryExpression(
|
||||
BinaryExpression(subExpr.left, "+", expr.right, subExpr.position),
|
||||
"-",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// (v-c1)+c2 -> v+(c2-c1)
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.right, "-", subExpr.right, subExpr.position),
|
||||
"+",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
}
|
||||
}
|
||||
else if(expr.operator=="-" && subExpr.operator=="+") {
|
||||
if(leftIsConst) {
|
||||
val change = if(subleftIsConst) {
|
||||
// c1-(c2+v) -> (c1-c2)-v
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "-", subExpr.left, subExpr.position),
|
||||
"-",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// c1-(v+c2) -> (c1-c2)-v
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, "-", subExpr.right, subExpr.position),
|
||||
"-",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
} else {
|
||||
val change = if(subleftIsConst) {
|
||||
// (c1+v)-c2 -> v+(c1-c2)
|
||||
BinaryExpression(
|
||||
BinaryExpression(subExpr.left, "-", expr.right, subExpr.position),
|
||||
"+",
|
||||
subExpr.right, expr.position)
|
||||
} else {
|
||||
// (v+c1)-c2 -> v+(c1-c2)
|
||||
BinaryExpression(
|
||||
BinaryExpression(subExpr.right, "-", expr.right, subExpr.position),
|
||||
"+",
|
||||
subExpr.left, expr.position)
|
||||
}
|
||||
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
232
codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt
Normal file
232
codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt
Normal file
@ -0,0 +1,232 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
import prog8.compilerinterface.IErrorReporter
|
||||
import prog8.compilerinterface.size
|
||||
import prog8.compilerinterface.toConstantIntegerRange
|
||||
|
||||
// Fix up the literal value's type to match that of the vardecl
|
||||
// (also check range literal operands types before they get expanded into arrays for instance)
|
||||
class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(decl.parent is AnonymousScope)
|
||||
throw FatalAstException("vardecl may no longer occur in anonymousscope")
|
||||
|
||||
try {
|
||||
val declConstValue = decl.value?.constValue(program)
|
||||
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||
&& declConstValue.inferType(program) isnot decl.datatype) {
|
||||
// cast the numeric literal to the appropriate datatype of the variable
|
||||
val cast = declConstValue.cast(decl.datatype)
|
||||
if(cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> {
|
||||
val from = range.from.constValue(program)?.number?.toDouble()
|
||||
val to = range.to.constValue(program)?.number?.toDouble()
|
||||
val step = range.step.constValue(program)?.number?.toDouble()
|
||||
|
||||
if(from==null) {
|
||||
if(!range.from.inferType(program).isInteger)
|
||||
errors.err("range expression from value must be integer", range.from.position)
|
||||
} else if(from-from.toInt()>0) {
|
||||
errors.err("range expression from value must be integer", range.from.position)
|
||||
}
|
||||
|
||||
if(to==null) {
|
||||
if(!range.to.inferType(program).isInteger)
|
||||
errors.err("range expression to value must be integer", range.to.position)
|
||||
} else if(to-to.toInt()>0) {
|
||||
errors.err("range expression to value must be integer", range.to.position)
|
||||
}
|
||||
|
||||
if(step==null) {
|
||||
if(!range.step.inferType(program).isInteger)
|
||||
errors.err("range expression step value must be integer", range.step.position)
|
||||
} else if(step-step.toInt()>0) {
|
||||
errors.err("range expression step value must be integer", range.step.position)
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Replace all constant identifiers with their actual value,
|
||||
// and the array var initializer values and sizes.
|
||||
// This is needed because further constant optimizations depend on those.
|
||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
// replace identifiers that refer to const value, with the value itself
|
||||
// if it's a simple type and if it's not a left hand side variable
|
||||
if(identifier.parent is AssignTarget)
|
||||
return noModifications
|
||||
var forloop = identifier.parent as? ForLoop
|
||||
if(forloop==null)
|
||||
forloop = identifier.parent.parent as? ForLoop
|
||||
if(forloop!=null && identifier===forloop.loopVar)
|
||||
return noModifications
|
||||
|
||||
try {
|
||||
val cval = identifier.constValue(program) ?: return noModifications
|
||||
return when (cval.type) {
|
||||
in NumericDatatypes -> listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteralValue(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
)
|
||||
)
|
||||
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
||||
else -> noModifications
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
// TODO: use call graph for this?
|
||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true) {
|
||||
errors.err("recursive var declaration", decl.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(decl.type== VarDeclType.CONST || decl.type== VarDeclType.VAR) {
|
||||
if(decl.isArray){
|
||||
val arraysize = decl.arraysize
|
||||
if(arraysize==null) {
|
||||
// for arrays that have no size specifier attempt to deduce the size
|
||||
val arrayval = decl.value as? ArrayLiteralValue
|
||||
if(arrayval!=null) {
|
||||
return listOf(IAstModification.SetExpression(
|
||||
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
||||
NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position),
|
||||
decl
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when(decl.datatype) {
|
||||
DataType.FLOAT -> {
|
||||
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
||||
val litval = decl.value as? NumericLiteralValue
|
||||
if (litval!=null && litval.type in IntegerDatatypes) {
|
||||
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
val rangeExpr = decl.value as? RangeExpr
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
if(constRange!=null) {
|
||||
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||
val newValue = if(eltType in ByteDatatypes) {
|
||||
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
} else {
|
||||
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
}
|
||||
val numericLv = decl.value as? NumericLiteralValue
|
||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||
errors.err("arraysize requires only integers here", numericLv.position)
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
if (rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||
val fillvalue = numericLv.number.toInt()
|
||||
when(decl.datatype){
|
||||
DataType.ARRAY_UB -> {
|
||||
if(fillvalue !in 0..255)
|
||||
errors.err("ubyte value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
if(fillvalue !in -128..127)
|
||||
errors.err("byte value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
if(fillvalue !in 0..65535)
|
||||
errors.err("uword value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
if(fillvalue !in -32768..32767)
|
||||
errors.err("word value overflow", numericLv.position)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayToElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val rangeExpr = decl.value as? RangeExpr
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array of floats
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
if(constRange!=null) {
|
||||
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
}
|
||||
|
||||
val numericLv = decl.value as? NumericLiteralValue
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
if(rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = numericLv.number.toDouble()
|
||||
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
errors.err("float value overflow", numericLv.position)
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// nothing to do for this type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
721
codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt
Normal file
721
codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt
Normal file
@ -0,0 +1,721 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.IntegerDatatypes
|
||||
import prog8.ast.base.NumericDatatypes
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.pow
|
||||
|
||||
/*
|
||||
todo add more peephole expression optimizations
|
||||
|
||||
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
||||
|
||||
*(&X) => X
|
||||
X % 1 => 0
|
||||
X / 1 => X
|
||||
X ^ -1 => ~x
|
||||
X >= 1 => X > 0
|
||||
X < 1 => X <= 0
|
||||
X + С1 == C2 => X == C2 - C1
|
||||
((X + C1) + C2) => (X + (C1 + C2))
|
||||
((X + C1) + (Y + C2)) => ((X + Y) + (C1 + C2))
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
|
||||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteralValue
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast)
|
||||
}
|
||||
|
||||
// remove redundant nested typecasts
|
||||
val subTypecast = typecast.expression as? TypecastExpression
|
||||
if (subTypecast != null) {
|
||||
// remove the sub-typecast if its datatype is larger than the outer typecast
|
||||
if(subTypecast.type largerThan typecast.type) {
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
}
|
||||
} else {
|
||||
if (typecast.expression.inferType(program) istype typecast.type) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||
if (expr.operator == "+") {
|
||||
// +X --> X
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent))
|
||||
} else if (expr.operator == "not") {
|
||||
when(expr.expression) {
|
||||
is PrefixExpression -> {
|
||||
// NOT(NOT(...)) -> ...
|
||||
val pe = expr.expression as PrefixExpression
|
||||
if(pe.operator == "not")
|
||||
return listOf(IAstModification.ReplaceNode(expr, pe.expression, parent))
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
// NOT (xxxx) -> invert the xxxx
|
||||
val be = expr.expression as BinaryExpression
|
||||
val newExpr = when (be.operator) {
|
||||
"<" -> BinaryExpression(be.left, ">=", be.right, be.position)
|
||||
">" -> BinaryExpression(be.left, "<=", be.right, be.position)
|
||||
"<=" -> BinaryExpression(be.left, ">", be.right, be.position)
|
||||
">=" -> BinaryExpression(be.left, "<", be.right, be.position)
|
||||
"==" -> BinaryExpression(be.left, "!=", be.right, be.position)
|
||||
"!=" -> BinaryExpression(be.left, "==", be.right, be.position)
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (newExpr != null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
}
|
||||
else -> return noModifications
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val leftVal = expr.left.constValue(program)
|
||||
val rightVal = expr.right.constValue(program)
|
||||
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
val rightIDt = expr.right.inferType(program)
|
||||
if (!leftIDt.isKnown || !rightIDt.isKnown)
|
||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
||||
if (expr.operator in associativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
||||
if(parent !is Assignment || !(expr.left isSameAs parent.target))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
}
|
||||
|
||||
// X + (-A) --> X - A
|
||||
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr,
|
||||
BinaryExpression(expr.left, "-", (expr.right as PrefixExpression).expression, expr.position),
|
||||
parent
|
||||
))
|
||||
}
|
||||
|
||||
// (-A) + X --> X - A
|
||||
if (expr.operator == "+" && (expr.left as? PrefixExpression)?.operator == "-") {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr,
|
||||
BinaryExpression(expr.right, "-", (expr.left as PrefixExpression).expression, expr.position),
|
||||
parent
|
||||
))
|
||||
}
|
||||
|
||||
// X - (-A) --> X + A
|
||||
if (expr.operator == "-" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr,
|
||||
BinaryExpression(expr.left, "+", (expr.right as PrefixExpression).expression, expr.position),
|
||||
parent
|
||||
))
|
||||
}
|
||||
|
||||
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
|
||||
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
|
||||
|
||||
if (expr.operator == "+" || expr.operator == "-"
|
||||
&& leftVal == null && rightVal == null
|
||||
&& leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||
val leftBinExpr = expr.left as? BinaryExpression
|
||||
val rightBinExpr = expr.right as? BinaryExpression
|
||||
if (leftBinExpr?.operator == "*") {
|
||||
if (expr.operator == "+") {
|
||||
// Y*X + X -> X*(Y + 1)
|
||||
// X*Y + X -> X*(Y + 1)
|
||||
val x = expr.right
|
||||
val y = determineY(x, leftBinExpr)
|
||||
if (y != null) {
|
||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
}
|
||||
} else {
|
||||
// Y*X - X -> X*(Y - 1)
|
||||
// X*Y - X -> X*(Y - 1)
|
||||
val x = expr.right
|
||||
val y = determineY(x, leftBinExpr)
|
||||
if (y != null) {
|
||||
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yMinus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
}
|
||||
}
|
||||
} else if (rightBinExpr?.operator == "*") {
|
||||
if (expr.operator == "+") {
|
||||
// X + Y*X -> X*(Y + 1)
|
||||
// X + X*Y -> X*(Y + 1)
|
||||
val x = expr.left
|
||||
val y = determineY(x, rightBinExpr)
|
||||
if (y != null) {
|
||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue.optimalInteger(1, y.position), y.position)
|
||||
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.operator == ">=" && rightVal?.number == 0) {
|
||||
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
||||
// unsigned >= 0 --> true
|
||||
return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(true, expr.position), parent))
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.operator == "<" && rightVal?.number == 0) {
|
||||
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
||||
// unsigned < 0 --> false
|
||||
return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(false, expr.position), parent))
|
||||
}
|
||||
when(leftDt) {
|
||||
DataType.BYTE -> {
|
||||
// signed < 0 --> signed & $80
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr,
|
||||
BinaryExpression(expr.left, "&", NumericLiteralValue.optimalInteger(0x80, expr.position), expr.position),
|
||||
parent
|
||||
))
|
||||
}
|
||||
DataType.WORD -> {
|
||||
// signedw < 0 --> msb(signedw) & $80
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr,
|
||||
BinaryExpression(FunctionCall(IdentifierReference(listOf("msb"), expr.position),
|
||||
mutableListOf(expr.left),
|
||||
expr.position
|
||||
), "&", NumericLiteralValue.optimalInteger(0x80, expr.position), expr.position),
|
||||
parent
|
||||
))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
// simplify when a term is constant and directly determines the outcome
|
||||
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
||||
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
||||
val newExpr: Expression? = when (expr.operator) {
|
||||
"or" -> {
|
||||
if ((leftVal != null && leftVal.asBooleanValue) || (rightVal != null && rightVal.asBooleanValue))
|
||||
constTrue
|
||||
else if (leftVal != null && !leftVal.asBooleanValue)
|
||||
expr.right
|
||||
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||
expr.left
|
||||
else
|
||||
null
|
||||
}
|
||||
"and" -> {
|
||||
if ((leftVal != null && !leftVal.asBooleanValue) || (rightVal != null && !rightVal.asBooleanValue))
|
||||
constFalse
|
||||
else if (leftVal != null && leftVal.asBooleanValue)
|
||||
expr.right
|
||||
else if (rightVal != null && rightVal.asBooleanValue)
|
||||
expr.left
|
||||
else
|
||||
null
|
||||
}
|
||||
"xor" -> {
|
||||
if (leftVal != null && !leftVal.asBooleanValue)
|
||||
expr.right
|
||||
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||
expr.left
|
||||
else if (leftVal != null && leftVal.asBooleanValue)
|
||||
PrefixExpression("not", expr.right, expr.right.position)
|
||||
else if (rightVal != null && rightVal.asBooleanValue)
|
||||
PrefixExpression("not", expr.left, expr.left.position)
|
||||
else
|
||||
null
|
||||
}
|
||||
"|", "^" -> {
|
||||
if (leftVal != null && !leftVal.asBooleanValue)
|
||||
expr.right
|
||||
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||
expr.left
|
||||
else
|
||||
null
|
||||
}
|
||||
"&" -> {
|
||||
if (leftVal != null && !leftVal.asBooleanValue)
|
||||
constFalse
|
||||
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||
constFalse
|
||||
else
|
||||
null
|
||||
}
|
||||
"*" -> optimizeMultiplication(expr, leftVal, rightVal)
|
||||
"/" -> optimizeDivision(expr, leftVal, rightVal)
|
||||
"+" -> optimizeAdd(expr, leftVal, rightVal)
|
||||
"-" -> optimizeSub(expr, leftVal, rightVal)
|
||||
"**" -> optimizePower(expr, leftVal, rightVal)
|
||||
"%" -> optimizeRemainder(expr, leftVal, rightVal)
|
||||
">>" -> optimizeShiftRight(expr, rightVal)
|
||||
"<<" -> optimizeShiftLeft(expr, rightVal)
|
||||
else -> null
|
||||
}
|
||||
|
||||
if(newExpr != null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCall.target.nameInSource == listOf("lsb")) {
|
||||
val arg = functionCall.args[0]
|
||||
if(arg is TypecastExpression) {
|
||||
val valueDt = arg.expression.inferType(program)
|
||||
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
|
||||
// useless lsb() of byte value that was typecasted to word
|
||||
return listOf(IAstModification.ReplaceNode(functionCall, arg.expression, parent))
|
||||
}
|
||||
} else {
|
||||
val argDt = arg.inferType(program)
|
||||
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||
// useless lsb() of byte value
|
||||
return listOf(IAstModification.ReplaceNode(functionCall, arg, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(functionCall.target.nameInSource == listOf("msb")) {
|
||||
val arg = functionCall.args[0]
|
||||
if(arg is TypecastExpression) {
|
||||
val valueDt = arg.expression.inferType(program)
|
||||
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
|
||||
// useless msb() of byte value that was typecasted to word, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCall,
|
||||
NumericLiteralValue(valueDt.getOr(DataType.UBYTE), 0, arg.expression.position),
|
||||
parent))
|
||||
}
|
||||
} else {
|
||||
val argDt = arg.inferType(program)
|
||||
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||
// useless msb() of byte value, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCall,
|
||||
NumericLiteralValue(argDt.getOr(DataType.UBYTE), 0, arg.position),
|
||||
parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||
return when {
|
||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||
subBinExpr.right isSameAs x -> subBinExpr.left
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeAdd(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||
if(expr.left.isSameAs(expr.right)) {
|
||||
// optimize X+X into X *2
|
||||
expr.operator = "*"
|
||||
expr.right = NumericLiteralValue.optimalInteger(2, expr.right.position)
|
||||
expr.right.linkParents(expr)
|
||||
return expr
|
||||
}
|
||||
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||
if (rightVal2 != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val rightConst: NumericLiteralValue = rightVal2
|
||||
when (rightConst.number.toDouble()) {
|
||||
0.0 -> {
|
||||
// left
|
||||
return expr2.left
|
||||
}
|
||||
}
|
||||
}
|
||||
// no need to check for left val constant (because of associativity)
|
||||
|
||||
val rnum = rightVal?.number?.toDouble()
|
||||
if(rnum!=null && rnum<0.0) {
|
||||
expr.operator = "-"
|
||||
expr.right = NumericLiteralValue(rightVal.type, -rnum, rightVal.position)
|
||||
return expr
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||
if(expr.left.isSameAs(expr.right)) {
|
||||
// optimize X-X into 0
|
||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||
}
|
||||
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
if (rightVal != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val rnum = rightVal.number.toDouble()
|
||||
if (rnum == 0.0) {
|
||||
// left
|
||||
return expr.left
|
||||
}
|
||||
|
||||
if(rnum<0.0) {
|
||||
expr.operator = "+"
|
||||
expr.right = NumericLiteralValue(rightVal.type, -rnum, rightVal.position)
|
||||
return expr
|
||||
}
|
||||
}
|
||||
if (leftVal != null) {
|
||||
// left value is a constant, see if we can optimize
|
||||
when (leftVal.number.toDouble()) {
|
||||
0.0 -> {
|
||||
// -right
|
||||
return PrefixExpression("-", expr.right, expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
if (rightVal != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val rightConst: NumericLiteralValue = rightVal
|
||||
when (rightConst.number.toDouble()) {
|
||||
-3.0 -> {
|
||||
// -1/(left*left*left)
|
||||
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
|
||||
expr.position)
|
||||
}
|
||||
-2.0 -> {
|
||||
// -1/(left*left)
|
||||
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||
BinaryExpression(expr.left, "*", expr.left, expr.position),
|
||||
expr.position)
|
||||
}
|
||||
-1.0 -> {
|
||||
// -1/left
|
||||
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||
expr.left, expr.position)
|
||||
}
|
||||
0.0 -> {
|
||||
// 1
|
||||
return NumericLiteralValue(rightConst.type, 1, expr.position)
|
||||
}
|
||||
0.5 -> {
|
||||
// sqrt(left)
|
||||
return FunctionCall(IdentifierReference(listOf("sqrt"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
}
|
||||
1.0 -> {
|
||||
// left
|
||||
return expr.left
|
||||
}
|
||||
2.0 -> {
|
||||
// left*left
|
||||
return BinaryExpression(expr.left, "*", expr.left, expr.position)
|
||||
}
|
||||
3.0 -> {
|
||||
// left*left*left
|
||||
return BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (leftVal != null) {
|
||||
// left value is a constant, see if we can optimize
|
||||
when (leftVal.number.toDouble()) {
|
||||
-1.0 -> {
|
||||
// -1
|
||||
return NumericLiteralValue(DataType.FLOAT, -1.0, expr.position)
|
||||
}
|
||||
0.0 -> {
|
||||
// 0
|
||||
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
||||
}
|
||||
1.0 -> {
|
||||
//1
|
||||
return NumericLiteralValue(leftVal.type, 1, expr.position)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
// simplify assignments A = B <operator> C
|
||||
|
||||
val cv = rightVal?.number?.toInt()?.toDouble()
|
||||
when (expr.operator) {
|
||||
"%" -> {
|
||||
if (cv == 1.0) {
|
||||
val idt = expr.inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw FatalAstException("unknown dt")
|
||||
return NumericLiteralValue(idt.getOr(DataType.UNDEFINED), 0, expr.position)
|
||||
} else if (cv in powersOfTwo) {
|
||||
expr.operator = "&"
|
||||
expr.right = NumericLiteralValue.optimalInteger(cv!!.toInt()-1, expr.position)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
|
||||
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
// cannot shuffle assiciativity with division!
|
||||
if (rightVal != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val rightConst: NumericLiteralValue = rightVal
|
||||
val cv = rightConst.number.toDouble()
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
if (!leftIDt.isKnown)
|
||||
return null
|
||||
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
|
||||
when (cv) {
|
||||
-1.0 -> {
|
||||
// '/' -> -left
|
||||
if (expr.operator == "/") {
|
||||
return PrefixExpression("-", expr.left, expr.position)
|
||||
}
|
||||
}
|
||||
1.0 -> {
|
||||
// '/' -> left
|
||||
if (expr.operator == "/") {
|
||||
return expr.left
|
||||
}
|
||||
}
|
||||
in powersOfTwo -> {
|
||||
if (leftDt in IntegerDatatypes) {
|
||||
// divided by a power of two => shift right
|
||||
val numshifts = log2(cv).toInt()
|
||||
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
in negativePowersOfTwo -> {
|
||||
if (leftDt in IntegerDatatypes) {
|
||||
// divided by a negative power of two => negate, then shift right
|
||||
val numshifts = log2(-cv).toInt()
|
||||
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (leftDt == DataType.UBYTE) {
|
||||
if (abs(rightConst.number.toDouble()) >= 256.0) {
|
||||
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
||||
}
|
||||
} else if (leftDt == DataType.UWORD) {
|
||||
if (abs(rightConst.number.toDouble()) >= 65536.0) {
|
||||
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (leftVal != null) {
|
||||
// left value is a constant, see if we can optimize
|
||||
when (leftVal.number.toDouble()) {
|
||||
0.0 -> {
|
||||
// 0
|
||||
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun optimizeMultiplication(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||
if (rightVal2 != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val leftValue: Expression = expr2.left
|
||||
val rightConst: NumericLiteralValue = rightVal2
|
||||
when (val cv = rightConst.number.toDouble()) {
|
||||
-1.0 -> {
|
||||
// -left
|
||||
return PrefixExpression("-", leftValue, expr.position)
|
||||
}
|
||||
0.0 -> {
|
||||
// 0
|
||||
return NumericLiteralValue(rightConst.type, 0, expr.position)
|
||||
}
|
||||
1.0 -> {
|
||||
// left
|
||||
return expr2.left
|
||||
}
|
||||
in powersOfTwo -> {
|
||||
if (leftValue.inferType(program).isInteger) {
|
||||
// times a power of two => shift left
|
||||
val numshifts = log2(cv).toInt()
|
||||
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
in negativePowersOfTwo -> {
|
||||
if (leftValue.inferType(program).isInteger) {
|
||||
// times a negative power of two => negate, then shift left
|
||||
val numshifts = log2(-cv).toInt()
|
||||
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// no need to check for left val constant (because of associativity)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun optimizeShiftLeft(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression? {
|
||||
if (amountLv == null)
|
||||
return null
|
||||
|
||||
val amount = amountLv.number.toInt()
|
||||
if (amount == 0) {
|
||||
return expr.left
|
||||
}
|
||||
val targetIDt = expr.left.inferType(program)
|
||||
if(!targetIDt.isKnown)
|
||||
throw FatalAstException("unknown dt")
|
||||
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
if (amount >= 8) {
|
||||
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||
}
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if (amount >= 16) {
|
||||
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||
} else if (amount >= 8) {
|
||||
val lsb = FunctionCall(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
if (amount == 8) {
|
||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
||||
}
|
||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun optimizeShiftRight(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression? {
|
||||
if (amountLv == null)
|
||||
return null
|
||||
|
||||
val amount = amountLv.number.toInt()
|
||||
if (amount == 0) {
|
||||
return expr.left
|
||||
}
|
||||
val idt = expr.left.inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw FatalAstException("unknown dt")
|
||||
when (idt.getOr(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
if (amount >= 8) {
|
||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if (amount > 8) {
|
||||
expr.right = NumericLiteralValue.optimalInteger(8, expr.right.position)
|
||||
return null
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if (amount >= 16) {
|
||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||
}
|
||||
else if (amount >= 8) {
|
||||
val msb = FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
if (amount == 8) {
|
||||
// mkword(0, msb(v))
|
||||
val zero = NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(zero, msb), expr.position)
|
||||
}
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if (amount > 16) {
|
||||
expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position)
|
||||
return null
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants {
|
||||
if (expr.operator in associativeOperators && leftVal != null) {
|
||||
// swap left and right so that right is always the constant
|
||||
val tmp = expr.left
|
||||
expr.left = expr.right
|
||||
expr.right = tmp
|
||||
return BinExprWithConstants(expr, expr.right.constValue(program), leftVal)
|
||||
}
|
||||
return BinExprWithConstants(expr, leftVal, expr.right.constValue(program))
|
||||
}
|
||||
|
||||
private data class BinExprWithConstants(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
|
||||
|
||||
}
|
67
codeOptimizers/src/prog8/optimizer/Extensions.kt
Normal file
67
codeOptimizers/src/prog8/optimizer/Extensions.kt
Normal file
@ -0,0 +1,67 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.Program
|
||||
import prog8.compilerinterface.CompilationOptions
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
import prog8.compilerinterface.IErrorReporter
|
||||
|
||||
|
||||
fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||
valuetypefixer.visit(this)
|
||||
if(errors.noErrors()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
|
||||
replacer.visit(this)
|
||||
if (errors.noErrors()) {
|
||||
replacer.applyModifications()
|
||||
|
||||
valuetypefixer.visit(this)
|
||||
if(errors.noErrors()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val optimizer = ConstantFoldingOptimizer(this)
|
||||
optimizer.visit(this)
|
||||
while (errors.noErrors() && optimizer.applyModifications() > 0) {
|
||||
optimizer.visit(this)
|
||||
}
|
||||
|
||||
if (errors.noErrors()) {
|
||||
replacer.visit(this)
|
||||
replacer.applyModifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(errors.noErrors())
|
||||
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
||||
}
|
||||
|
||||
|
||||
fun Program.optimizeStatements(errors: IErrorReporter,
|
||||
functions: IBuiltinFunctions,
|
||||
compTarget: ICompilationTarget
|
||||
): Int {
|
||||
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
|
||||
optimizer.visit(this)
|
||||
val optimizationCount = optimizer.applyModifications()
|
||||
|
||||
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
||||
|
||||
return optimizationCount
|
||||
}
|
||||
|
||||
fun Program.simplifyExpressions() : Int {
|
||||
val opti = ExpressionSimplifier(this)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICompilationTarget) : Int {
|
||||
val opti = BinExprSplitter(this, options, compTarget)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
479
codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt
Normal file
479
codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt
Normal file
@ -0,0 +1,479 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
import prog8.compilerinterface.IErrorReporter
|
||||
import prog8.compilerinterface.size
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
class StatementOptimizer(private val program: Program,
|
||||
private val errors: IErrorReporter,
|
||||
private val functions: IBuiltinFunctions,
|
||||
private val compTarget: ICompilationTarget
|
||||
) : AstWalker() {
|
||||
|
||||
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
// if the first instruction in the called subroutine is a return statement with a simple value,
|
||||
// remove the jump altogeter and inline the returnvalue directly.
|
||||
val subroutine = functionCall.target.targetSubroutine(program)
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Return && first.value?.isSimple==true) {
|
||||
val copy = when(val orig = first.value!!) {
|
||||
is AddressOf -> {
|
||||
val scoped = scopePrefix(orig.identifier, subroutine)
|
||||
AddressOf(scoped, orig.position)
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
when(val expr = orig.addressExpression) {
|
||||
is NumericLiteralValue -> DirectMemoryRead(expr.copy(), orig.position)
|
||||
else -> return noModifications
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> scopePrefix(orig, subroutine)
|
||||
is NumericLiteralValue -> orig.copy()
|
||||
is StringLiteralValue -> orig.copy()
|
||||
else -> return noModifications
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(functionCall, copy, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun scopePrefix(variable: IdentifierReference, subroutine: Subroutine): IdentifierReference {
|
||||
val scoped = subroutine.makeScopedName(variable.nameInSource.last())
|
||||
return IdentifierReference(scoped.split('.'), variable.position)
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in functions.names) {
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
if (functionName in functions.purefunctionNames) {
|
||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
|
||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||
// only do this optimization if the arg is a known-constant string literal instead of a user defined variable.
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
arg.identifier
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
|
||||
val string = stringVar.targetVarDecl(program)?.value as? StringLiteralValue
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
val firstCharEncoded = compTarget.encodeString(string.value, string.altEncoding)[0]
|
||||
val chrout = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||
} else if (string.value.length == 2) {
|
||||
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||
val chrout1 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val chrout2 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
||||
val subroutine = functionCallStatement.target.targetSubroutine(program)
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Return)
|
||||
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
// override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
// // if the first instruction in the called subroutine is a return statement with constant value, replace with the constant value
|
||||
// val subroutine = functionCall.target.targetSubroutine(program)
|
||||
// if(subroutine!=null) {
|
||||
// val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
// if(first is Return && first.value!=null) {
|
||||
// val constval = first.value?.constValue(program)
|
||||
// if(constval!=null)
|
||||
// return listOf(IAstModification.ReplaceNode(functionCall, constval, parent))
|
||||
// }
|
||||
// }
|
||||
// return noModifications
|
||||
// }
|
||||
|
||||
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||
// remove empty if statements
|
||||
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isEmpty())
|
||||
return listOf(IAstModification.Remove(ifStatement, parent as IStatementContainer))
|
||||
|
||||
// empty true part? switch with the else part
|
||||
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isNotEmpty()) {
|
||||
val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
||||
val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
||||
val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifStatement.condition, invertedCondition, ifStatement),
|
||||
IAstModification.ReplaceNode(ifStatement.truepart, truepart, ifStatement),
|
||||
IAstModification.ReplaceNode(ifStatement.elsepart, emptyscope, ifStatement)
|
||||
)
|
||||
}
|
||||
|
||||
val constvalue = ifStatement.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only if-part
|
||||
errors.warn("condition is always true", ifStatement.position)
|
||||
listOf(IAstModification.ReplaceNode(ifStatement, ifStatement.truepart, parent))
|
||||
} else {
|
||||
// always false -> keep only else-part
|
||||
errors.warn("condition is always false", ifStatement.position)
|
||||
listOf(IAstModification.ReplaceNode(ifStatement, ifStatement.elsepart, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||
if(forLoop.body.isEmpty()) {
|
||||
errors.warn("removing empty for loop", forLoop.position)
|
||||
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||
} else if(forLoop.body.statements.size==1) {
|
||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
||||
// remove empty for loop (only loopvar decl in it)
|
||||
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
|
||||
val range = forLoop.iterable as? RangeExpr
|
||||
if(range!=null) {
|
||||
if (range.size() == 1) {
|
||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), range.from, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
|
||||
if(iterable!=null) {
|
||||
if(iterable.datatype==DataType.STR) {
|
||||
val sv = iterable.value as StringLiteralValue
|
||||
val size = sv.value.length
|
||||
if(size==1) {
|
||||
// loop over string of length 1 -> just assign the single character
|
||||
val character = compTarget.encodeString(sv.value, sv.altEncoding)[0]
|
||||
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
else if(iterable.datatype in ArrayDatatypes) {
|
||||
val size = iterable.arraysize!!.constIndex()
|
||||
if(size==1) {
|
||||
// loop over array of length 1 -> just assign the single value
|
||||
val av = (iterable.value as ArrayLiteralValue).value[0].constValue(program)?.number
|
||||
if(av!=null) {
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(
|
||||
AssignTarget(forLoop.loopVar, null, null, forLoop.position), NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
|
||||
forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||
val constvalue = untilLoop.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
if(constvalue.asBooleanValue) {
|
||||
// always true -> keep only the statement block (if there are no break statements)
|
||||
errors.warn("condition is always true", untilLoop.condition.position)
|
||||
if(!hasBreak(untilLoop.body))
|
||||
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
|
||||
} else {
|
||||
// always false
|
||||
val forever = RepeatLoop(null, untilLoop.body, untilLoop.position)
|
||||
return listOf(IAstModification.ReplaceNode(untilLoop, forever, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||
val constvalue = whileLoop.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue) {
|
||||
// always true
|
||||
val forever = RepeatLoop(null, whileLoop.body, whileLoop.position)
|
||||
listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
|
||||
} else {
|
||||
// always false -> remove the while statement altogether
|
||||
errors.warn("condition is always false", whileLoop.condition.position)
|
||||
listOf(IAstModification.Remove(whileLoop, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
||||
val iter = repeatLoop.iterations
|
||||
if(iter!=null) {
|
||||
if(repeatLoop.body.isEmpty()) {
|
||||
errors.warn("empty loop removed", repeatLoop.position)
|
||||
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||
}
|
||||
val iterations = iter.constValue(program)?.number?.toInt()
|
||||
if (iterations == 0) {
|
||||
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||
}
|
||||
if (iterations == 1) {
|
||||
errors.warn("iterations is always 1", iter.position)
|
||||
return listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||
// if the jump is to the next statement, remove the jump
|
||||
val scope = jump.parent as IStatementContainer
|
||||
val label = jump.identifier?.targetStatement(program)
|
||||
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
||||
return listOf(IAstModification.Remove(jump, scope))
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
if(binExpr.left isSameAs assignment.target) {
|
||||
val rExpr = binExpr.right as? BinaryExpression
|
||||
if(rExpr!=null) {
|
||||
val op1 = binExpr.operator
|
||||
val op2 = rExpr.operator
|
||||
|
||||
if(rExpr.left is NumericLiteralValue && op2 in associativeOperators) {
|
||||
// associative operator, make sure the constant numeric value is second (right)
|
||||
return listOf(IAstModification.SwapOperands(rExpr))
|
||||
}
|
||||
|
||||
val rNum = (rExpr.right as? NumericLiteralValue)?.number
|
||||
if(rNum!=null) {
|
||||
if (op1 == "+" || op1 == "-") {
|
||||
if (op2 == "+") {
|
||||
// A = A +/- B + N
|
||||
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||
val addConstant = Assignment(
|
||||
assignment.target,
|
||||
BinaryExpression(binExpr.left, "+", rExpr.right, rExpr.position),
|
||||
assignment.position
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||
IAstModification.InsertAfter(assignment, addConstant, parent as IStatementContainer))
|
||||
} else if (op2 == "-") {
|
||||
// A = A +/- B - N
|
||||
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||
val subConstant = Assignment(
|
||||
assignment.target,
|
||||
BinaryExpression(binExpr.left, "-", rExpr.right, rExpr.position),
|
||||
assignment.position
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||
IAstModification.InsertAfter(assignment, subConstant, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(binExpr.operator in associativeOperators && binExpr.right isSameAs assignment.target) {
|
||||
// associative operator, swap the operands so that the assignment target is first (left)
|
||||
// unless the other operand is the same in which case we don't swap (endless loop!)
|
||||
if (!(binExpr.left isSameAs binExpr.right))
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
if(assignment.target isSameAs assignment.value) {
|
||||
// remove assignment to self
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
}
|
||||
|
||||
val targetIDt = assignment.target.inferType(program)
|
||||
if(!targetIDt.isKnown)
|
||||
throw FatalAstException("can't infer type of assignment target")
|
||||
|
||||
// optimize binary expressions a bit
|
||||
val targetDt = targetIDt.getOr(DataType.UNDEFINED)
|
||||
val bexpr=assignment.value as? BinaryExpression
|
||||
if(bexpr!=null) {
|
||||
val rightCv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||
if (rightCv != null && assignment.target isSameAs bexpr.left) {
|
||||
// assignments of the form: X = X <operator> <expr>
|
||||
// remove assignments that have no effect (such as X=X+0)
|
||||
// optimize/rewrite some other expressions
|
||||
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program))?.type
|
||||
when (bexpr.operator) {
|
||||
"+" -> {
|
||||
if (rightCv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(rightCv.toInt()) {
|
||||
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if (rightCv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(rightCv.toInt()) {
|
||||
decs.statements.add(PostIncrDecr(assignment.target.copy(), "--", assignment.position))
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
"<<" -> {
|
||||
if (rightCv == 0.0)
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
}
|
||||
">>" -> {
|
||||
if (rightCv == 0.0)
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||
fun returnViaIntermediaryVar(value: Expression): Iterable<IAstModification>? {
|
||||
val subr = returnStmt.definingSubroutine!!
|
||||
val returnDt = subr.returntypes.single()
|
||||
if (returnDt in IntegerDatatypes) {
|
||||
// first assign to intermediary variable, then return that
|
||||
val returnVarName = "retval_interm_" + when(returnDt) {
|
||||
DataType.UBYTE -> "ub"
|
||||
DataType.BYTE -> "b"
|
||||
DataType.UWORD -> "uw"
|
||||
DataType.WORD -> "w"
|
||||
else -> "<undefined>"
|
||||
}
|
||||
val returnValueIntermediary = IdentifierReference(listOf("prog8_lib", returnVarName), returnStmt.position)
|
||||
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
||||
val assign = Assignment(tgt, value, returnStmt.position)
|
||||
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
when(returnStmt.value) {
|
||||
is PrefixExpression -> {
|
||||
val mod = returnViaIntermediaryVar(returnStmt.value!!)
|
||||
if(mod!=null)
|
||||
return mod
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
val mod = returnViaIntermediaryVar(returnStmt.value!!)
|
||||
if(mod!=null)
|
||||
return mod
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
return super.after(returnStmt, parent)
|
||||
}
|
||||
|
||||
private fun hasBreak(scope: IStatementContainer): Boolean {
|
||||
|
||||
class Searcher: IAstVisitor
|
||||
{
|
||||
var count=0
|
||||
|
||||
override fun visit(breakStmt: Break) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
val s=Searcher()
|
||||
for(stmt in scope.statements) {
|
||||
stmt.accept(s)
|
||||
if(s.count>0)
|
||||
return true
|
||||
}
|
||||
return s.count > 0
|
||||
}
|
||||
|
||||
}
|
167
codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt
Normal file
167
codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt
Normal file
@ -0,0 +1,167 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.expressions.PrefixExpression
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compilerinterface.CallGraph
|
||||
import prog8.compilerinterface.ICompilationTarget
|
||||
import prog8.compilerinterface.IErrorReporter
|
||||
import prog8.compilerinterface.isInRegularRAMof
|
||||
|
||||
|
||||
class UnusedCodeRemover(private val program: Program,
|
||||
private val errors: IErrorReporter,
|
||||
private val compTarget: ICompilationTarget
|
||||
): AstWalker() {
|
||||
|
||||
private val callgraph = CallGraph(program)
|
||||
|
||||
override fun before(module: Module, parent: Node): Iterable<IAstModification> {
|
||||
return if (!module.isLibrary && (module.containsNoCodeNorVars || callgraph.unused(module)))
|
||||
listOf(IAstModification.Remove(module, parent as IStatementContainer))
|
||||
else
|
||||
noModifications
|
||||
}
|
||||
|
||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(breakStmt, parent as IStatementContainer)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(jump, parent as IStatementContainer)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(returnStmt, parent as IStatementContainer)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource.last() == "exit")
|
||||
reportUnreachable(functionCallStatement, parent as IStatementContainer)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun reportUnreachable(stmt: Statement, parent: IStatementContainer) {
|
||||
when(val next = stmt.nextSibling()) {
|
||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
|
||||
else -> errors.warn("unreachable code", next.position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||
val removeDoubleAssignments = deduplicateAssignments(scope.statements)
|
||||
return removeDoubleAssignments.map { IAstModification.Remove(it, scope) }
|
||||
}
|
||||
|
||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars) {
|
||||
if(block.name != internedStringsModuleName)
|
||||
errors.warn("removing unused block '${block.name}'", block.position)
|
||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||
}
|
||||
if(callgraph.unused(block)) {
|
||||
errors.warn("removing unused block '${block.name}'", block.position)
|
||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
|
||||
val removeDoubleAssignments = deduplicateAssignments(block.statements)
|
||||
return removeDoubleAssignments.map { IAstModification.Remove(it, block) }
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
val forceOutput = "force_output" in subroutine.definingBlock.options()
|
||||
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
|
||||
if(callgraph.unused(subroutine)) {
|
||||
if(subroutine.containsNoCodeNorVars) {
|
||||
if(!subroutine.definingModule.isLibrary)
|
||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||
val removals = mutableListOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||
callgraph.calledBy[subroutine]?.let {
|
||||
for(node in it)
|
||||
removals.add(IAstModification.Remove(node, node.parent as IStatementContainer))
|
||||
}
|
||||
return removals
|
||||
}
|
||||
if(!subroutine.definingModule.isLibrary)
|
||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||
return listOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||
}
|
||||
}
|
||||
|
||||
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
|
||||
return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) }
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if(decl.type==VarDeclType.VAR) {
|
||||
val forceOutput = "force_output" in decl.definingBlock.options()
|
||||
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.sharedWithAsm && !decl.definingBlock.isInLibrary) {
|
||||
val usages = callgraph.usages(decl)
|
||||
if (usages.isEmpty()) {
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||
} else {
|
||||
// if all usages are just an assignment to this vardecl
|
||||
// and it is in regular RAM, then remove the var as well including all assignments
|
||||
val assignTargets = usages.mapNotNull {
|
||||
if(it.parent is AssignTarget)
|
||||
it.parent as AssignTarget
|
||||
else if(it.parent.parent is AssignTarget)
|
||||
it.parent.parent as AssignTarget
|
||||
else null
|
||||
}.filter {
|
||||
it.isInRegularRAMof(compTarget.machine)
|
||||
}
|
||||
if(assignTargets.size==usages.size) {
|
||||
// TODO FIX THAT A MEMREAD OF THE VARIABLE ISN'T RECOGNISED AS A USE (imageviewer iff_module.p8 pixptr)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
val assignmentsToRemove = assignTargets.map { it.parent to it.parent.parent as IStatementContainer}.toSet()
|
||||
return assignmentsToRemove.map {
|
||||
IAstModification.Remove(it.first, it.second)
|
||||
} + listOf(
|
||||
IAstModification.Remove(decl, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> {
|
||||
// removes 'duplicate' assignments that assign the same target directly after another
|
||||
val linesToRemove = mutableListOf<Assignment>()
|
||||
|
||||
for (stmtPairs in statements.windowed(2, step = 1)) {
|
||||
val assign1 = stmtPairs[0] as? Assignment
|
||||
val assign2 = stmtPairs[1] as? Assignment
|
||||
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
||||
if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAMof(compTarget.machine)) {
|
||||
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
|
||||
// only remove the second assignment if its value is a simple expression!
|
||||
when(assign2.value) {
|
||||
is PrefixExpression,
|
||||
is BinaryExpression,
|
||||
is TypecastExpression,
|
||||
is FunctionCall -> { /* don't remove */ }
|
||||
else -> linesToRemove.add(assign1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return linesToRemove
|
||||
}
|
||||
}
|
@ -1,54 +1,46 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.0'
|
||||
id "io.kotest" version "0.3.8"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
// id "org.jetbrains.kotlin.jvm" version $kotlinVersion
|
||||
id 'application'
|
||||
id 'org.jetbrains.dokka' version "0.9.18"
|
||||
id 'com.github.johnrengelman.shadow' version '5.1.0'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
apply plugin: "kotlin"
|
||||
apply plugin: "java"
|
||||
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
||||
|
||||
dependencies {
|
||||
implementation project(':parser')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
// runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
runtime 'org.antlr:antlr4-runtime:4.7.2'
|
||||
runtime project(':parser')
|
||||
implementation project(':compilerInterfaces')
|
||||
implementation project(':codeOptimizers')
|
||||
implementation project(':compilerAst')
|
||||
implementation project(':codeGeneration')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.3'
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
|
||||
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
|
||||
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
|
||||
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:4.6.3'
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
verbose = true
|
||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||
configurations.all {
|
||||
exclude group: 'com.ibm.icu', module: 'icu4j'
|
||||
exclude group: "org.antlr", module: "antlr4"
|
||||
}
|
||||
|
||||
configurations {
|
||||
// strange antlr plugin issue, see https://github.com/gradle/gradle/issues/820
|
||||
// this avoids linking in the complete antlr binary jar
|
||||
compile {
|
||||
extendsFrom = extendsFrom.findAll { it != configurations.antlr }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
@ -60,7 +52,8 @@ sourceSets {
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/test"]
|
||||
srcDir "${project.projectDir}/test"
|
||||
srcDir "${project(':compilerAst').projectDir}/test/helpers"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,32 +61,13 @@ sourceSets {
|
||||
startScripts.enabled = true
|
||||
|
||||
application {
|
||||
mainClassName = 'prog8.CompilerMainKt'
|
||||
mainClass = 'prog8.CompilerMainKt'
|
||||
applicationName = 'p8compile'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives shadowJar
|
||||
}
|
||||
|
||||
task p8vmScript(type: CreateStartScripts) {
|
||||
mainClassName = "prog8.vm.stackvm.MainKt"
|
||||
applicationName = "p8vm"
|
||||
outputDir = new File(project.buildDir, 'scripts')
|
||||
classpath = jar.outputs.files + project.configurations.runtime
|
||||
}
|
||||
|
||||
applicationDistribution.into("bin") {
|
||||
from(p8vmScript)
|
||||
fileMode = 0755
|
||||
}
|
||||
|
||||
// To create a fat-jar use the 'create_compiler_jar' script for now
|
||||
// @todo investigate https://imperceptiblethoughts.com/shadow/introduction/
|
||||
|
||||
shadowJar {
|
||||
baseName = 'prog8compiler'
|
||||
version = prog8version
|
||||
archiveBaseName = 'prog8compiler'
|
||||
archiveVersion = prog8version
|
||||
// minimize()
|
||||
}
|
||||
|
||||
@ -107,12 +81,8 @@ test {
|
||||
|
||||
// Show test results.
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
events "skipped", "failed"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dokka {
|
||||
outputFormat = 'html'
|
||||
outputDirectory = "$buildDir/kdoc"
|
||||
}
|
||||
build.finalizedBy installDist, installShadowDist
|
||||
|
@ -8,11 +8,16 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="antlr-runtime-4.7.2" level="project" />
|
||||
<orderEntry type="module" module-name="parser" />
|
||||
<orderEntry type="library" name="unittest-libs" level="project" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="codeOptimizers" />
|
||||
<orderEntry type="module" module-name="compilerInterfaces" />
|
||||
<orderEntry type="module" module-name="codeGeneration" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
20
compiler/res/.editorconfig
Normal file
20
compiler/res/.editorconfig
Normal file
@ -0,0 +1,20 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
tab_width = 8
|
||||
trim_trailing_whitespace = true
|
||||
ij_smart_tabs = true
|
||||
|
||||
[*.p8]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[*.asm]
|
||||
indent_size = 8
|
||||
indent_style = tab
|
Binary file not shown.
Before Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB |
680
compiler/res/prog8lib/c64/floats.asm
Normal file
680
compiler/res/prog8lib/c64/floats.asm
Normal file
@ -0,0 +1,680 @@
|
||||
; --- low level floating point assembly routines for the C64
|
||||
|
||||
FL_ONE_const .byte 129 ; 1.0
|
||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
|
||||
|
||||
floats_store_reg .byte 0 ; temp storage
|
||||
|
||||
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
b2float .proc
|
||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
uw2float .proc
|
||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
w2float .proc
|
||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
jsr GIVAYF
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_uw .proc
|
||||
; -- uword in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_w .proc
|
||||
; -- word in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_ub .proc
|
||||
; -- ubyte in Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADUY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_b .proc
|
||||
; -- byte in A into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
cast_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast float at A/Y to uword into Y/A
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_uw_into_ya
|
||||
.pend
|
||||
|
||||
cast_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast float at A/Y to word into A/Y
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_w_into_ay
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast fac1 to uword into Y/A
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR ; into Y/A
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast fac1 to word into A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr AYINT
|
||||
ldy $64
|
||||
lda $65
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
stack_b2float .proc
|
||||
; -- b2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADSA
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_w2float .proc
|
||||
; -- w2float operating on the stack
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
lda P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_ub2float .proc
|
||||
; -- ub2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tay
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_uw2float .proc
|
||||
; -- uw2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_float2w .proc ; also used for float2b
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr AYINT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
lda $64
|
||||
sta P8ESTACK_HI,x
|
||||
lda $65
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_float2uw .proc ; also used for float2ub
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
sta P8ESTACK_HI,x
|
||||
tya
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
push_float .proc
|
||||
; ---- push mflpt5 in A/Y onto stack
|
||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float .proc
|
||||
; ---- pops mflpt5 from stack to memory A/Y
|
||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #4
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float_fac1 .proc
|
||||
; -- pops float from stack into FAC1
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
sta _target+1
|
||||
sty _target+2
|
||||
ldy #4
|
||||
_loop lda (P8ZP_SCRATCH_W1),y
|
||||
_target sta $ffff,y ; modified
|
||||
dey
|
||||
bpl _loop
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
dec_var_f .proc
|
||||
; -- subtract 1 from float pointed to by A/Y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr FSUB
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
pop_2_floats_f2_in_fac1 .proc
|
||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
|
||||
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
|
||||
|
||||
push_fac1 .proc
|
||||
; -- push the float in FAC1 onto the stack
|
||||
stx P8ZP_SCRATCH_REG
|
||||
_internal ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
|
||||
pow_f .proc
|
||||
; -- push f1 ** f2 on stack
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr CONUPK ; fac2 = float1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr FPWR
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
div_f .proc
|
||||
; -- push f1/f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FDIV
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
add_f .proc
|
||||
; -- push f1+f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FADD
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
sub_f .proc
|
||||
; -- push f1-f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FSUB
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
mul_f .proc
|
||||
; -- push f1*f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FMULT
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
neg_f .proc
|
||||
; -- toggle the sign bit on the stack
|
||||
lda P8ESTACK_HI+3,x
|
||||
eor #$80
|
||||
sta P8ESTACK_HI+3,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_less_f .proc
|
||||
; -- is the float in FAC1 < the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_lesseq_f .proc
|
||||
; -- is the float in FAC1 <= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #255
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_greater_f .proc
|
||||
; -- is the float in FAC1 > the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #1
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_greatereq_f .proc
|
||||
; -- is the float in FAC1 >= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #1
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_notequal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
and #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_equal_f .proc
|
||||
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical?
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
lda #1
|
||||
rts
|
||||
_false lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
equal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack identical?
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
lda P8ESTACK_LO-3,x
|
||||
cmp P8ESTACK_LO,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bne _equals_false
|
||||
_equals_true lda #1
|
||||
_equals_store inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_equals_false lda #0
|
||||
beq _equals_store
|
||||
.pend
|
||||
|
||||
notequal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack different?
|
||||
jsr equal_f
|
||||
eor #1 ; invert the result
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_less_f .proc
|
||||
; -- is float in AY < float in P8ZP_SCRATCH_W2 ?
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
lda #1
|
||||
rts
|
||||
+ lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_lesseq_f .proc
|
||||
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ?
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
- lda #1
|
||||
rts
|
||||
+ cmp #0
|
||||
beq -
|
||||
lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_f .proc
|
||||
; -- is f1 < f2?
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
|
||||
lesseq_f .proc
|
||||
; -- is f1 <= f2?
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
cmp #0
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
greater_f .proc
|
||||
; -- is f1 > f2?
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
greatereq_f .proc
|
||||
; -- is f1 >= f2?
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
cmp #0
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
compare_floats .proc
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVFM ; fac1 = flt1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_return_false lda #0
|
||||
_return_result sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_return_true lda #1
|
||||
bne _return_result
|
||||
.pend
|
||||
|
||||
set_array_float_from_fac1 .proc
|
||||
; -- set the float in FAC1 in the array (index in A, array in P8ZP_SCRATCH_W1)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
iny
|
||||
+ stx floats_store_reg
|
||||
tax
|
||||
jsr MOVMF
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
set_0_array_float .proc
|
||||
; -- set a float in an array to zero (index in A, array in P8ZP_SCRATCH_W1)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
tay
|
||||
lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
set_array_float .proc
|
||||
; -- set a float in an array to a value (index in A, float in P8ZP_SCRATCH_W1, array in P8ZP_SCRATCH_W2)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
adc P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp copy_float
|
||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
.pend
|
||||
|
||||
|
201
compiler/res/prog8lib/c64/floats.p8
Normal file
201
compiler/res/prog8lib/c64/floats.p8
Normal file
@ -0,0 +1,201 @@
|
||||
; Prog8 definitions for floating point handling on the Commodore-64
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%option enable_floats
|
||||
|
||||
floats {
|
||||
; ---- this block contains C-64 floating point related functions ----
|
||||
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
float tempvar_swap_float ; used for some swap() operations
|
||||
|
||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||
|
||||
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
||||
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
||||
|
||||
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
romsub $bba2 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
||||
romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac1
|
||||
romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
||||
romsub $ba90 = FAREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac2
|
||||
romsub $bbfc = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||
romsub $bc0c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||
romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
|
||||
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
||||
; (tip: use floats.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
romsub $b1aa = FTOSWORDYA() clobbers(X) -> ubyte @ Y, ubyte @ A ; note: calls AYINT.
|
||||
|
||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use floats.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||
|
||||
romsub $bc9b = QINT() clobbers(A,X,Y) ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||
romsub $b1bf = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
; (tip: use floats.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||
; there is also floats.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also floats.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also floats.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||
romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||
romsub $bc3c = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length
|
||||
romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||
romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100)
|
||||
|
||||
romsub $b849 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
||||
romsub $bae2 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||
romsub $bafe = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
romsub $bc5b = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||
|
||||
romsub $b86a = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
||||
romsub $b867 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
|
||||
romsub $b853 = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands
|
||||
romsub $b850 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||
romsub $ba2b = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||
romsub $ba28 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||
romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
||||
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||
|
||||
romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1)
|
||||
romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||
romsub $b9ea = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||
romsub $bc39 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
romsub $bc2b = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
romsub $bc58 = ABS() ; fac1 = ABS(fac1)
|
||||
romsub $bf71 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||
romsub $bf74 = SQRA() clobbers(A,X,Y) ; fac1 = SQRT(fac2)
|
||||
romsub $bfed = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||
romsub $bfb4 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
||||
romsub $e097 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||
romsub $e264 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||
romsub $e26b = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||
romsub $e2b4 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||
romsub $e30e = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||
|
||||
|
||||
|
||||
asmsub FREADS32() clobbers(A,X,Y) {
|
||||
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
||||
%asm {{
|
||||
lda $62
|
||||
eor #$ff
|
||||
asl a
|
||||
lda #0
|
||||
ldx #$a0
|
||||
jmp $bc4f ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADUS32 () clobbers(A,X,Y) {
|
||||
; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST)
|
||||
%asm {{
|
||||
sec
|
||||
lda #0
|
||||
ldx #$a0
|
||||
jmp $bc4f ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADS24AXY (ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A,X,Y) {
|
||||
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
|
||||
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
|
||||
%asm {{
|
||||
sty $62
|
||||
stx $63
|
||||
sta $64
|
||||
lda $62
|
||||
eor #$FF
|
||||
asl a
|
||||
lda #0
|
||||
sta $65
|
||||
ldx #$98
|
||||
jmp $bc4f ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
sty $62
|
||||
sta $63
|
||||
ldx #$90
|
||||
sec
|
||||
jmp $bc49 ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to signed word in A/Y
|
||||
%asm {{
|
||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
%asm {{
|
||||
jsr GETADR ; this uses the inverse order, Y/A
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_f (float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%asm {{
|
||||
stx floats_store_reg
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM ; load float into fac1
|
||||
jsr FOUT ; fac1 to string in A/Y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx floats_store_reg
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
}
|
437
compiler/res/prog8lib/c64/floats_funcs.asm
Normal file
437
compiler/res/prog8lib/c64/floats_funcs.asm
Normal file
@ -0,0 +1,437 @@
|
||||
; --- floating point builtin functions
|
||||
|
||||
|
||||
abs_f_stack .proc
|
||||
; -- push abs(AY) on stack
|
||||
jsr floats.MOVFM
|
||||
jsr floats.ABS
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
abs_f_fac1 .proc
|
||||
; -- FAC1 = abs(AY)
|
||||
jsr floats.MOVFM
|
||||
jmp floats.ABS
|
||||
.pend
|
||||
|
||||
func_atan_stack .proc
|
||||
jsr func_atan_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_atan_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr ATN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_ceil_stack .proc
|
||||
jsr func_ceil_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_ceil_fac1 .proc
|
||||
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
jsr INT
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FCOMP
|
||||
cmp #0
|
||||
beq +
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_floor_stack .proc
|
||||
jsr func_floor_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_floor_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr INT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_round_stack .proc
|
||||
jsr func_round_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_round_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FADDH
|
||||
jsr INT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sin_stack .proc
|
||||
jsr func_sin_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_sin_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr SIN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_cos_stack .proc
|
||||
jsr func_cos_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_cos_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr COS
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_tan_stack .proc
|
||||
jsr func_tan_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_tan_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr TAN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_rad_stack .proc
|
||||
jsr func_rad_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_rad_fac1 .proc
|
||||
; -- convert degrees to radians (d * pi / 180)
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_pi_div_180
|
||||
ldy #>_pi_div_180
|
||||
jsr FMULT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
||||
.pend
|
||||
|
||||
func_deg_stack .proc
|
||||
jsr func_deg_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_deg_fac1 .proc
|
||||
; -- convert radians to degrees (d * (1/ pi * 180))
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_one_over_pi_div_180
|
||||
ldy #>_one_over_pi_div_180
|
||||
jsr FMULT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
||||
.pend
|
||||
|
||||
func_ln_stack .proc
|
||||
jsr func_ln_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_ln_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_log2_stack .proc
|
||||
jsr func_log2_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_log2_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
jsr MOVEF
|
||||
lda #<FL_LOG2_const
|
||||
ldy #>FL_LOG2_const
|
||||
jsr MOVFM
|
||||
jsr FDIVT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_f_stack .proc
|
||||
jsr func_sign_f_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_f_into_A .proc
|
||||
jsr MOVFM
|
||||
jmp SIGN
|
||||
.pend
|
||||
|
||||
func_sqrt_stack .proc
|
||||
jsr func_sqrt_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_sqrt_fac1 .proc
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr SQR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_rndf_stack .proc
|
||||
jsr func_rndf_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_rndf_fac1 .proc
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #1
|
||||
jsr FREADSA
|
||||
jsr RND ; rng into fac1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_swap_f .proc
|
||||
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
||||
ldy #4
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
pla
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_reverse_f .proc
|
||||
; --- reverse an array of floats (array in P8ZP_SCRATCH_W1, num elements in A)
|
||||
_left_index = P8ZP_SCRATCH_W2
|
||||
_right_index = P8ZP_SCRATCH_W2+1
|
||||
_loop_count = P8ZP_SCRATCH_REG
|
||||
pha
|
||||
jsr a_times_5
|
||||
sec
|
||||
sbc #5
|
||||
sta _right_index
|
||||
lda #0
|
||||
sta _left_index
|
||||
pla
|
||||
lsr a
|
||||
sta _loop_count
|
||||
_loop ; push the left indexed float on the stack
|
||||
ldy _left_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
; copy right index float to left index float
|
||||
ldy _right_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy _left_index
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
inc _left_index
|
||||
inc _right_index
|
||||
ldy _right_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy _left_index
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
inc _left_index
|
||||
inc _right_index
|
||||
ldy _right_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy _left_index
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
inc _left_index
|
||||
inc _right_index
|
||||
ldy _right_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy _left_index
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
inc _left_index
|
||||
inc _right_index
|
||||
ldy _right_index
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy _left_index
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
; pop the float off the stack into the right index float
|
||||
ldy _right_index
|
||||
pla
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
pla
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
pla
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
pla
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
pla
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
inc _left_index
|
||||
lda _right_index
|
||||
sec
|
||||
sbc #9
|
||||
sta _right_index
|
||||
dec _loop_count
|
||||
bne _loop
|
||||
rts
|
||||
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
a_times_5 .proc
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_any_f_into_A .proc
|
||||
jsr a_times_5
|
||||
jmp prog8_lib.func_any_b_into_A
|
||||
.pend
|
||||
|
||||
func_all_f_into_A .proc
|
||||
jsr a_times_5
|
||||
jmp prog8_lib.func_all_b_into_A
|
||||
.pend
|
||||
|
||||
func_any_f_stack .proc
|
||||
jsr a_times_5
|
||||
jmp prog8_lib.func_any_b_stack
|
||||
.pend
|
||||
|
||||
func_all_f_stack .proc
|
||||
jsr a_times_5
|
||||
jmp prog8_lib.func_all_b_stack
|
||||
.pend
|
||||
|
||||
func_max_f_stack .proc
|
||||
jsr func_max_f_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_max_f_fac1 .proc
|
||||
; -- max(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
|
||||
_loop_count = P8ZP_SCRATCH_REG
|
||||
stx floats_store_reg
|
||||
sta _loop_count
|
||||
lda #255
|
||||
sta _minmax_cmp+1 ; modifying
|
||||
lda #<_largest_neg_float
|
||||
ldy #>_largest_neg_float
|
||||
_minmax_entry jsr MOVFM
|
||||
- lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr FCOMP
|
||||
_minmax_cmp cmp #255 ; modified
|
||||
bne +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVFM
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
clc
|
||||
adc #5
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ dec _loop_count
|
||||
bne -
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
||||
.pend
|
||||
|
||||
func_min_f_stack .proc
|
||||
jsr func_min_f_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_min_f_fac1 .proc
|
||||
; -- min(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
|
||||
sta func_max_f_fac1._loop_count
|
||||
lda #1
|
||||
sta func_max_f_fac1._minmax_cmp+1
|
||||
lda #<_largest_pos_float
|
||||
ldy #>_largest_pos_float
|
||||
jmp func_max_f_fac1._minmax_entry
|
||||
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
func_sum_f_stack .proc
|
||||
jsr func_sum_f_fac1
|
||||
jmp push_fac1
|
||||
.pend
|
||||
|
||||
func_sum_f_fac1 .proc
|
||||
; -- sum(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
|
||||
_loop_count = P8ZP_SCRATCH_REG
|
||||
stx floats_store_reg
|
||||
sta _loop_count
|
||||
lda #<FL_ZERO_const
|
||||
ldy #>FL_ZERO_const
|
||||
jsr MOVFM
|
||||
- lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr FADD
|
||||
lda P8ZP_SCRATCH_W1
|
||||
clc
|
||||
adc #5
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ dec _loop_count
|
||||
bne -
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
.pend
|
369
compiler/res/prog8lib/c64/graphics.p8
Normal file
369
compiler/res/prog8lib/c64/graphics.p8
Normal file
@ -0,0 +1,369 @@
|
||||
%import textio
|
||||
|
||||
; bitmap pixel graphics module for the C64
|
||||
; only black/white monochrome 320x200 for now
|
||||
; assumes bitmap screen memory is $2000-$3fff
|
||||
|
||||
graphics {
|
||||
const uword BITMAP_ADDRESS = $2000
|
||||
const uword WIDTH = 320
|
||||
const ubyte HEIGHT = 200
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
c64.SCROLY = %00111011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||
clear_screen(1, 0)
|
||||
}
|
||||
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
c64.SCROLY = %00011011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
|
||||
txt.fill_screen(' ', 1)
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||
sys.memset(BITMAP_ADDRESS, 320*200/8, 0)
|
||||
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||
; Bresenham algorithm.
|
||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
}
|
||||
word @zp dx = (x2 as word)-x1
|
||||
word @zp dy = (y2 as word)-y1
|
||||
|
||||
if dx==0 {
|
||||
vertical_line(x1, y1, abs(dy) as ubyte +1)
|
||||
return
|
||||
}
|
||||
if dy==0 {
|
||||
if x1>x2
|
||||
x1=x2
|
||||
horizontal_line(x1, y1, abs(dx) as uword +1)
|
||||
return
|
||||
}
|
||||
|
||||
word @zp d = 0
|
||||
ubyte positive_ix = true
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
positive_ix = false
|
||||
}
|
||||
word @zp dx2 = dx*2
|
||||
word @zp dy2 = dy*2
|
||||
internal_plotx = x1
|
||||
|
||||
if dx >= dy {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if internal_plotx==x2
|
||||
return
|
||||
internal_plotx++
|
||||
d += dy2
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if internal_plotx==x2
|
||||
return
|
||||
internal_plotx--
|
||||
d += dy2
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
internal_plotx++
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
internal_plotx--
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub rect(uword x, ubyte y, uword width, ubyte height) {
|
||||
if width==0 or height==0
|
||||
return
|
||||
horizontal_line(x, y, width)
|
||||
if height==1
|
||||
return
|
||||
horizontal_line(x, y+height-1, width)
|
||||
vertical_line(x, y+1, height-2)
|
||||
if width==1
|
||||
return
|
||||
vertical_line(x+width-1, y+1, height-2)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, ubyte y, uword width, ubyte height) {
|
||||
if width==0
|
||||
return
|
||||
repeat height {
|
||||
horizontal_line(x, y, width)
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, ubyte y, uword length) {
|
||||
if length<8 {
|
||||
internal_plotx=x
|
||||
repeat lsb(length) {
|
||||
internal_plot(y)
|
||||
internal_plotx++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ubyte separate_pixels = lsb(x) & 7
|
||||
uword addr = get_y_lookup(y) + (x&$fff8)
|
||||
|
||||
if separate_pixels {
|
||||
%asm {{
|
||||
lda addr
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda addr+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
ldy separate_pixels
|
||||
lda _filled_right,y
|
||||
eor #255
|
||||
ldy #0
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
}}
|
||||
addr += 8
|
||||
length += separate_pixels
|
||||
length -= 8
|
||||
}
|
||||
|
||||
if length {
|
||||
%asm {{
|
||||
lda length
|
||||
and #7
|
||||
sta separate_pixels
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda addr
|
||||
sta _modified+1
|
||||
lda addr+1
|
||||
sta _modified+2
|
||||
lda length
|
||||
ora length+1
|
||||
beq _zero
|
||||
ldy length
|
||||
ldx #$ff
|
||||
_modified stx $ffff ; modified
|
||||
lda _modified+1
|
||||
clc
|
||||
adc #8
|
||||
sta _modified+1
|
||||
bcc +
|
||||
inc _modified+2
|
||||
+ dey
|
||||
bne _modified
|
||||
_zero ldx P8ZP_SCRATCH_REG
|
||||
|
||||
ldy separate_pixels
|
||||
beq _zero2
|
||||
lda _modified+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda _modified+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda _filled_right,y
|
||||
ldy #0
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
jmp _zero2
|
||||
_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110
|
||||
_zero2
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, ubyte y, ubyte height) {
|
||||
internal_plotx = x
|
||||
repeat height {
|
||||
internal_plot(y)
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Midpoint algorithm
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp ploty
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
|
||||
while radius>=yy {
|
||||
internal_plotx = xcenter + radius
|
||||
ploty = ycenter + yy
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - radius
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter + radius
|
||||
ploty = ycenter - yy
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - radius
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter + yy
|
||||
ploty = ycenter + radius
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - yy
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter + yy
|
||||
ploty = ycenter - radius
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - yy
|
||||
internal_plot(ploty)
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
else {
|
||||
radius--
|
||||
decisionOver2 += (yy as word -radius)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Midpoint algorithm, filled
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word decisionOver2 = (1 as word)-radius
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*2+1)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*2+1)
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
else {
|
||||
radius--
|
||||
decisionOver2 += (yy as word -radius)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; here is the non-asm code for the plot routine below:
|
||||
; sub plot_nonasm(uword px, ubyte py) {
|
||||
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
; uword addr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||
; @(addr) |= ormask[lsb(px) & 7]
|
||||
; }
|
||||
|
||||
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
||||
%asm {{
|
||||
stx graphics.internal_plotx
|
||||
sty graphics.internal_plotx+1
|
||||
jsr graphics.internal_plot
|
||||
}}
|
||||
}
|
||||
|
||||
; for efficiency of internal algorithms here is the internal plot routine
|
||||
; that takes the plotx coordinate in a separate variable instead of the XY register pair:
|
||||
|
||||
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
|
||||
|
||||
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||
%asm {{
|
||||
tay
|
||||
lda internal_plotx+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lsr a ; 0
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda internal_plotx
|
||||
pha
|
||||
and #7
|
||||
tax
|
||||
|
||||
lda _y_lookup_lo,y
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda _y_lookup_hi,y
|
||||
adc P8ZP_SCRATCH_W2+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
|
||||
pla ; internal_plotx
|
||||
and #%11111000
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora _ormask,x
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
|
||||
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
|
||||
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
||||
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||
|
||||
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
||||
|
||||
_y_lookup_lo .byte <_plot_y_values
|
||||
_y_lookup_hi .byte >_plot_y_values
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_y_lookup(ubyte y @Y) -> uword @AY {
|
||||
%asm {{
|
||||
lda internal_plot._y_lookup_lo,y
|
||||
pha
|
||||
lda internal_plot._y_lookup_hi,y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
786
compiler/res/prog8lib/c64/syslib.p8
Normal file
786
compiler/res/prog8lib/c64/syslib.p8
Normal file
@ -0,0 +1,786 @@
|
||||
; Prog8 definitions for the Commodore-64
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
c64 {
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||
|
||||
&ubyte COLOR = $0286 ; cursor color
|
||||
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
||||
&uword CINV = $0314 ; IRQ vector
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
; the default addresses for the character screen chars and colors
|
||||
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
&ubyte SPRPTR0 = 2040
|
||||
&ubyte SPRPTR1 = 2041
|
||||
&ubyte SPRPTR2 = 2042
|
||||
&ubyte SPRPTR3 = 2043
|
||||
&ubyte SPRPTR4 = 2044
|
||||
&ubyte SPRPTR5 = 2045
|
||||
&ubyte SPRPTR6 = 2046
|
||||
&ubyte SPRPTR7 = 2047
|
||||
&ubyte[8] SPRPTR = 2040 ; the 8 sprite pointers as an array.
|
||||
|
||||
|
||||
; ---- VIC-II 6567/6569/856x registers ----
|
||||
|
||||
&ubyte SP0X = $d000
|
||||
&ubyte SP0Y = $d001
|
||||
&ubyte SP1X = $d002
|
||||
&ubyte SP1Y = $d003
|
||||
&ubyte SP2X = $d004
|
||||
&ubyte SP2Y = $d005
|
||||
&ubyte SP3X = $d006
|
||||
&ubyte SP3Y = $d007
|
||||
&ubyte SP4X = $d008
|
||||
&ubyte SP4Y = $d009
|
||||
&ubyte SP5X = $d00a
|
||||
&ubyte SP5Y = $d00b
|
||||
&ubyte SP6X = $d00c
|
||||
&ubyte SP6Y = $d00d
|
||||
&ubyte SP7X = $d00e
|
||||
&ubyte SP7Y = $d00f
|
||||
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
|
||||
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
|
||||
|
||||
&ubyte MSIGX = $d010
|
||||
&ubyte SCROLY = $d011
|
||||
&ubyte RASTER = $d012
|
||||
&ubyte LPENX = $d013
|
||||
&ubyte LPENY = $d014
|
||||
&ubyte SPENA = $d015
|
||||
&ubyte SCROLX = $d016
|
||||
&ubyte YXPAND = $d017
|
||||
&ubyte VMCSB = $d018
|
||||
&ubyte VICIRQ = $d019
|
||||
&ubyte IREQMASK = $d01a
|
||||
&ubyte SPBGPR = $d01b
|
||||
&ubyte SPMC = $d01c
|
||||
&ubyte XXPAND = $d01d
|
||||
&ubyte SPSPCL = $d01e
|
||||
&ubyte SPBGCL = $d01f
|
||||
|
||||
&ubyte EXTCOL = $d020 ; border color
|
||||
&ubyte BGCOL0 = $d021 ; screen color
|
||||
&ubyte BGCOL1 = $d022
|
||||
&ubyte BGCOL2 = $d023
|
||||
&ubyte BGCOL4 = $d024
|
||||
&ubyte SPMC0 = $d025
|
||||
&ubyte SPMC1 = $d026
|
||||
&ubyte SP0COL = $d027
|
||||
&ubyte SP1COL = $d028
|
||||
&ubyte SP2COL = $d029
|
||||
&ubyte SP3COL = $d02a
|
||||
&ubyte SP4COL = $d02b
|
||||
&ubyte SP5COL = $d02c
|
||||
&ubyte SP6COL = $d02d
|
||||
&ubyte SP7COL = $d02e
|
||||
&ubyte[8] SPCOL = $d027
|
||||
|
||||
|
||||
; ---- end of VIC-II registers ----
|
||||
|
||||
; ---- CIA 6526 1 & 2 registers ----
|
||||
|
||||
&ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive (and joystick control port #2)
|
||||
&ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port (and joystick control port #1)
|
||||
&ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column
|
||||
&ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row
|
||||
&ubyte CIA1TAL = $DC04 ; CIA 1 timer A low byte
|
||||
&ubyte CIA1TAH = $DC05 ; CIA 1 timer A high byte
|
||||
&ubyte CIA1TBL = $DC06 ; CIA 1 timer B low byte
|
||||
&ubyte CIA1TBH = $DC07 ; CIA 1 timer B high byte
|
||||
&ubyte CIA1TOD10 = $DC08 ; time of day, 1/10 sec.
|
||||
&ubyte CIA1TODSEC = $DC09 ; time of day, seconds
|
||||
&ubyte CIA1TODMMIN = $DC0A ; time of day, minutes
|
||||
&ubyte CIA1TODHR = $DC0B ; time of day, hours
|
||||
&ubyte CIA1SDR = $DC0C ; Serial Data Register
|
||||
&ubyte CIA1ICR = $DC0D
|
||||
&ubyte CIA1CRA = $DC0E
|
||||
&ubyte CIA1CRB = $DC0F
|
||||
|
||||
&ubyte CIA2PRA = $DD00 ; CIA 2 DRA, serial port and video address
|
||||
&ubyte CIA2PRB = $DD01 ; CIA 2 DRB, RS232 port / USERPORT
|
||||
&ubyte CIA2DDRA = $DD02 ; CIA 2 DDRA, serial port and video address
|
||||
&ubyte CIA2DDRB = $DD03 ; CIA 2 DDRB, RS232 port / USERPORT
|
||||
&ubyte CIA2TAL = $DD04 ; CIA 2 timer A low byte
|
||||
&ubyte CIA2TAH = $DD05 ; CIA 2 timer A high byte
|
||||
&ubyte CIA2TBL = $DD06 ; CIA 2 timer B low byte
|
||||
&ubyte CIA2TBH = $DD07 ; CIA 2 timer B high byte
|
||||
&ubyte CIA2TOD10 = $DD08 ; time of day, 1/10 sec.
|
||||
&ubyte CIA2TODSEC = $DD09 ; time of day, seconds
|
||||
&ubyte CIA2TODMIN = $DD0A ; time of day, minutes
|
||||
&ubyte CIA2TODHR = $DD0B ; time of day, hours
|
||||
&ubyte CIA2SDR = $DD0C ; Serial Data Register
|
||||
&ubyte CIA2ICR = $DD0D
|
||||
&ubyte CIA2CRA = $DD0E
|
||||
&ubyte CIA2CRB = $DD0F
|
||||
|
||||
; ---- end of CIA registers ----
|
||||
|
||||
; ---- SID 6581/8580 registers ----
|
||||
|
||||
&ubyte FREQLO1 = $D400 ; channel 1 freq lo
|
||||
&ubyte FREQHI1 = $D401 ; channel 1 freq hi
|
||||
&uword FREQ1 = $D400 ; channel 1 freq (word)
|
||||
&ubyte PWLO1 = $D402 ; channel 1 pulse width lo (7-0)
|
||||
&ubyte PWHI1 = $D403 ; channel 1 pulse width hi (11-8)
|
||||
&uword PW1 = $D402 ; channel 1 pulse width (word)
|
||||
&ubyte CR1 = $D404 ; channel 1 voice control register
|
||||
&ubyte AD1 = $D405 ; channel 1 attack & decay
|
||||
&ubyte SR1 = $D406 ; channel 1 sustain & release
|
||||
&ubyte FREQLO2 = $D407 ; channel 2 freq lo
|
||||
&ubyte FREQHI2 = $D408 ; channel 2 freq hi
|
||||
&uword FREQ2 = $D407 ; channel 2 freq (word)
|
||||
&ubyte PWLO2 = $D409 ; channel 2 pulse width lo (7-0)
|
||||
&ubyte PWHI2 = $D40A ; channel 2 pulse width hi (11-8)
|
||||
&uword PW2 = $D409 ; channel 2 pulse width (word)
|
||||
&ubyte CR2 = $D40B ; channel 2 voice control register
|
||||
&ubyte AD2 = $D40C ; channel 2 attack & decay
|
||||
&ubyte SR2 = $D40D ; channel 2 sustain & release
|
||||
&ubyte FREQLO3 = $D40E ; channel 3 freq lo
|
||||
&ubyte FREQHI3 = $D40F ; channel 3 freq hi
|
||||
&uword FREQ3 = $D40E ; channel 3 freq (word)
|
||||
&ubyte PWLO3 = $D410 ; channel 3 pulse width lo (7-0)
|
||||
&ubyte PWHI3 = $D411 ; channel 3 pulse width hi (11-8)
|
||||
&uword PW3 = $D410 ; channel 3 pulse width (word)
|
||||
&ubyte CR3 = $D412 ; channel 3 voice control register
|
||||
&ubyte AD3 = $D413 ; channel 3 attack & decay
|
||||
&ubyte SR3 = $D414 ; channel 3 sustain & release
|
||||
&ubyte FCLO = $D415 ; filter cutoff lo (2-0)
|
||||
&ubyte FCHI = $D416 ; filter cutoff hi (10-3)
|
||||
&uword FC = $D415 ; filter cutoff (word)
|
||||
&ubyte RESFILT = $D417 ; filter resonance and routing
|
||||
&ubyte MVOL = $D418 ; filter mode and main volume control
|
||||
&ubyte POTX = $D419 ; potentiometer X
|
||||
&ubyte POTY = $D41A ; potentiometer Y
|
||||
&ubyte OSC3 = $D41B ; channel 3 oscillator value read
|
||||
&ubyte ENV3 = $D41C ; channel 3 envelope value read
|
||||
|
||||
; ---- end of SID registers ----
|
||||
|
||||
|
||||
; ---- C64 ROM kernal routines ----
|
||||
|
||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 ROM kernal routines ----
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
txa
|
||||
pha
|
||||
jsr c64.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr c64.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
; ---- C64 specific system utility routines: ----
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
lda #%00101111
|
||||
sta $00
|
||||
lda #%00100111
|
||||
sta $01
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
lda #6
|
||||
sta c64.EXTCOL
|
||||
lda #7
|
||||
sta c64.COLOR
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
jsr disable_runstop_and_charsetswitch
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
rts ; no phase 2 steps on the C64
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||
%asm {{
|
||||
lda #$80
|
||||
sta 657 ; disable charset switching
|
||||
lda #239
|
||||
sta 808 ; disable run/stop key
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
sta _use_kernal
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr _irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr _irq_handler_end
|
||||
lda _use_kernal
|
||||
bne +
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
|
||||
|
||||
_use_kernal .byte 0
|
||||
|
||||
_irq_handler_init
|
||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||
stx IRQ_X_REG
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta IRQ_SCRATCH_ZPB1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta IRQ_SCRATCH_ZPREG
|
||||
lda P8ZP_SCRATCH_W1
|
||||
sta IRQ_SCRATCH_ZPWORD1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sta IRQ_SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
cld
|
||||
rts
|
||||
|
||||
_irq_handler_end
|
||||
; restore all zp scratch registers and the X register
|
||||
lda IRQ_SCRATCH_ZPB1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda IRQ_SCRATCH_ZPREG
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda IRQ_SCRATCH_ZPWORD1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda IRQ_SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda IRQ_SCRATCH_ZPWORD2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda IRQ_SCRATCH_ZPWORD2+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
ldx IRQ_X_REG
|
||||
rts
|
||||
|
||||
IRQ_X_REG .byte 0
|
||||
IRQ_SCRATCH_ZPB1 .byte 0
|
||||
IRQ_SCRATCH_ZPREG .byte 0
|
||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<c64.IRQDFRT
|
||||
sta c64.CINV
|
||||
lda #>c64.IRQDFRT
|
||||
sta c64.CINV+1
|
||||
lda #0
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
sta set_irq._use_kernal
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
sei
|
||||
jsr _setup_raster_irq
|
||||
lda #<_raster_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irq._irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr set_irq._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda set_irq._use_kernal
|
||||
bne +
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with kernal irq routine
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
and c64.SCROLY
|
||||
sta c64.SCROLY ; clear most significant bit of raster position
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
pla
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
cpy #0
|
||||
beq +
|
||||
lda c64.SCROLY
|
||||
ora #%10000000
|
||||
sta c64.SCROLY ; set most significant bit of raster position
|
||||
+ lda #%00000001
|
||||
sta c64.IREQMASK ;enable raster interrupt signals from vic
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- end of C64 specific system utility routines ----
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 64 ; compilation target specifier. 64 = C64, 16 = CommanderX16.
|
||||
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
lda #14
|
||||
sta $01 ; bank the kernal in
|
||||
jmp (c64.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
|
||||
sub wait(uword jiffies) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
||||
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||
repeat jiffies {
|
||||
ubyte jiff = lsb(c64.RDTIM16())
|
||||
while jiff==lsb(c64.RDTIM16()) {
|
||||
; wait until 1 jiffy has passed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asmsub waitvsync() clobbers(A) {
|
||||
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||
; note: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
|
||||
%asm {{
|
||||
- bit c64.SCROLY
|
||||
bpl -
|
||||
- bit c64.SCROLY
|
||||
bmi -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub waitrastborder() {
|
||||
; --- busy wait till the raster position has reached the bottom screen border (approximately)
|
||||
; note: a more accurate way to do this is by using a raster irq handler instead.
|
||||
%asm {{
|
||||
- bit c64.SCROLY
|
||||
bpl -
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
ldx cx16.r0
|
||||
stx P8ZP_SCRATCH_W1 ; source in ZP
|
||||
ldx cx16.r0+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
stx P8ZP_SCRATCH_W2 ; target in ZP
|
||||
ldx cx16.r1+1
|
||||
stx P8ZP_SCRATCH_W2+1
|
||||
cpy #0
|
||||
bne _longcopy
|
||||
|
||||
; copy <= 255 bytes
|
||||
tay
|
||||
bne _copyshort
|
||||
rts ; nothing to copy
|
||||
|
||||
_copyshort
|
||||
; decrease source and target pointers so we can simply index by Y
|
||||
lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W1+1
|
||||
+ dec P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W2+1
|
||||
+ dec P8ZP_SCRATCH_W2
|
||||
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
|
||||
_longcopy
|
||||
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder in last page
|
||||
tya
|
||||
tax ; x = num pages (1+)
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
bne -
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
inc P8ZP_SCRATCH_W2+1
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
bne _copyshort
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memset(uword mem @R0, uword numbytes @R1, ubyte value @A) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
ldy cx16.r0
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy cx16.r0+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
ldy cx16.r1+1
|
||||
jmp prog8_lib.memset
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memsetw(uword mem @R0, uword numwords @R1, uword value @AY) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
ldx cx16.r0
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx cx16.r0+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
stx P8ZP_SCRATCH_W2
|
||||
ldx cx16.r1+1
|
||||
stx P8ZP_SCRATCH_W2+1
|
||||
jmp prog8_lib.memsetw
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
inline asmsub rsave() {
|
||||
; save cpu status flag and all registers A, X, Y.
|
||||
; see http://6502.org/tutorials/register_preservation.html
|
||||
%asm {{
|
||||
php
|
||||
sta P8ZP_SCRATCH_REG
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
lda P8ZP_SCRATCH_REG
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub rrestore() {
|
||||
; restore all registers and cpu status flag
|
||||
%asm {{
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
plp
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub read_flags() -> ubyte @A {
|
||||
%asm {{
|
||||
php
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_carry() {
|
||||
%asm {{
|
||||
clc
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_carry() {
|
||||
%asm {{
|
||||
sec
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_irqd() {
|
||||
%asm {{
|
||||
cli
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_irqd() {
|
||||
%asm {{
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
jsr c64.CLRCHN ; reset i/o channels
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progend() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_end
|
||||
ldy #>prog8_program_end
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the C64 as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
; they are allocated at the bottom of the eval-stack (should be ample space unless
|
||||
; you're doing insane nesting of expressions...)
|
||||
&uword r0 = $cf00
|
||||
&uword r1 = $cf02
|
||||
&uword r2 = $cf04
|
||||
&uword r3 = $cf06
|
||||
&uword r4 = $cf08
|
||||
&uword r5 = $cf0a
|
||||
&uword r6 = $cf0c
|
||||
&uword r7 = $cf0e
|
||||
&uword r8 = $cf10
|
||||
&uword r9 = $cf12
|
||||
&uword r10 = $cf14
|
||||
&uword r11 = $cf16
|
||||
&uword r12 = $cf18
|
||||
&uword r13 = $cf1a
|
||||
&uword r14 = $cf1c
|
||||
&uword r15 = $cf1e
|
||||
|
||||
&word r0s = $cf00
|
||||
&word r1s = $cf02
|
||||
&word r2s = $cf04
|
||||
&word r3s = $cf06
|
||||
&word r4s = $cf08
|
||||
&word r5s = $cf0a
|
||||
&word r6s = $cf0c
|
||||
&word r7s = $cf0e
|
||||
&word r8s = $cf10
|
||||
&word r9s = $cf12
|
||||
&word r10s = $cf14
|
||||
&word r11s = $cf16
|
||||
&word r12s = $cf18
|
||||
&word r13s = $cf1a
|
||||
&word r14s = $cf1c
|
||||
&word r15s = $cf1e
|
||||
|
||||
&ubyte r0L = $cf00
|
||||
&ubyte r1L = $cf02
|
||||
&ubyte r2L = $cf04
|
||||
&ubyte r3L = $cf06
|
||||
&ubyte r4L = $cf08
|
||||
&ubyte r5L = $cf0a
|
||||
&ubyte r6L = $cf0c
|
||||
&ubyte r7L = $cf0e
|
||||
&ubyte r8L = $cf10
|
||||
&ubyte r9L = $cf12
|
||||
&ubyte r10L = $cf14
|
||||
&ubyte r11L = $cf16
|
||||
&ubyte r12L = $cf18
|
||||
&ubyte r13L = $cf1a
|
||||
&ubyte r14L = $cf1c
|
||||
&ubyte r15L = $cf1e
|
||||
|
||||
&ubyte r0H = $cf01
|
||||
&ubyte r1H = $cf03
|
||||
&ubyte r2H = $cf05
|
||||
&ubyte r3H = $cf07
|
||||
&ubyte r4H = $cf09
|
||||
&ubyte r5H = $cf0b
|
||||
&ubyte r6H = $cf0d
|
||||
&ubyte r7H = $cf0f
|
||||
&ubyte r8H = $cf11
|
||||
&ubyte r9H = $cf13
|
||||
&ubyte r10H = $cf15
|
||||
&ubyte r11H = $cf17
|
||||
&ubyte r12H = $cf19
|
||||
&ubyte r13H = $cf1b
|
||||
&ubyte r14H = $cf1d
|
||||
&ubyte r15H = $cf1f
|
||||
|
||||
&byte r0sL = $cf00
|
||||
&byte r1sL = $cf02
|
||||
&byte r2sL = $cf04
|
||||
&byte r3sL = $cf06
|
||||
&byte r4sL = $cf08
|
||||
&byte r5sL = $cf0a
|
||||
&byte r6sL = $cf0c
|
||||
&byte r7sL = $cf0e
|
||||
&byte r8sL = $cf10
|
||||
&byte r9sL = $cf12
|
||||
&byte r10sL = $cf14
|
||||
&byte r11sL = $cf16
|
||||
&byte r12sL = $cf18
|
||||
&byte r13sL = $cf1a
|
||||
&byte r14sL = $cf1c
|
||||
&byte r15sL = $cf1e
|
||||
|
||||
&byte r0sH = $cf01
|
||||
&byte r1sH = $cf03
|
||||
&byte r2sH = $cf05
|
||||
&byte r3sH = $cf07
|
||||
&byte r4sH = $cf09
|
||||
&byte r5sH = $cf0b
|
||||
&byte r6sH = $cf0d
|
||||
&byte r7sH = $cf0f
|
||||
&byte r8sH = $cf11
|
||||
&byte r9sH = $cf13
|
||||
&byte r10sH = $cf15
|
||||
&byte r11sH = $cf17
|
||||
&byte r12sH = $cf19
|
||||
&byte r13sH = $cf1b
|
||||
&byte r14sH = $cf1d
|
||||
&byte r15sH = $cf1f
|
||||
}
|
617
compiler/res/prog8lib/c64/textio.p8
Normal file
617
compiler/res/prog8lib/c64/textio.p8
Normal file
@ -0,0 +1,617 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
|
||||
txt {
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
txt.chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
txt.chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
txt.chrout(' ')
|
||||
}
|
||||
|
||||
asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||
%asm {{
|
||||
sec
|
||||
jsr c64.PLOT
|
||||
tay
|
||||
clc
|
||||
jmp c64.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
; (assumes screen and color matrix are at their default addresses)
|
||||
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr clear_screencolors
|
||||
pla
|
||||
jsr clear_screenchars
|
||||
rts
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- sta c64.Screen+250*0-1,y
|
||||
sta c64.Screen+250*1-1,y
|
||||
sta c64.Screen+250*2-1,y
|
||||
sta c64.Screen+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- sta c64.Colors+250*0-1,y
|
||||
sta c64.Colors+250*1-1,y
|
||||
sta c64.Colors+250*2-1,y
|
||||
sta c64.Colors+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
c64.COLOR = txtcol
|
||||
}
|
||||
|
||||
sub lowercase() {
|
||||
c64.VMCSB |= 2
|
||||
}
|
||||
|
||||
sub uppercase() {
|
||||
c64.VMCSB &= ~2
|
||||
}
|
||||
|
||||
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
lda c64.Colors + 40*row + 1,x
|
||||
sta c64.Colors + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
lda c64.Colors + 40*row + 0,x
|
||||
sta c64.Colors + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row+1),x
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
txa
|
||||
jsr c64.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr c64.CHROUT
|
||||
_ones txa
|
||||
jsr c64.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr c64.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr c64.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubbin
|
||||
pla
|
||||
clc
|
||||
jmp print_ubbin
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubhex
|
||||
pla
|
||||
clc
|
||||
jmp print_ubhex
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
iny
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp c64.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
pla
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp print_uw
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr c64.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
iny
|
||||
bne -
|
||||
+ lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||
rts
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
|
||||
; ---- sets the character in the screen matrix at the given position
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word $0400 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the character in the screen matrix at the given location
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
||||
; ---- set the color in A on the screen matrix at the given position
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda _colorrows+1,y
|
||||
sta _mod+2
|
||||
txa
|
||||
clc
|
||||
adc _colorrows,y
|
||||
sta _mod+1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_colorrows .word $d800 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the color in the screen color matrix at the given location
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda setclr._colorrows+1,y
|
||||
sta _mod+2
|
||||
pla
|
||||
clc
|
||||
adc setclr._colorrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
adc #$d4
|
||||
sta _colormod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc column
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
+ lda char
|
||||
_charmod sta $ffff ; modified
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr c64.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
; -- returns the text screen width (number of columns)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
txa
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||
; -- returns the text screen height (number of rows)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
@ -1,959 +0,0 @@
|
||||
; Prog8 definitions for floating point handling on the Commodore-64
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%option enable_floats
|
||||
|
||||
|
||||
~ c64flt {
|
||||
; ---- this block contains C-64 floating point related functions ----
|
||||
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
|
||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||
|
||||
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
||||
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
||||
|
||||
; constants in five-byte "mflpt" format in the BASIC ROM
|
||||
&float FL_PIVAL = $aea8 ; 3.1415926...
|
||||
&float FL_N32768 = $b1a5 ; -32768
|
||||
&float FL_FONE = $b9bc ; 1
|
||||
&float FL_SQRHLF = $b9d6 ; SQR(2) / 2
|
||||
&float FL_SQRTWO = $b9db ; SQR(2)
|
||||
&float FL_NEGHLF = $b9e0 ; -.5
|
||||
&float FL_LOG2 = $b9e5 ; LOG(2)
|
||||
&float FL_TENC = $baf9 ; 10
|
||||
&float FL_NZMIL = $bdbd ; 1e9 (1 billion)
|
||||
&float FL_FHALF = $bf11 ; .5
|
||||
&float FL_LOGEB2 = $bfbf ; 1 / LOG(2)
|
||||
&float FL_PIHALF = $e2e0 ; PI / 2
|
||||
&float FL_TWOPI = $e2e5 ; 2 * PI
|
||||
&float FL_FR4 = $e2ea ; .25
|
||||
float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel
|
||||
|
||||
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
; checked functions below:
|
||||
asmsub MOVFM (uword mflpt @ AY) clobbers(A,Y) = $bba2 ; load mflpt value from memory in A/Y into fac1
|
||||
asmsub FREADMEM () clobbers(A,Y) = $bba6 ; load mflpt value from memory in $22/$23 into fac1
|
||||
asmsub CONUPK (uword mflpt @ AY) clobbers(A,Y) = $ba8c ; load mflpt value from memory in A/Y into fac2
|
||||
asmsub FAREADMEM () clobbers(A,Y) = $ba90 ; load mflpt value from memory in $22/$23 into fac2
|
||||
asmsub MOVFA () clobbers(A,X) = $bbfc ; copy fac2 to fac1
|
||||
asmsub MOVAF () clobbers(A,X) = $bc0c ; copy fac1 to fac2 (rounded)
|
||||
asmsub MOVEF () clobbers(A,X) = $bc0f ; copy fac1 to fac2
|
||||
asmsub MOVMF (uword mflpt @ XY) clobbers(A,Y) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
|
||||
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
||||
; (tip: use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
asmsub FTOSWORDYA () clobbers(X) -> ubyte @ Y, ubyte @ A = $b1aa
|
||||
|
||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
asmsub GETADR () clobbers(X) -> ubyte @ Y, ubyte @ A = $b7f7
|
||||
|
||||
asmsub QINT () clobbers(A,X,Y) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||
asmsub AYINT () clobbers(A,X,Y) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
; (tip: use c64flt.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
; there is also c64flt.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||
; there is also c64flt.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also c64flt.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also c64flt.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||
asmsub GIVAYF (ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y) = $b391
|
||||
|
||||
asmsub FREADUY (ubyte value @ Y) clobbers(A,X,Y) = $b3a2 ; 8 bit unsigned Y -> float in fac1
|
||||
asmsub FREADSA (byte value @ A) clobbers(A,X,Y) = $bc3c ; 8 bit signed A -> float in fac1
|
||||
asmsub FREADSTR (ubyte length @ A) clobbers(A,X,Y) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
|
||||
asmsub FPRINTLN () clobbers(A,X,Y) = $aabc ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||
asmsub FOUT () clobbers(X) -> uword @ AY = $bddd ; fac1 -> string, address returned in AY ($0100)
|
||||
|
||||
asmsub FADDH () clobbers(A,X,Y) = $b849 ; fac1 += 0.5, for rounding- call this before INT
|
||||
asmsub MUL10 () clobbers(A,X,Y) = $bae2 ; fac1 *= 10
|
||||
asmsub DIV10 () clobbers(A,X,Y) = $bafe ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
asmsub FCOMP (uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||
|
||||
asmsub FADDT () clobbers(A,X,Y) = $b86a ; fac1 += fac2
|
||||
asmsub FADD (uword mflpt @ AY) clobbers(A,X,Y) = $b867 ; fac1 += mflpt value from A/Y
|
||||
asmsub FSUBT () clobbers(A,X,Y) = $b853 ; fac1 = fac2-fac1 mind the order of the operands
|
||||
asmsub FSUB (uword mflpt @ AY) clobbers(A,X,Y) = $b850 ; fac1 = mflpt from A/Y - fac1
|
||||
asmsub FMULTT () clobbers(A,X,Y) = $ba2b ; fac1 *= fac2
|
||||
asmsub FMULT (uword mflpt @ AY) clobbers(A,X,Y) = $ba28 ; fac1 *= mflpt value from A/Y
|
||||
asmsub FDIVT () clobbers(A,X,Y) = $bb12 ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||
asmsub FDIV (uword mflpt @ AY) clobbers(A,X,Y) = $bb0f ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||
asmsub FPWRT () clobbers(A,X,Y) = $bf7b ; fac1 = fac2 ** fac1
|
||||
asmsub FPWR (uword mflpt @ AY) clobbers(A,X,Y) = $bf78 ; fac1 = fac2 ** mflpt from A/Y
|
||||
|
||||
asmsub NOTOP () clobbers(A,X,Y) = $aed4 ; fac1 = NOT(fac1)
|
||||
asmsub INT () clobbers(A,X,Y) = $bccc ; INT() truncates, use FADDH first to round instead of trunc
|
||||
asmsub LOG () clobbers(A,X,Y) = $b9ea ; fac1 = LN(fac1) (natural log)
|
||||
asmsub SGN () clobbers(A,X,Y) = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
asmsub SIGN () -> ubyte @ A = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
asmsub ABS () = $bc58 ; fac1 = ABS(fac1)
|
||||
asmsub SQR () clobbers(A,X,Y) = $bf71 ; fac1 = SQRT(fac1)
|
||||
asmsub SQRA () clobbers(A,X,Y) = $bf74 ; fac1 = SQRT(fac2)
|
||||
asmsub EXP () clobbers(A,X,Y) = $bfed ; fac1 = EXP(fac1) (e ** fac1)
|
||||
asmsub NEGOP () clobbers(A) = $bfb4 ; switch the sign of fac1
|
||||
asmsub RND () clobbers(A,X,Y) = $e097 ; fac1 = RND(fac1) float random number generator
|
||||
asmsub COS () clobbers(A,X,Y) = $e264 ; fac1 = COS(fac1)
|
||||
asmsub SIN () clobbers(A,X,Y) = $e26b ; fac1 = SIN(fac1)
|
||||
asmsub TAN () clobbers(A,X,Y) = $e2b4 ; fac1 = TAN(fac1)
|
||||
asmsub ATN () clobbers(A,X,Y) = $e30e ; fac1 = ATN(fac1)
|
||||
|
||||
|
||||
|
||||
|
||||
asmsub FREADS32 () clobbers(A,X,Y) {
|
||||
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
||||
%asm {{
|
||||
lda $62
|
||||
eor #$ff
|
||||
asl a
|
||||
lda #0
|
||||
ldx #$a0
|
||||
jmp $bc4f ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADUS32 () clobbers(A,X,Y) {
|
||||
; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST)
|
||||
%asm {{
|
||||
sec
|
||||
lda #0
|
||||
ldx #$a0
|
||||
jmp $bc4f ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADS24AXY (ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A,X,Y) {
|
||||
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
|
||||
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
|
||||
%asm {{
|
||||
sty $62
|
||||
stx $63
|
||||
sta $64
|
||||
lda $62
|
||||
eor #$FF
|
||||
asl a
|
||||
lda #0
|
||||
sta $65
|
||||
ldx #$98
|
||||
jmp $bc4f ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
sty $62
|
||||
sta $63
|
||||
ldx #$90
|
||||
sec
|
||||
jmp $bc49 ; internal BASIC routine
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
%asm {{
|
||||
sta c64.SCRATCH_ZPREG
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to signed word in A/Y
|
||||
%asm {{
|
||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||
sta c64.SCRATCH_ZPREG
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
%asm {{
|
||||
jsr GETADR ; this uses the inverse order, Y/A
|
||||
sta c64.SCRATCH_ZPB1
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPB1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_f (float value) {
|
||||
; ---- prints the floating point value (without a newline) using basic rom routines.
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<print_f_value
|
||||
ldy #>print_f_value
|
||||
jsr MOVFM ; load float into fac1
|
||||
jsr FOUT ; fac1 to string in A/Y
|
||||
jsr c64.STROUT ; print string in A/Y
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_fln (float value) {
|
||||
; ---- prints the floating point value (with a newline at the end) using basic rom routines
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<print_fln_value
|
||||
ldy #>print_fln_value
|
||||
jsr MOVFM ; load float into fac1
|
||||
jsr FPRINTLN ; print fac1 with newline
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
; --- low level floating point assembly routines
|
||||
%asm {{
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy c64.SCRATCH_ZPB1
|
||||
jsr FREADUY
|
||||
_fac_to_mem ldx c64.SCRATCH_ZPWORD2
|
||||
ldy c64.SCRATCH_ZPWORD2+1
|
||||
jsr MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
.pend
|
||||
|
||||
b2float .proc
|
||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPB1
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
uw2float .proc
|
||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
w2float .proc
|
||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy c64.SCRATCH_ZPWORD1
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
jsr GIVAYF
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
stack_b2float .proc
|
||||
; -- b2float operating on the stack
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr FREADSA
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
stack_w2float .proc
|
||||
; -- w2float operating on the stack
|
||||
inx
|
||||
ldy c64.ESTACK_LO,x
|
||||
lda c64.ESTACK_HI,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr GIVAYF
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
stack_ub2float .proc
|
||||
; -- ub2float operating on the stack
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
tay
|
||||
jsr FREADUY
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
stack_uw2float .proc
|
||||
; -- uw2float operating on the stack
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
ldy c64.ESTACK_HI,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr GIVUAYFAY
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
stack_float2w .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr AYINT
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
lda $64
|
||||
sta c64.ESTACK_HI,x
|
||||
lda $65
|
||||
sta c64.ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_float2uw .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr GETADR
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
sta c64.ESTACK_HI,x
|
||||
tya
|
||||
sta c64.ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
push_float .proc
|
||||
; ---- push mflpt5 in A/Y onto stack
|
||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_LO,x
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_LO,x
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_rndf .proc
|
||||
; -- put a random floating point value on the stack
|
||||
stx c64.SCRATCH_ZPREG
|
||||
lda #1
|
||||
jsr FREADSA
|
||||
jsr RND ; rng into fac1
|
||||
ldx #<_rndf_rnum5
|
||||
ldy #>_rndf_rnum5
|
||||
jsr MOVMF ; fac1 to mem X/Y
|
||||
ldx c64.SCRATCH_ZPREG
|
||||
lda #<_rndf_rnum5
|
||||
ldy #>_rndf_rnum5
|
||||
jmp push_float
|
||||
_rndf_rnum5 .byte 0,0,0,0,0
|
||||
.pend
|
||||
|
||||
push_float_from_indexed_var .proc
|
||||
; -- push the float from the array at A/Y with index on stack, onto the stack.
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
jsr prog8_lib.pop_index_times_5
|
||||
jsr prog8_lib.add_a_to_zpword
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
pop_float .proc
|
||||
; ---- pops mflpt5 from stack to memory A/Y
|
||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
ldy #4
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
inx
|
||||
lda c64.ESTACK_HI,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
lda c64.ESTACK_LO,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
inx
|
||||
lda c64.ESTACK_HI,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
lda c64.ESTACK_LO,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float_fac1 .proc
|
||||
; -- pops float from stack into FAC1
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
pop_float_to_indexed_var .proc
|
||||
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
jsr prog8_lib.pop_index_times_5
|
||||
jsr prog8_lib.add_a_to_zpword
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jmp pop_float
|
||||
.pend
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr MOVFM
|
||||
lda #<FL_FONE
|
||||
ldy #>FL_FONE
|
||||
jsr FADD
|
||||
ldx c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jsr MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
.pend
|
||||
|
||||
dec_var_f .proc
|
||||
; -- subtract 1 from float pointed to by A/Y
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<FL_FONE
|
||||
ldy #>FL_FONE
|
||||
jsr MOVFM
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jsr FSUB
|
||||
ldx c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jsr MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_indexed_var_f .proc
|
||||
; -- add 1 to float in array pointed to by A/Y, at index X
|
||||
pha
|
||||
txa
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
sta c64.SCRATCH_ZPB1
|
||||
pla
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp inc_var_f
|
||||
.pend
|
||||
|
||||
dec_indexed_var_f .proc
|
||||
; -- subtract 1 to float in array pointed to by A/Y, at index X
|
||||
pha
|
||||
txa
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
sta c64.SCRATCH_ZPB1
|
||||
pla
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp dec_var_f
|
||||
.pend
|
||||
|
||||
|
||||
pop_2_floats_f2_in_fac1 .proc
|
||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
|
||||
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
|
||||
push_fac1_as_result .proc
|
||||
; -- push the float in FAC1 onto the stack, and return from calculation
|
||||
ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
pow_f .proc
|
||||
; -- push f1 ** f2 on stack
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr CONUPK ; fac2 = float1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr FPWR
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
div_f .proc
|
||||
; -- push f1/f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FDIV
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
add_f .proc
|
||||
; -- push f1+f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FADD
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
sub_f .proc
|
||||
; -- push f1-f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FSUB
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
mul_f .proc
|
||||
; -- push f1*f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FMULT
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
neg_f .proc
|
||||
; -- push -flt back on stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr NEGOP
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
abs_f .proc
|
||||
; -- push abs(float) on stack (as float)
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr ABS
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
equal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack identical?
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
lda c64.ESTACK_LO-3,x
|
||||
cmp c64.ESTACK_LO,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_LO-2,x
|
||||
cmp c64.ESTACK_LO+1,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_LO-1,x
|
||||
cmp c64.ESTACK_LO+2,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_HI-2,x
|
||||
cmp c64.ESTACK_HI+1,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_HI-1,x
|
||||
cmp c64.ESTACK_HI+2,x
|
||||
bne _equals_false
|
||||
_equals_true lda #1
|
||||
_equals_store inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
_equals_false lda #0
|
||||
beq _equals_store
|
||||
.pend
|
||||
|
||||
notequal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack different?
|
||||
jsr equal_f
|
||||
eor #1 ; invert the result
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_f .proc
|
||||
; -- is f1 < f2?
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
|
||||
lesseq_f .proc
|
||||
; -- is f1 <= f2?
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
cmp #0
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
greater_f .proc
|
||||
; -- is f1 > f2?
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
greatereq_f .proc
|
||||
; -- is f1 >= f2?
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
cmp #0
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
compare_floats .proc
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVFM ; fac1 = flt1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
stx c64.SCRATCH_ZPREG
|
||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||
ldx c64.SCRATCH_ZPREG
|
||||
rts
|
||||
_return_false lda #0
|
||||
_return_result sta c64.ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_return_true lda #1
|
||||
bne _return_result
|
||||
.pend
|
||||
|
||||
func_sin .proc
|
||||
; -- push sin(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr SIN
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_cos .proc
|
||||
; -- push cos(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr COS
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_tan .proc
|
||||
; -- push tan(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr TAN
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_atan .proc
|
||||
; -- push atan(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr ATN
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_ln .proc
|
||||
; -- push ln(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr LOG
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_log2 .proc
|
||||
; -- push log base 2, ln(f)/ln(2), back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr LOG
|
||||
jsr MOVEF
|
||||
lda #<c64.FL_LOG2
|
||||
ldy #>c64.FL_LOG2
|
||||
jsr MOVFM
|
||||
jsr FDIVT
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_sqrt .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr SQR
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_rad .proc
|
||||
; -- convert degrees to radians (d * pi / 180)
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<_pi_div_180
|
||||
ldy #>_pi_div_180
|
||||
jsr FMULT
|
||||
jmp push_fac1_as_result
|
||||
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
||||
.pend
|
||||
|
||||
func_deg .proc
|
||||
; -- convert radians to degrees (d * (1/ pi * 180))
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<_one_over_pi_div_180
|
||||
ldy #>_one_over_pi_div_180
|
||||
jsr FMULT
|
||||
jmp push_fac1_as_result
|
||||
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
||||
.pend
|
||||
|
||||
func_round .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr FADDH
|
||||
jsr INT
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_floor .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr INT
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_ceil .proc
|
||||
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
jsr INT
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FCOMP
|
||||
cmp #0
|
||||
beq +
|
||||
lda #<FL_FONE
|
||||
ldy #>FL_FONE
|
||||
jsr FADD
|
||||
+ jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_any_f .proc
|
||||
inx
|
||||
lda c64.ESTACK_LO,x ; array size
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
||||
jmp prog8_lib.func_any_b._entry
|
||||
.pend
|
||||
|
||||
func_all_f .proc
|
||||
inx
|
||||
jsr prog8_lib.peek_address
|
||||
lda c64.ESTACK_LO,x ; array size
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
||||
tay
|
||||
dey
|
||||
- lda (c64.SCRATCH_ZPWORD1),y
|
||||
clc
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
cmp #0
|
||||
beq +
|
||||
cpy #255
|
||||
bne -
|
||||
lda #1
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
+ sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_max_f .proc
|
||||
lda #255
|
||||
sta _minmax_cmp+1
|
||||
lda #<_largest_neg_float
|
||||
ldy #>_largest_neg_float
|
||||
_minmax_entry jsr MOVFM
|
||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
- sty c64.SCRATCH_ZPREG
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jsr FCOMP
|
||||
_minmax_cmp cmp #255 ; modified
|
||||
bne +
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jsr MOVFM
|
||||
+ lda c64.SCRATCH_ZPWORD1
|
||||
clc
|
||||
adc #5
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
bcc +
|
||||
inc c64.SCRATCH_ZPWORD1+1
|
||||
+ ldy c64.SCRATCH_ZPREG
|
||||
dey
|
||||
cpy #255
|
||||
bne -
|
||||
jmp push_fac1_as_result
|
||||
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
||||
.pend
|
||||
|
||||
func_min_f .proc
|
||||
lda #1
|
||||
sta func_max_f._minmax_cmp+1
|
||||
lda #<_largest_pos_float
|
||||
ldy #>_largest_pos_float
|
||||
jmp func_max_f._minmax_entry
|
||||
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sum_f .proc
|
||||
lda #<FL_ZERO
|
||||
ldy #>FL_ZERO
|
||||
jsr MOVFM
|
||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
- sty c64.SCRATCH_ZPREG
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jsr FADD
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
dey
|
||||
cpy #255
|
||||
beq +
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
clc
|
||||
adc #5
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
bcc -
|
||||
inc c64.SCRATCH_ZPWORD1+1
|
||||
bne -
|
||||
+ jmp push_fac1_as_result
|
||||
.pend
|
||||
}}
|
||||
|
||||
} ; ------ end of block c64flt
|
@ -1,243 +0,0 @@
|
||||
; Prog8 definitions for the Commodore-64
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
|
||||
~ c64 {
|
||||
const uword ESTACK_LO = $ce00 ; evaluation stack (lsb)
|
||||
const uword ESTACK_HI = $cf00 ; evaluation stack (msb)
|
||||
&ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP
|
||||
&ubyte SCRATCH_ZPREG = $03 ; scratch register in ZP
|
||||
&ubyte SCRATCH_ZPREGX = $fa ; temp storage for X register (stack pointer)
|
||||
&uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
||||
&uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
||||
|
||||
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||
|
||||
&ubyte COLOR = $0286 ; cursor color
|
||||
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
||||
&uword CINV = $0314 ; IRQ vector
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
; the default addresses for the character screen chars and colors
|
||||
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
&ubyte SPRPTR0 = 2040
|
||||
&ubyte SPRPTR1 = 2041
|
||||
&ubyte SPRPTR2 = 2042
|
||||
&ubyte SPRPTR3 = 2043
|
||||
&ubyte SPRPTR4 = 2044
|
||||
&ubyte SPRPTR5 = 2045
|
||||
&ubyte SPRPTR6 = 2046
|
||||
&ubyte SPRPTR7 = 2047
|
||||
&ubyte[8] SPRPTR = 2040 ; the 8 sprite pointers as an array.
|
||||
|
||||
|
||||
; ---- VIC-II 6567/6569/856x registers ----
|
||||
|
||||
&ubyte SP0X = $d000
|
||||
&ubyte SP0Y = $d001
|
||||
&ubyte SP1X = $d002
|
||||
&ubyte SP1Y = $d003
|
||||
&ubyte SP2X = $d004
|
||||
&ubyte SP2Y = $d005
|
||||
&ubyte SP3X = $d006
|
||||
&ubyte SP3Y = $d007
|
||||
&ubyte SP4X = $d008
|
||||
&ubyte SP4Y = $d009
|
||||
&ubyte SP5X = $d00a
|
||||
&ubyte SP5Y = $d00b
|
||||
&ubyte SP6X = $d00c
|
||||
&ubyte SP6Y = $d00d
|
||||
&ubyte SP7X = $d00e
|
||||
&ubyte SP7Y = $d00f
|
||||
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
|
||||
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
|
||||
|
||||
&ubyte MSIGX = $d010
|
||||
&ubyte SCROLY = $d011
|
||||
&ubyte RASTER = $d012
|
||||
&ubyte LPENX = $d013
|
||||
&ubyte LPENY = $d014
|
||||
&ubyte SPENA = $d015
|
||||
&ubyte SCROLX = $d016
|
||||
&ubyte YXPAND = $d017
|
||||
&ubyte VMCSB = $d018
|
||||
&ubyte VICIRQ = $d019
|
||||
&ubyte IREQMASK = $d01a
|
||||
&ubyte SPBGPR = $d01b
|
||||
&ubyte SPMC = $d01c
|
||||
&ubyte XXPAND = $d01d
|
||||
&ubyte SPSPCL = $d01e
|
||||
&ubyte SPBGCL = $d01f
|
||||
|
||||
&ubyte EXTCOL = $d020 ; border color
|
||||
&ubyte BGCOL0 = $d021 ; screen color
|
||||
&ubyte BGCOL1 = $d022
|
||||
&ubyte BGCOL2 = $d023
|
||||
&ubyte BGCOL4 = $d024
|
||||
&ubyte SPMC0 = $d025
|
||||
&ubyte SPMC1 = $d026
|
||||
&ubyte SP0COL = $d027
|
||||
&ubyte SP1COL = $d028
|
||||
&ubyte SP2COL = $d029
|
||||
&ubyte SP3COL = $d02a
|
||||
&ubyte SP4COL = $d02b
|
||||
&ubyte SP5COL = $d02c
|
||||
&ubyte SP6COL = $d02d
|
||||
&ubyte SP7COL = $d02e
|
||||
&ubyte[8] SPCOL = $d027
|
||||
|
||||
|
||||
; ---- end of VIC-II registers ----
|
||||
|
||||
; ---- CIA 6526 1 & 2 registers ----
|
||||
|
||||
&ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive (and joystick control port #2)
|
||||
&ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port (and joystick control port #1)
|
||||
&ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column
|
||||
&ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row
|
||||
&ubyte CIA1TAL = $DC04 ; CIA 1 timer A low byte
|
||||
&ubyte CIA1TAH = $DC05 ; CIA 1 timer A high byte
|
||||
&ubyte CIA1TBL = $DC06 ; CIA 1 timer B low byte
|
||||
&ubyte CIA1TBH = $DC07 ; CIA 1 timer B high byte
|
||||
&ubyte CIA1TOD10 = $DC08 ; time of day, 1/10 sec.
|
||||
&ubyte CIA1TODSEC = $DC09 ; time of day, seconds
|
||||
&ubyte CIA1TODMMIN = $DC0A ; time of day, minutes
|
||||
&ubyte CIA1TODHR = $DC0B ; time of day, hours
|
||||
&ubyte CIA1SDR = $DC0C ; Serial Data Register
|
||||
&ubyte CIA1ICR = $DC0D
|
||||
&ubyte CIA1CRA = $DC0E
|
||||
&ubyte CIA1CRB = $DC0F
|
||||
|
||||
&ubyte CIA2PRA = $DD00 ; CIA 2 DRA, serial port and video address
|
||||
&ubyte CIA2PRB = $DD01 ; CIA 2 DRB, RS232 port / USERPORT
|
||||
&ubyte CIA2DDRA = $DD02 ; CIA 2 DDRA, serial port and video address
|
||||
&ubyte CIA2DDRB = $DD03 ; CIA 2 DDRB, RS232 port / USERPORT
|
||||
&ubyte CIA2TAL = $DD04 ; CIA 2 timer A low byte
|
||||
&ubyte CIA2TAH = $DD05 ; CIA 2 timer A high byte
|
||||
&ubyte CIA2TBL = $DD06 ; CIA 2 timer B low byte
|
||||
&ubyte CIA2TBH = $DD07 ; CIA 2 timer B high byte
|
||||
&ubyte CIA2TOD10 = $DD08 ; time of day, 1/10 sec.
|
||||
&ubyte CIA2TODSEC = $DD09 ; time of day, seconds
|
||||
&ubyte CIA2TODMIN = $DD0A ; time of day, minutes
|
||||
&ubyte CIA2TODHR = $DD0B ; time of day, hours
|
||||
&ubyte CIA2SDR = $DD0C ; Serial Data Register
|
||||
&ubyte CIA2ICR = $DD0D
|
||||
&ubyte CIA2CRA = $DD0E
|
||||
&ubyte CIA2CRB = $DD0F
|
||||
|
||||
; ---- end of CIA registers ----
|
||||
|
||||
; ---- SID 6581/8580 registers ----
|
||||
|
||||
&ubyte FREQLO1 = $D400 ; channel 1 freq lo
|
||||
&ubyte FREQHI1 = $D401 ; channel 1 freq hi
|
||||
&uword FREQ1 = $D400 ; channel 1 freq (word)
|
||||
&ubyte PWLO1 = $D402 ; channel 1 pulse width lo (7-0)
|
||||
&ubyte PWHI1 = $D403 ; channel 1 pulse width hi (11-8)
|
||||
&uword PW1 = $D402 ; channel 1 pulse width (word)
|
||||
&ubyte CR1 = $D404 ; channel 1 voice control register
|
||||
&ubyte AD1 = $D405 ; channel 1 attack & decay
|
||||
&ubyte SR1 = $D406 ; channel 1 sustain & release
|
||||
&ubyte FREQLO2 = $D407 ; channel 2 freq lo
|
||||
&ubyte FREQHI2 = $D408 ; channel 2 freq hi
|
||||
&uword FREQ2 = $D407 ; channel 2 freq (word)
|
||||
&ubyte PWLO2 = $D409 ; channel 2 pulse width lo (7-0)
|
||||
&ubyte PWHI2 = $D40A ; channel 2 pulse width hi (11-8)
|
||||
&uword PW2 = $D409 ; channel 2 pulse width (word)
|
||||
&ubyte CR2 = $D40B ; channel 2 voice control register
|
||||
&ubyte AD2 = $D40C ; channel 2 attack & decay
|
||||
&ubyte SR2 = $D40D ; channel 2 sustain & release
|
||||
&ubyte FREQLO3 = $D40E ; channel 3 freq lo
|
||||
&ubyte FREQHI3 = $D40F ; channel 3 freq hi
|
||||
&uword FREQ3 = $D40E ; channel 3 freq (word)
|
||||
&ubyte PWLO3 = $D410 ; channel 3 pulse width lo (7-0)
|
||||
&ubyte PWHI3 = $D411 ; channel 3 pulse width hi (11-8)
|
||||
&uword PW3 = $D410 ; channel 3 pulse width (word)
|
||||
&ubyte CR3 = $D412 ; channel 3 voice control register
|
||||
&ubyte AD3 = $D413 ; channel 3 attack & decay
|
||||
&ubyte SR3 = $D414 ; channel 3 sustain & release
|
||||
&ubyte FCLO = $D415 ; filter cutoff lo (2-0)
|
||||
&ubyte FCHI = $D416 ; filter cutoff hi (10-3)
|
||||
&uword FC = $D415 ; filter cutoff (word)
|
||||
&ubyte RESFILT = $D417 ; filter resonance and routing
|
||||
&ubyte MVOL = $D418 ; filter mode and main volume control
|
||||
&ubyte POTX = $D419 ; potentiometer X
|
||||
&ubyte POTY = $D41A ; potentiometer Y
|
||||
&ubyte OSC3 = $D41B ; channel 3 oscillator value read
|
||||
&ubyte ENV3 = $D41C ; channel 3 envelope value read
|
||||
|
||||
; ---- end of SID registers ----
|
||||
|
||||
|
||||
|
||||
; ---- C64 basic routines ----
|
||||
|
||||
asmsub CLEARSCR () clobbers(A,X,Y) = $E544 ; clear the screen
|
||||
asmsub HOMECRSR () clobbers(A,X,Y) = $E566 ; cursor to top left of screen
|
||||
|
||||
|
||||
; ---- end of C64 basic routines ----
|
||||
|
||||
|
||||
; ---- C64 kernal routines ----
|
||||
|
||||
asmsub STROUT (uword strptr @ AY) clobbers(A, X, Y) = $AB1E ; print null-terminated string (use c64scr.print instead)
|
||||
asmsub IRQDFRT () clobbers(A,X,Y) = $EA31 ; default IRQ routine
|
||||
asmsub IRQDFEND () clobbers(A,X,Y) = $EA81 ; default IRQ end/cleanup
|
||||
asmsub CINT () clobbers(A,X,Y) = $FF81 ; (alias: SCINIT) initialize screen editor and video chip
|
||||
asmsub IOINIT () clobbers(A, X) = $FF84 ; initialize I/O devices (CIA, SID, IRQ)
|
||||
asmsub RAMTAS () clobbers(A,X,Y) = $FF87 ; initialize RAM, tape buffer, screen
|
||||
asmsub RESTOR () clobbers(A,X,Y) = $FF8A ; restore default I/O vectors
|
||||
asmsub VECTOR (ubyte dir @ Pc, uword userptr @ XY) clobbers(A,Y) = $FF8D ; read/set I/O vector table
|
||||
asmsub SETMSG (ubyte value @ A) = $FF90 ; set Kernal message control flag
|
||||
asmsub SECOND (ubyte address @ A) clobbers(A) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
asmsub TKSA (ubyte address @ A) clobbers(A) = $FF96 ; (alias: TALKSA) send secondary address after TALK
|
||||
asmsub MEMTOP (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF99 ; read/set top of memory pointer
|
||||
asmsub MEMBOT (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF9C ; read/set bottom of memory pointer
|
||||
asmsub SCNKEY () clobbers(A,X,Y) = $FF9F ; scan the keyboard
|
||||
asmsub SETTMO (ubyte timeout @ A) = $FFA2 ; set time-out flag for IEEE bus
|
||||
asmsub ACPTR () -> ubyte @ A = $FFA5 ; (alias: IECIN) input byte from serial bus
|
||||
asmsub CIOUT (ubyte databyte @ A) = $FFA8 ; (alias: IECOUT) output byte to serial bus
|
||||
asmsub UNTLK () clobbers(A) = $FFAB ; command serial bus device to UNTALK
|
||||
asmsub UNLSN () clobbers(A) = $FFAE ; command serial bus device to UNLISTEN
|
||||
asmsub LISTEN (ubyte device @ A) clobbers(A) = $FFB1 ; command serial bus device to LISTEN
|
||||
asmsub TALK (ubyte device @ A) clobbers(A) = $FFB4 ; command serial bus device to TALK
|
||||
asmsub READST () -> ubyte @ A = $FFB7 ; read I/O status word
|
||||
asmsub SETLFS (ubyte logical @ A, ubyte device @ X, ubyte address @ Y) = $FFBA ; set logical file parameters
|
||||
asmsub SETNAM (ubyte namelen @ A, str filename @ XY) = $FFBD ; set filename parameters
|
||||
asmsub OPEN () clobbers(A,X,Y) = $FFC0 ; (via 794 ($31A)) open a logical file
|
||||
asmsub CLOSE (ubyte logical @ A) clobbers(A,X,Y) = $FFC3 ; (via 796 ($31C)) close a logical file
|
||||
asmsub CHKIN (ubyte logical @ X) clobbers(A,X) = $FFC6 ; (via 798 ($31E)) define an input channel
|
||||
asmsub CHKOUT (ubyte logical @ X) clobbers(A,X) = $FFC9 ; (via 800 ($320)) define an output channel
|
||||
asmsub CLRCHN () clobbers(A,X) = $FFCC ; (via 802 ($322)) restore default devices
|
||||
asmsub CHRIN () clobbers(Y) -> ubyte @ A = $FFCF ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
asmsub CHROUT (ubyte char @ A) = $FFD2 ; (via 806 ($326)) output a character
|
||||
asmsub LOAD (ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y = $FFD5 ; (via 816 ($330)) load from device
|
||||
asmsub SAVE (ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A = $FFD8 ; (via 818 ($332)) save to a device
|
||||
asmsub SETTIM (ubyte low @ A, ubyte middle @ X, ubyte high @ Y) = $FFDB ; set the software clock
|
||||
asmsub RDTIM () -> ubyte @ A, ubyte @ X, ubyte @ Y = $FFDE ; read the software clock
|
||||
asmsub STOP () clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc = $FFE1 ; (via 808 ($328)) check the STOP key
|
||||
asmsub GETIN () clobbers(X,Y) -> ubyte @ A = $FFE4 ; (via 810 ($32A)) get a character
|
||||
asmsub CLALL () clobbers(A,X) = $FFE7 ; (via 812 ($32C)) close all files
|
||||
asmsub UDTIM () clobbers(A,X) = $FFEA ; update the software clock
|
||||
asmsub SCREEN () -> ubyte @ X, ubyte @ Y = $FFED ; read number of screen rows and columns
|
||||
asmsub PLOT (ubyte dir @ Pc, ubyte col @ Y, ubyte row @ X) -> ubyte @ X, ubyte @ Y = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||
asmsub IOBASE () -> uword @ XY = $FFF3 ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 kernal routines ----
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
736
compiler/res/prog8lib/conv.p8
Normal file
736
compiler/res/prog8lib/conv.p8
Normal file
@ -0,0 +1,736 @@
|
||||
; Number conversions routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
|
||||
conv {
|
||||
|
||||
; ----- number conversions to decimal strings ----
|
||||
|
||||
str string_out = "????????????????" ; result buffer for the string conversion routines
|
||||
|
||||
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
sty string_out
|
||||
sta string_out+1
|
||||
stx string_out+2
|
||||
lda #0
|
||||
sta string_out+3
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- convert the ubyte in A in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_B1
|
||||
jsr conv.ubyte2decimal
|
||||
_output_byte_digits
|
||||
; hundreds?
|
||||
cpy #'0'
|
||||
beq +
|
||||
pha
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
sta string_out,y
|
||||
pla
|
||||
inc P8ZP_SCRATCH_B1
|
||||
; tens?
|
||||
+ ldy P8ZP_SCRATCH_B1
|
||||
cmp #'0'
|
||||
beq +
|
||||
sta string_out,y
|
||||
iny
|
||||
+ ; ones.
|
||||
txa
|
||||
sta string_out,y
|
||||
iny
|
||||
lda #0
|
||||
sta string_out,y
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||
; ---- convert the byte in A in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_B1
|
||||
cmp #0
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
sta string_out
|
||||
inc P8ZP_SCRATCH_B1
|
||||
pla
|
||||
+ jsr conv.byte2decimal
|
||||
bra str_ub._output_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- convert the ubyte in A in hex string form
|
||||
%asm {{
|
||||
jsr conv.ubyte2hex
|
||||
sta string_out
|
||||
sty string_out+1
|
||||
lda #0
|
||||
sta string_out+2
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ubbin (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- convert the ubyte in A in binary string form
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
sty string_out+8
|
||||
ldy #7
|
||||
- lsr P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
bne _digit
|
||||
+ lda #'0'
|
||||
_digit sta string_out,y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uwbin (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- convert the uword in A/Y in binary string form
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
jsr str_ubbin
|
||||
ldy #0
|
||||
sty string_out+16
|
||||
ldy #7
|
||||
- lsr P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
lda #'1'
|
||||
bne _digit
|
||||
+ lda #'0'
|
||||
_digit sta string_out+8,y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uwhex (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr conv.ubyte2hex
|
||||
sta string_out
|
||||
sty string_out+1
|
||||
pla
|
||||
jsr conv.ubyte2hex
|
||||
sta string_out+2
|
||||
sty string_out+3
|
||||
lda #0
|
||||
sta string_out+4
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
sta string_out,y
|
||||
beq +
|
||||
iny
|
||||
bne -
|
||||
+ plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uw (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
ldx #0
|
||||
_output_digits
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
iny
|
||||
bne -
|
||||
_gotdigit sta string_out,x
|
||||
inx
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
_end lda #0
|
||||
sta string_out,x
|
||||
plx
|
||||
rts
|
||||
|
||||
_allzero lda #'0'
|
||||
sta string_out,x
|
||||
inx
|
||||
bne _end
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_w (word value @ AY) clobbers(A,Y) {
|
||||
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl str_uw
|
||||
phx
|
||||
pha
|
||||
lda #'-'
|
||||
sta string_out
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
pla
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
bcc +
|
||||
iny
|
||||
+ jsr conv.uword2decimal
|
||||
ldx #1
|
||||
bne str_uw._output_digits
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
; ---- string conversion to numbers -----
|
||||
|
||||
asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
|
||||
; -- parses a string into a 16 bit unsigned number. String may be in decimal, hex or binary format.
|
||||
; (the latter two require a $ or % prefix to be recognised)
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; returns amount of processed characters in A, and the parsed number will be in cx16.r15.
|
||||
; if the string was invalid, 0 will be returned in A.
|
||||
%asm {{
|
||||
pha
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp #'$'
|
||||
beq _hex
|
||||
cmp #'%'
|
||||
beq _bin
|
||||
pla
|
||||
jsr str2uword
|
||||
jmp _result
|
||||
_hex pla
|
||||
jsr hex2uword
|
||||
jmp _result
|
||||
_bin pla
|
||||
jsr bin2uword
|
||||
_result
|
||||
pha
|
||||
lda cx16.r15
|
||||
sta P8ZP_SCRATCH_B1 ; result value
|
||||
pla
|
||||
sta cx16.r15
|
||||
sty cx16.r15+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub str2ubyte(str string @AY) clobbers(Y) -> ubyte @A {
|
||||
; -- returns in A the unsigned byte value of the string number argument in AY
|
||||
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; result in A, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
jsr conv.str2uword
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub str2byte(str string @AY) clobbers(Y) -> ubyte @A {
|
||||
; -- returns in A the signed byte value of the string number argument in AY
|
||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; result in A, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
jsr conv.str2word
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str2uword(str string @AY) -> uword @AY {
|
||||
; -- returns the unsigned word value of the string number argument in AY
|
||||
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; result in AY, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
_result = P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sty _result
|
||||
sty _result+1
|
||||
sty cx16.r15+1
|
||||
_loop
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sec
|
||||
sbc #48
|
||||
bpl _digit
|
||||
_done
|
||||
sty cx16.r15
|
||||
lda _result
|
||||
ldy _result+1
|
||||
rts
|
||||
_digit
|
||||
cmp #10
|
||||
bcs _done
|
||||
; add digit to result
|
||||
pha
|
||||
jsr _result_times_10
|
||||
pla
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
bcc +
|
||||
inc _result+1
|
||||
+ iny
|
||||
bne _loop
|
||||
; never reached
|
||||
|
||||
_result_times_10 ; (W*4 + W)*2
|
||||
lda _result+1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda _result
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc _result+1
|
||||
asl _result
|
||||
rol a
|
||||
sta _result+1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str2word(str string @AY) -> word @AY {
|
||||
; -- returns the signed word value of the string number argument in AY
|
||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; result in AY, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
_result = P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sty _result
|
||||
sty _result+1
|
||||
sty _negative
|
||||
sty cx16.r15+1
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
cmp #'+'
|
||||
bne +
|
||||
iny
|
||||
+ cmp #'-'
|
||||
bne _parse
|
||||
inc _negative
|
||||
iny
|
||||
_parse lda (P8ZP_SCRATCH_W2),y
|
||||
sec
|
||||
sbc #48
|
||||
bpl _digit
|
||||
_done
|
||||
sty cx16.r15
|
||||
lda _negative
|
||||
beq +
|
||||
sec
|
||||
lda #0
|
||||
sbc _result
|
||||
sta _result
|
||||
lda #0
|
||||
sbc _result+1
|
||||
sta _result+1
|
||||
+ lda _result
|
||||
ldy _result+1
|
||||
rts
|
||||
_digit
|
||||
cmp #10
|
||||
bcs _done
|
||||
; add digit to result
|
||||
pha
|
||||
jsr str2uword._result_times_10
|
||||
pla
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
bcc +
|
||||
inc _result+1
|
||||
+ iny
|
||||
bne _parse
|
||||
; never reached
|
||||
_negative .byte 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub hex2uword(str string @AY) -> uword @AY {
|
||||
; -- hexadecimal string (with or without '$') to uword.
|
||||
; string may be in petscii or c64-screencode encoding.
|
||||
; stops parsing at the first character that's not a hex digit (except leading $)
|
||||
; result in AY, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
sty cx16.r15+1
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
beq _stop
|
||||
cmp #'$'
|
||||
bne _loop
|
||||
iny
|
||||
_loop
|
||||
lda #0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
beq _stop
|
||||
cmp #7 ; screencode letters A-F are 1-6
|
||||
bcc _add_letter
|
||||
cmp #'g'
|
||||
bcs _stop
|
||||
cmp #'a'
|
||||
bcs _add_letter
|
||||
cmp #'0'
|
||||
bcc _stop
|
||||
cmp #'9'+1
|
||||
bcs _stop
|
||||
_calc
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
and #$0f
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
ora P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
iny
|
||||
bne _loop
|
||||
_stop
|
||||
sty cx16.r15
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
rts
|
||||
_add_letter
|
||||
pha
|
||||
lda #9
|
||||
sta P8ZP_SCRATCH_B1
|
||||
pla
|
||||
jmp _calc
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub bin2uword(str string @AY) -> uword @AY {
|
||||
; -- binary string (with or without '%') to uword.
|
||||
; stops parsing at the first character that's not a 0 or 1. (except leading %)
|
||||
; result in AY, number of characters processed also remains in cx16.r15 if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
sty cx16.r15+1
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
beq _stop
|
||||
cmp #'%'
|
||||
bne _loop
|
||||
iny
|
||||
_loop
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
cmp #'0'
|
||||
bcc _stop
|
||||
cmp #'2'
|
||||
bcs _stop
|
||||
_first asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
and #1
|
||||
ora P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
iny
|
||||
bne _loop
|
||||
_stop
|
||||
sty cx16.r15
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
; ----- low level number conversions to decimal strings ----
|
||||
|
||||
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
|
||||
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
||||
%asm {{
|
||||
ldy #uword2decimal.ASCII_0_OFFSET
|
||||
bne uword2decimal.hex_try200
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
|
||||
; ---- convert 16 bit uword in A/Y to decimal
|
||||
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
|
||||
; (these are terminated by a zero byte so they can be easily printed)
|
||||
; also returns Y = 100's, A = 10's, X = 1's
|
||||
|
||||
%asm {{
|
||||
|
||||
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
|
||||
;By Omegamatrix Further optimizations by tepples
|
||||
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
|
||||
|
||||
;HexToDec99
|
||||
; start in A
|
||||
; end with A = 10's, decOnes (also in X)
|
||||
|
||||
;HexToDec255
|
||||
; start in A
|
||||
; end with Y = 100's, A = 10's, decOnes (also in X)
|
||||
|
||||
;HexToDec999
|
||||
; start with A = high byte, Y = low byte
|
||||
; end with Y = 100's, A = 10's, decOnes (also in X)
|
||||
; requires 1 extra temp register on top of decOnes, could combine
|
||||
; these two if HexToDec65535 was eliminated...
|
||||
|
||||
;HexToDec65535
|
||||
; start with A/Y (low/high) as 16 bit value
|
||||
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
|
||||
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
|
||||
|
||||
|
||||
ASCII_0_OFFSET = $30
|
||||
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
|
||||
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
|
||||
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
|
||||
|
||||
|
||||
HexToDec65535; SUBROUTINE
|
||||
sty hexHigh ;3 @9
|
||||
sta hexLow ;3 @12
|
||||
tya
|
||||
tax ;2 @14
|
||||
lsr a ;2 @16
|
||||
lsr a ;2 @18 integer divide 1024 (result 0-63)
|
||||
|
||||
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
|
||||
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
|
||||
|
||||
;at this point we have a number 1-65 that we have to times by 24,
|
||||
;add to original sum, and Mod 1024 to get a remainder 0-999
|
||||
|
||||
|
||||
sta temp ;3 @25
|
||||
asl a ;2 @27
|
||||
adc temp ;3 @30 x3
|
||||
tay ;2 @32
|
||||
lsr a ;2 @34
|
||||
lsr a ;2 @36
|
||||
lsr a ;2 @38
|
||||
lsr a ;2 @40
|
||||
lsr a ;2 @42
|
||||
tax ;2 @44
|
||||
tya ;2 @46
|
||||
asl a ;2 @48
|
||||
asl a ;2 @50
|
||||
asl a ;2 @52
|
||||
clc ;2 @54
|
||||
adc hexLow ;3 @57
|
||||
sta hexLow ;3 @60
|
||||
txa ;2 @62
|
||||
adc hexHigh ;3 @65
|
||||
sta hexHigh ;3 @68
|
||||
ror a ;2 @70
|
||||
lsr a ;2 @72
|
||||
tay ;2 @74 integer divide 1,000 (result 0-65)
|
||||
|
||||
lsr a ;2 @76 split the 1,000 and 10,000 digit
|
||||
tax ;2 @78
|
||||
lda ShiftedBcdTab,x ;4 @82
|
||||
tax ;2 @84
|
||||
rol a ;2 @86
|
||||
and #$0F ;2 @88
|
||||
ora #ASCII_0_OFFSET
|
||||
sta decThousands ;3 @91
|
||||
txa ;2 @93
|
||||
lsr a ;2 @95
|
||||
lsr a ;2 @97
|
||||
lsr a ;2 @99
|
||||
ora #ASCII_0_OFFSET
|
||||
sta decTenThousands ;3 @102
|
||||
|
||||
lda hexLow ;3 @105
|
||||
cpy temp ;3 @108
|
||||
bmi _doSubtract ;2³ @110/111
|
||||
beq _useZero ;2³ @112/113
|
||||
adc #23 + 24 ;2 @114
|
||||
_doSubtract
|
||||
sbc #23 ;2 @116
|
||||
sta hexLow ;3 @119
|
||||
_useZero
|
||||
lda hexHigh ;3 @122
|
||||
sbc #0 ;2 @124
|
||||
|
||||
Start100s
|
||||
and #$03 ;2 @126
|
||||
tax ;2 @128 0,1,2,3
|
||||
cmp #2 ;2 @130
|
||||
rol a ;2 @132 0,2,5,7
|
||||
ora #ASCII_0_OFFSET
|
||||
tay ;2 @134 Y = Hundreds digit
|
||||
|
||||
lda hexLow ;3 @137
|
||||
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
|
||||
bcs hex_doSub200 ;2³ @143/144
|
||||
|
||||
hex_try200
|
||||
cmp #200 ;2 @145
|
||||
bcc hex_try100 ;2³ @147/148
|
||||
hex_doSub200
|
||||
iny ;2 @149
|
||||
iny ;2 @151
|
||||
sbc #200 ;2 @153
|
||||
hex_try100
|
||||
cmp #100 ;2 @155
|
||||
bcc HexToDec99 ;2³ @157/158
|
||||
iny ;2 @159
|
||||
sbc #100 ;2 @161
|
||||
|
||||
HexToDec99; SUBROUTINE
|
||||
lsr a ;2 @163
|
||||
tax ;2 @165
|
||||
lda ShiftedBcdTab,x ;4 @169
|
||||
tax ;2 @171
|
||||
rol a ;2 @173
|
||||
and #$0F ;2 @175
|
||||
ora #ASCII_0_OFFSET
|
||||
sta decOnes ;3 @178
|
||||
txa ;2 @180
|
||||
lsr a ;2 @182
|
||||
lsr a ;2 @184
|
||||
lsr a ;2 @186
|
||||
ora #ASCII_0_OFFSET
|
||||
|
||||
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
|
||||
sty decHundreds
|
||||
sta decTens
|
||||
ldx decOnes
|
||||
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
|
||||
|
||||
|
||||
HexToDec999; SUBROUTINE
|
||||
sty hexLow ;3 @9
|
||||
jmp Start100s ;3 @12
|
||||
|
||||
Mod100Tab
|
||||
.byte 0,56,12,56+12
|
||||
|
||||
ShiftedBcdTab
|
||||
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
|
||||
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
|
||||
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
|
||||
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
|
||||
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
|
||||
|
||||
decTenThousands .byte 0
|
||||
decThousands .byte 0
|
||||
decHundreds .byte 0
|
||||
decTens .byte 0
|
||||
decOnes .byte 0
|
||||
.byte 0 ; zero-terminate the decimal output string
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
|
||||
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
||||
; note: if the number is negative, you have to deal with the '-' yourself!
|
||||
%asm {{
|
||||
cmp #0
|
||||
bpl +
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
+ jmp ubyte2decimal
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
|
||||
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
and #$0f
|
||||
tax
|
||||
ldy _hex_digits,x
|
||||
pla
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
tax
|
||||
lda _hex_digits,x
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub uword2hex (uword value @AY) clobbers(A,Y) {
|
||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
jsr ubyte2hex
|
||||
sta output
|
||||
sty output+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
jsr ubyte2hex
|
||||
sta output+2
|
||||
sty output+3
|
||||
rts
|
||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
164
compiler/res/prog8lib/cx16/floats.p8
Normal file
164
compiler/res/prog8lib/cx16/floats.p8
Normal file
@ -0,0 +1,164 @@
|
||||
; Prog8 definitions for floating point handling on the CommanderX16
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%option enable_floats
|
||||
|
||||
floats {
|
||||
; ---- this block contains C-64 compatible floating point related functions ----
|
||||
; the addresses are from cx16 V39 emulator and roms! they won't work on older versions.
|
||||
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
float tempvar_swap_float ; used for some swap() operations
|
||||
|
||||
|
||||
; ---- ROM float functions ----
|
||||
|
||||
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
||||
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
||||
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
romsub $fe06 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||
|
||||
romsub $fe09 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
||||
romsub $fe0c = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||
romsub $fe0f = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands
|
||||
romsub $fe12 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
|
||||
romsub $fe15 = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
||||
romsub $fe1b = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
|
||||
romsub $fe1e = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
|
||||
romsub $fe24 = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||
romsub $fe27 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||
romsub $fe2a = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||
romsub $fe30 = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
|
||||
romsub $fe33 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||
romsub $fe36 = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
romsub $fe39 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||
romsub $fe3c = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||
|
||||
romsub $fe42 = MOVFM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1
|
||||
romsub $fe45 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
romsub $fe48 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||
romsub $fe4b = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||
romsub $fe4e = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||
romsub $fe54 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
romsub $fe57 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
romsub $fe5a = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||
romsub $fe66 = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
|
||||
romsub $fe69 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||
romsub $fe72 = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||
romsub $fe78 = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||
romsub $fe7b = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
romsub $fe81 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||
romsub $fe84 = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||
romsub $fe8a = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
||||
romsub $fe8d = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||
romsub $fe96 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||
romsub $fe99 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||
romsub $fe9c = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||
romsub $fe9f = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||
romsub $fea2 = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
phx
|
||||
sta _tmp
|
||||
sty P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy _tmp
|
||||
jsr GIVAYF ; load it as signed... correct afterwards
|
||||
lda P8ZP_SCRATCH_B1
|
||||
bpl +
|
||||
lda #<_flt65536
|
||||
ldy #>_flt65536
|
||||
jsr FADD
|
||||
+ plx
|
||||
rts
|
||||
_tmp .byte 0
|
||||
_flt65536 .byte 145,0,0,0,0 ; 65536.0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to signed word in A/Y
|
||||
%asm {{
|
||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
%asm {{
|
||||
jsr GETADR ; this uses the inverse order, Y/A
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADUY (ubyte value @Y) {
|
||||
; -- 8 bit unsigned Y -> float in fac1
|
||||
%asm {{
|
||||
lda #0
|
||||
jmp GIVAYF
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_f (float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%asm {{
|
||||
phx
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM ; load float into fac1
|
||||
jsr FOUT ; fac1 to string in A/Y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
}
|
989
compiler/res/prog8lib/cx16/gfx2.p8
Normal file
989
compiler/res/prog8lib/cx16/gfx2.p8
Normal file
@ -0,0 +1,989 @@
|
||||
; Bitmap pixel graphics routines for the CommanderX16
|
||||
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
|
||||
; (These modes are not supported by the documented GRAPH_xxxx kernal routines)
|
||||
;
|
||||
; No text layer is currently shown, text can be drawn as part of the bitmap itself.
|
||||
; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead.
|
||||
; Note: for color palette manipulation, use the "palette" module or write Vera registers yourself.
|
||||
; Note: this library implements code for various resolutions and color depths. This takes up memory.
|
||||
; If you're memory constrained you should probably not use this built-in library,
|
||||
; but make a copy in your project only containing the code for the required resolution.
|
||||
;
|
||||
;
|
||||
; SCREEN MODE LIST:
|
||||
; mode 0 = reset back to default text mode
|
||||
; mode 1 = bitmap 320 x 240 monochrome
|
||||
; mode 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
|
||||
; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
|
||||
; mode 4 = bitmap 320 x 240 x 256c
|
||||
; mode 5 = bitmap 640 x 480 monochrome
|
||||
; mode 6 = bitmap 640 x 480 x 4c
|
||||
; higher color dephts in highres are not supported due to lack of VRAM
|
||||
|
||||
|
||||
; TODO can we make a FB vector table and emulation routines for the Cx16s' GRAPH_init() call? to replace the builtin 320x200 fb driver?
|
||||
|
||||
gfx2 {
|
||||
|
||||
; read-only control variables:
|
||||
ubyte active_mode = 0
|
||||
uword width = 0
|
||||
uword height = 0
|
||||
ubyte bpp = 0
|
||||
ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
|
||||
|
||||
sub screen_mode(ubyte mode) {
|
||||
when mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
cx16.VERA_DC_VSCALE = 64
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0
|
||||
width = 320
|
||||
height = 240
|
||||
bpp = 1
|
||||
}
|
||||
; TODO modes 2, 3 not yet implemented
|
||||
4 -> {
|
||||
; lores 256c
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
cx16.VERA_DC_VSCALE = 64
|
||||
cx16.VERA_L1_CONFIG = %00000111
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0
|
||||
width = 320
|
||||
height = 240
|
||||
bpp = 8
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 128
|
||||
cx16.VERA_DC_VSCALE = 128
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = %00000001
|
||||
width = 640
|
||||
height = 480
|
||||
bpp = 1
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 128
|
||||
cx16.VERA_DC_VSCALE = 128
|
||||
cx16.VERA_L1_CONFIG = %00000101
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = %00000001
|
||||
width = 640
|
||||
height = 480
|
||||
bpp = 2
|
||||
}
|
||||
else -> {
|
||||
; back to default text mode and colors
|
||||
cx16.VERA_CTRL = %10000000 ; reset VERA and palette
|
||||
c64.CINT() ; back to text mode
|
||||
width = 0
|
||||
height = 0
|
||||
bpp = 0
|
||||
mode = 0
|
||||
}
|
||||
}
|
||||
|
||||
active_mode = mode
|
||||
if bpp
|
||||
clear_screen()
|
||||
}
|
||||
|
||||
sub clear_screen() {
|
||||
monochrome_stipple(false)
|
||||
position(0, 0)
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
repeat 240/2/8
|
||||
cs_innerloop640()
|
||||
}
|
||||
; TODO mode 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
repeat 240/2
|
||||
cs_innerloop640()
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
repeat 480/8
|
||||
cs_innerloop640()
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
repeat 480/4
|
||||
cs_innerloop640()
|
||||
}
|
||||
; modes 7 and 8 not supported due to lack of VRAM
|
||||
}
|
||||
position(0, 0)
|
||||
}
|
||||
|
||||
sub monochrome_stipple(ubyte enable) {
|
||||
monochrome_dont_stipple_flag = not enable
|
||||
}
|
||||
|
||||
sub rect(uword x, uword y, uword width, uword height, ubyte color) {
|
||||
if width==0 or height==0
|
||||
return
|
||||
horizontal_line(x, y, width, color)
|
||||
if height==1
|
||||
return
|
||||
horizontal_line(x, y+height-1, width, color)
|
||||
vertical_line(x, y+1, height-2, color)
|
||||
if width==1
|
||||
return
|
||||
vertical_line(x+width-1, y+1, height-2, color)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, uword y, uword width, uword height, ubyte color) {
|
||||
if width==0
|
||||
return
|
||||
repeat height {
|
||||
horizontal_line(x, y, width, color)
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, uword y, uword length, ubyte color) {
|
||||
if length==0
|
||||
return
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome modes, either resolution
|
||||
ubyte separate_pixels = (8-lsb(x)) & 7
|
||||
if separate_pixels as uword > length
|
||||
separate_pixels = lsb(length)
|
||||
repeat separate_pixels {
|
||||
; TODO optimize this by writing a masked byte in 1 go
|
||||
plot(x, y, color)
|
||||
x++
|
||||
}
|
||||
length -= separate_pixels
|
||||
if length {
|
||||
position(x, y)
|
||||
separate_pixels = lsb(length) & 7
|
||||
x += length & $fff8
|
||||
%asm {{
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda color
|
||||
bne +
|
||||
ldy #0 ; black
|
||||
bra _loop
|
||||
+ lda monochrome_dont_stipple_flag
|
||||
beq _stipple
|
||||
ldy #255 ; don't stipple
|
||||
bra _loop
|
||||
_stipple lda y
|
||||
and #1 ; determine stipple pattern to use
|
||||
bne +
|
||||
ldy #%01010101
|
||||
bra _loop
|
||||
+ ldy #%10101010
|
||||
_loop lda length
|
||||
ora length+1
|
||||
beq _done
|
||||
sty cx16.VERA_DATA0
|
||||
lda length
|
||||
bne +
|
||||
dec length+1
|
||||
+ dec length
|
||||
bra _loop
|
||||
_done
|
||||
}}
|
||||
repeat separate_pixels {
|
||||
; TODO optimize this by writing a masked byte in 1 go
|
||||
plot(x, y, color)
|
||||
x++
|
||||
}
|
||||
}
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
|
||||
}
|
||||
4 -> {
|
||||
; lores 256c
|
||||
position(x, y)
|
||||
%asm {{
|
||||
lda color
|
||||
phx
|
||||
ldx length+1
|
||||
beq +
|
||||
ldy #0
|
||||
- sta cx16.VERA_DATA0
|
||||
iny
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
+ ldy length ; remaining
|
||||
beq +
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
+ plx
|
||||
}}
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
; TODO also mostly usable for lores 4c?
|
||||
color &= 3
|
||||
ubyte[4] colorbits
|
||||
ubyte ii
|
||||
for ii in 3 downto 0 {
|
||||
colorbits[ii] = color
|
||||
color <<= 2
|
||||
}
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
lda cx16.VERA_ADDR_H
|
||||
and #%00000111 ; no auto advance
|
||||
sta cx16.VERA_ADDR_H
|
||||
stz cx16.VERA_CTRL ; setup vera addr 0
|
||||
lda cx16.r1
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
phx
|
||||
ldx x
|
||||
}}
|
||||
|
||||
repeat length {
|
||||
%asm {{
|
||||
txa
|
||||
and #3
|
||||
tay
|
||||
lda cx16.VERA_DATA0
|
||||
and gfx2.plot.mask4c,y
|
||||
ora colorbits,y
|
||||
sta cx16.VERA_DATA0
|
||||
cpy #%00000011 ; next vera byte?
|
||||
bne ++
|
||||
inc cx16.VERA_ADDR_L
|
||||
bne ++
|
||||
inc cx16.VERA_ADDR_M
|
||||
+ bne +
|
||||
inc cx16.VERA_ADDR_H
|
||||
+ inx ; next pixel
|
||||
}}
|
||||
}
|
||||
|
||||
%asm {{
|
||||
plx
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword height, ubyte color) {
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome, lo-res
|
||||
cx16.r15L = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||
if color {
|
||||
if monochrome_dont_stipple_flag {
|
||||
; draw continuous line.
|
||||
position2(x,y,true)
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat height {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
} else {
|
||||
; draw stippled line.
|
||||
if x&1 {
|
||||
y++
|
||||
height--
|
||||
}
|
||||
position2(x,y,true)
|
||||
if active_mode==1
|
||||
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(13) ; 160 increment = 2 line in 640 px monochrome
|
||||
repeat height/2 {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
position2(x,y,true)
|
||||
cx16.r15 = ~cx16.r15 ; erase pixels
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat height {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
and cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
; lores 256c
|
||||
; set vera auto-increment to 320 pixel increment (=next line)
|
||||
position(x,y)
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
||||
%asm {{
|
||||
ldy height
|
||||
beq +
|
||||
lda color
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
+
|
||||
}}
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
|
||||
if height==0
|
||||
return
|
||||
position2(x,y,true)
|
||||
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||
color &= 3
|
||||
color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||
repeat height {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
and mask
|
||||
ora color
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub set_both_strides(ubyte stride) {
|
||||
stride <<= 4
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
||||
; Bresenham algorithm.
|
||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
}
|
||||
word @zp dx = (x2 as word)-x1
|
||||
word @zp dy = (y2 as word)-y1
|
||||
|
||||
if dx==0 {
|
||||
vertical_line(x1, y1, abs(dy) as uword +1, color)
|
||||
return
|
||||
}
|
||||
if dy==0 {
|
||||
if x1>x2
|
||||
x1=x2
|
||||
horizontal_line(x1, y1, abs(dx) as uword +1, color)
|
||||
return
|
||||
}
|
||||
|
||||
word @zp d = 0
|
||||
cx16.r13 = true ; 'positive_ix'
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
cx16.r13 = false
|
||||
}
|
||||
word @zp dx2 = dx*2
|
||||
word @zp dy2 = dy*2
|
||||
cx16.r14 = x1 ; internal plot X
|
||||
|
||||
if dx >= dy {
|
||||
if cx16.r13 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, color)
|
||||
if cx16.r14==x2
|
||||
return
|
||||
cx16.r14++
|
||||
d += dy2
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, color)
|
||||
if cx16.r14==x2
|
||||
return
|
||||
cx16.r14--
|
||||
d += dy2
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if cx16.r13 {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, color)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
cx16.r14++
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, color)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
cx16.r14--
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub circle(uword @zp xcenter, uword @zp ycenter, ubyte radius, ubyte color) {
|
||||
; Midpoint algorithm.
|
||||
if radius==0
|
||||
return
|
||||
|
||||
ubyte @zp xx = radius
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-xx
|
||||
; R14 = internal plot X
|
||||
; R15 = internal plot Y
|
||||
|
||||
while xx>=yy {
|
||||
cx16.r14 = xcenter + xx
|
||||
cx16.r15 = ycenter + yy
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
cx16.r14 = xcenter - xx
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
cx16.r14 = xcenter + xx
|
||||
cx16.r15 = ycenter - yy
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
cx16.r14 = xcenter - xx
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
cx16.r14 = xcenter + yy
|
||||
cx16.r15 = ycenter + xx
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
cx16.r14 = xcenter - yy
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
cx16.r14 = xcenter + yy
|
||||
cx16.r15 = ycenter - xx
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
cx16.r14 = xcenter - yy
|
||||
plot(cx16.r14, cx16.r15, color)
|
||||
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
else {
|
||||
xx--
|
||||
decisionOver2 += (yy as word -xx)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, ubyte color) {
|
||||
; Midpoint algorithm, filled
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, color)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, color)
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
else {
|
||||
radius--
|
||||
decisionOver2 += (yy as word -radius)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub plot(uword @zp x, uword y, ubyte color) {
|
||||
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
ubyte[4] mask4c = [%00111111, %11001111, %11110011, %11111100]
|
||||
ubyte[4] shift4c = [6,4,2,0]
|
||||
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
ora monochrome_dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
cx16.r0L = lsb(x) & 7 ; xbits
|
||||
x /= 8
|
||||
x += y*(320/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ldy cx16.r0L ; xbits
|
||||
lda bits,y
|
||||
ldy color
|
||||
beq +
|
||||
tsb cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ trb cx16.VERA_DATA0
|
||||
+
|
||||
}}
|
||||
}
|
||||
}
|
||||
; TODO mode 2,3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1
|
||||
ora #%00010000 ; enable auto-increment so next_pixel() can be used after this
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda color
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
ora monochrome_dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
cx16.r0L = lsb(x) & 7 ; xbits
|
||||
x /= 8
|
||||
x += y*(640/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ldy cx16.r0L ; xbits
|
||||
lda bits,y
|
||||
ldy color
|
||||
beq +
|
||||
tsb cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ trb cx16.VERA_DATA0
|
||||
+
|
||||
}}
|
||||
}
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
; TODO also mostly usable for lores 4c?
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = lsb(x) & 3 ; xbits
|
||||
color &= 3
|
||||
color <<= shift4c[cx16.r2L]
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1L
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0H
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.r0L
|
||||
sta cx16.VERA_ADDR_L
|
||||
ldy cx16.r2L ; xbits
|
||||
lda mask4c,y
|
||||
and cx16.VERA_DATA0
|
||||
ora color
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub position(uword @zp x, uword y) {
|
||||
ubyte bank
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
cx16.r0 = y*(320/8) + x/8
|
||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||
}
|
||||
; TODO modes 2,3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
bank = lsb(cx16.r1)
|
||||
cx16.vaddr(bank, cx16.r0, 0, 1)
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
cx16.r0 = y*(640/8) + x/8
|
||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
bank = lsb(cx16.r1)
|
||||
cx16.vaddr(bank, cx16.r0, 0, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub position2(uword @zp x, uword y, ubyte also_port_1) {
|
||||
position(x, y)
|
||||
if also_port_1 {
|
||||
when active_mode {
|
||||
1, 5 -> cx16.vaddr(0, cx16.r0, 1, 1)
|
||||
; TODO modes 2, 3
|
||||
4, 6 -> {
|
||||
ubyte bank = lsb(cx16.r1)
|
||||
cx16.vaddr(bank, cx16.r0, 1, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline asmsub next_pixel(ubyte color @A) {
|
||||
; -- sets the next pixel byte to the graphics chip.
|
||||
; for 8 bpp screens this will plot 1 pixel.
|
||||
; for 1 bpp screens it will plot 8 pixels at once (color = bit pattern).
|
||||
; for 2 bpp screens it will plot 4 pixels at once (color = bit pattern).
|
||||
%asm {{
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, Y) {
|
||||
; -- sets the next bunch of pixels from a prepared array of bytes.
|
||||
; for 8 bpp screens this will plot 1 pixel per byte.
|
||||
; for 1 bpp screens it will plot 8 pixels at once (colors are the bit patterns per byte).
|
||||
; for 2 bpp screens it will plot 4 pixels at once (colors are the bit patterns per byte).
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r0+1
|
||||
beq +
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
bne -
|
||||
inc P8ZP_SCRATCH_W1+1 ; next page of 256 pixels
|
||||
dex
|
||||
bne -
|
||||
|
||||
+ ldx cx16.r0 ; remaining pixels
|
||||
beq +
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
dex
|
||||
bne -
|
||||
+ plx
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) {
|
||||
; this is only useful in 256 color mode where one pixel equals one byte value.
|
||||
%asm {{
|
||||
phx
|
||||
ldx #8
|
||||
- asl cx16.r0
|
||||
bcc +
|
||||
sta cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ sty cx16.VERA_DATA0
|
||||
+ dex
|
||||
bne -
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
const ubyte charset_orig_bank = $0
|
||||
const uword charset_orig_addr = $f800 ; in bank 0, so $0f800
|
||||
const ubyte charset_bank = $1
|
||||
const uword charset_addr = $f000 ; in bank 1, so $1f000
|
||||
|
||||
sub text_charset(ubyte charset) {
|
||||
; -- make a copy of the selected character set to use with text()
|
||||
; the charset number is the same as for the cx16.screen_set_charset() ROM function.
|
||||
; 1 = ISO charset, 2 = PETSCII uppercase+graphs, 3= PETSCII uppercase+lowercase.
|
||||
cx16.screen_set_charset(charset, 0)
|
||||
cx16.vaddr(charset_orig_bank, charset_orig_addr, 0, 1)
|
||||
cx16.vaddr(charset_bank, charset_addr, 1, 1)
|
||||
repeat 256*8 {
|
||||
cx16.VERA_DATA1 = cx16.VERA_DATA0
|
||||
}
|
||||
}
|
||||
|
||||
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to multiples of 8 ! TODO allow per-pixel horizontal positioning
|
||||
uword chardataptr
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome mode, either resolution
|
||||
cx16.r2 = 40
|
||||
if active_mode==5
|
||||
cx16.r2 = 80
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||
position(x,y)
|
||||
%asm {{
|
||||
lda cx16.VERA_ADDR_H
|
||||
and #%111 ; don't auto-increment, we have to do that manually because of the ora
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda color
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #8
|
||||
- lda P8ZP_SCRATCH_B1
|
||||
bne + ; white color, plot normally
|
||||
lda cx16.VERA_DATA1
|
||||
eor #255 ; black color, keep only the other pixels
|
||||
and cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ lda cx16.VERA_DATA0
|
||||
ora cx16.VERA_DATA1
|
||||
+ sta cx16.VERA_DATA0
|
||||
lda cx16.VERA_ADDR_L
|
||||
clc
|
||||
adc cx16.r2
|
||||
sta cx16.VERA_ADDR_L
|
||||
bcc +
|
||||
inc cx16.VERA_ADDR_M
|
||||
+ lda x
|
||||
clc
|
||||
adc #1
|
||||
sta x
|
||||
bcc +
|
||||
inc x+1
|
||||
+ dey
|
||||
bne -
|
||||
}}
|
||||
sctextptr++
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
; lores 256c
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||
repeat 8 {
|
||||
; TODO rewrite this inner loop fully in assembly
|
||||
position(x,y)
|
||||
y++
|
||||
%asm {{
|
||||
phx
|
||||
ldx #1
|
||||
lda cx16.VERA_DATA1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #8
|
||||
- asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
stx cx16.VERA_DATA0 ; write a pixel
|
||||
bra ++
|
||||
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
|
||||
+ dey
|
||||
bne -
|
||||
plx
|
||||
}}
|
||||
}
|
||||
x+=8
|
||||
y-=8
|
||||
sctextptr++
|
||||
}
|
||||
}
|
||||
6 -> {
|
||||
; hires 4c
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
repeat 8 {
|
||||
; TODO rewrite this inner loop fully in assembly
|
||||
ubyte charbits = cx16.vpeek(charset_bank, chardataptr)
|
||||
repeat 8 {
|
||||
charbits <<= 1
|
||||
if_cs
|
||||
plot(x, y, color)
|
||||
x++
|
||||
}
|
||||
x-=8
|
||||
chardataptr++
|
||||
y++
|
||||
}
|
||||
x+=8
|
||||
y-=8
|
||||
sctextptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asmsub cs_innerloop640() clobbers(Y) {
|
||||
%asm {{
|
||||
ldy #80
|
||||
- stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub addr_mul_24_for_highres_4c(uword yy @R2, uword xx @R3) clobbers(A, Y) -> uword @R0, uword @R1 {
|
||||
; yy * 160 + xx/4 (24 bits calculation)
|
||||
; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
ldy #5
|
||||
- asl cx16.r2
|
||||
rol cx16.r2+1
|
||||
dey
|
||||
bne -
|
||||
lda cx16.r2
|
||||
sta cx16.r0
|
||||
lda cx16.r2+1
|
||||
sta cx16.r0+1
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
|
||||
; xx >>= 2 (xx=R3)
|
||||
lsr cx16.r3+1
|
||||
ror cx16.r3
|
||||
lsr cx16.r3+1
|
||||
ror cx16.r3
|
||||
|
||||
; add r2 and xx (r3) to r0 (24-bits)
|
||||
stz cx16.r1
|
||||
clc
|
||||
lda cx16.r0
|
||||
adc cx16.r2
|
||||
sta cx16.r0
|
||||
lda cx16.r0+1
|
||||
adc cx16.r2+1
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+ clc
|
||||
lda cx16.r0
|
||||
adc cx16.r3
|
||||
sta cx16.r0
|
||||
lda cx16.r0+1
|
||||
adc cx16.r3+1
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub addr_mul_24_for_lores_256c(uword yy @R0, uword xx @AY) clobbers(A) -> uword @R0, ubyte @R1 {
|
||||
; yy * 320 + xx (24 bits calculation)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda cx16.r0+1
|
||||
sta cx16.r1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda cx16.r0
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
sta cx16.r0
|
||||
lda P8ZP_SCRATCH_B1
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+ ; now add the value to this 24-bits number
|
||||
lda cx16.r0
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
sta cx16.r0
|
||||
lda cx16.r0+1
|
||||
adc P8ZP_SCRATCH_W1+1
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+ lda cx16.r1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
137
compiler/res/prog8lib/cx16/graphics.p8
Normal file
137
compiler/res/prog8lib/cx16/graphics.p8
Normal file
@ -0,0 +1,137 @@
|
||||
%import syslib
|
||||
%import textio
|
||||
|
||||
; Bitmap pixel graphics module for the CommanderX16
|
||||
; wraps the graphics functions that are in ROM.
|
||||
; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom)
|
||||
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
|
||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||
|
||||
|
||||
graphics {
|
||||
const uword WIDTH = 320
|
||||
const ubyte HEIGHT = 200
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
void cx16.screen_set_mode($80)
|
||||
cx16.GRAPH_init(0)
|
||||
clear_screen(1, 0)
|
||||
}
|
||||
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
void cx16.screen_set_mode(2)
|
||||
txt.fill_screen(' ', 1) ; doesn't seem to fully clear the text screen after returning from gfx mode
|
||||
}
|
||||
|
||||
|
||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
||||
cx16.GRAPH_clear()
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, uword y, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(x, y, width, height, 0, 1)
|
||||
}
|
||||
|
||||
sub rect(uword x, uword y, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(x, y, width, height, 0, 0)
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, uword y, uword length) {
|
||||
if length
|
||||
cx16.GRAPH_draw_line(x, y, x+length-1, y)
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword height) {
|
||||
if height
|
||||
cx16.GRAPH_draw_line(x, y, x, y+height-1)
|
||||
}
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
;cx16.r0 = xcenter - radius/2
|
||||
;cx16.r1 = ycenter - radius/2
|
||||
;cx16.r2 = radius*2
|
||||
;cx16.r3 = radius*2
|
||||
;cx16.GRAPH_draw_oval(false) ; currently this call is not implemented on cx16, does a BRK
|
||||
|
||||
; Midpoint algorithm
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp xx = radius
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-xx
|
||||
|
||||
while xx>=yy {
|
||||
cx16.r0 = xcenter + xx
|
||||
cx16.r1 = ycenter + yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r0 = xcenter - xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r0 = xcenter + xx
|
||||
cx16.r1 = ycenter - yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r0 = xcenter - xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r0 = xcenter + yy
|
||||
cx16.r1 = ycenter + xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r0 = xcenter - yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r0 = xcenter + yy
|
||||
cx16.r1 = ycenter - xx
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r0 = xcenter - yy
|
||||
cx16.FB_cursor_position2()
|
||||
cx16.FB_set_pixel(1)
|
||||
yy++
|
||||
if decisionOver2<=0 {
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
} else {
|
||||
xx--
|
||||
decisionOver2 += (yy as word -xx)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word decisionOver2 = (1 as word)-radius
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*2+1)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*2+1)
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
else {
|
||||
radius--
|
||||
decisionOver2 += (yy as word -radius)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr cx16.FB_cursor_position
|
||||
lda #1
|
||||
jsr cx16.FB_set_pixel
|
||||
}}
|
||||
}
|
||||
}
|
189
compiler/res/prog8lib/cx16/palette.p8
Normal file
189
compiler/res/prog8lib/cx16/palette.p8
Normal file
@ -0,0 +1,189 @@
|
||||
; Manipulate the Commander X16's display color palette.
|
||||
; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
|
||||
|
||||
palette {
|
||||
|
||||
uword vera_palette_ptr
|
||||
ubyte c
|
||||
|
||||
sub set_color(ubyte index, uword color) {
|
||||
vera_palette_ptr = $fa00+(index as uword * 2)
|
||||
cx16.vpoke(1, vera_palette_ptr, lsb(color))
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
||||
}
|
||||
|
||||
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
|
||||
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
|
||||
vera_palette_ptr = $fa00
|
||||
repeat num_colors {
|
||||
cx16.vpoke(1, vera_palette_ptr+1, @(palette_bytes_ptr))
|
||||
palette_bytes_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, @(palette_bytes_ptr))
|
||||
palette_bytes_ptr++
|
||||
vera_palette_ptr+=2
|
||||
}
|
||||
}
|
||||
|
||||
sub set_rgb(uword palette_words_ptr, uword num_colors) {
|
||||
; 1 word per color entry (in little endian format so $gb0r)
|
||||
vera_palette_ptr = $fa00
|
||||
repeat num_colors*2 {
|
||||
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))
|
||||
palette_words_ptr++
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
|
||||
sub set_rgb8(uword palette_bytes_ptr, uword num_colors) {
|
||||
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
|
||||
vera_palette_ptr = $fa00
|
||||
ubyte red
|
||||
ubyte greenblue
|
||||
repeat num_colors {
|
||||
red = @(palette_bytes_ptr) >> 4
|
||||
palette_bytes_ptr++
|
||||
greenblue = @(palette_bytes_ptr) & %11110000
|
||||
palette_bytes_ptr++
|
||||
greenblue |= @(palette_bytes_ptr) >> 4 ; add Blue
|
||||
palette_bytes_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, greenblue)
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, red)
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
|
||||
sub set_monochrome(uword screencolorRGB, uword drawcolorRGB) {
|
||||
vera_palette_ptr = $fa00
|
||||
cx16.vpoke(1, vera_palette_ptr, lsb(screencolorRGB)) ; G,B
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(screencolorRGB)) ; R
|
||||
vera_palette_ptr++
|
||||
repeat 255 {
|
||||
cx16.vpoke(1, vera_palette_ptr, lsb(drawcolorRGB)) ; G,B
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(drawcolorRGB)) ; R
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
|
||||
sub set_all_black() {
|
||||
set_monochrome($000, $000)
|
||||
}
|
||||
|
||||
sub set_all_white() {
|
||||
set_monochrome($fff, $fff)
|
||||
}
|
||||
|
||||
sub set_grayscale() {
|
||||
vera_palette_ptr = $fa00
|
||||
repeat 16 {
|
||||
c=0
|
||||
repeat 16 {
|
||||
cx16.vpoke(1, vera_palette_ptr, c)
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, c)
|
||||
vera_palette_ptr++
|
||||
c += $11
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uword[] C64_colorpalette_dark = [ ; this is a darker palette with more contrast
|
||||
$000, ; 0 = black
|
||||
$FFF, ; 1 = white
|
||||
$632, ; 2 = red
|
||||
$7AB, ; 3 = cyan
|
||||
$638, ; 4 = purple
|
||||
$584, ; 5 = green
|
||||
$327, ; 6 = blue
|
||||
$BC6, ; 7 = yellow
|
||||
$642, ; 8 = orange
|
||||
$430, ; 9 = brown
|
||||
$965, ; 10 = light red
|
||||
$444, ; 11 = dark grey
|
||||
$666, ; 12 = medium grey
|
||||
$9D8, ; 13 = light green
|
||||
$65B, ; 14 = light blue
|
||||
$999 ; 15 = light grey
|
||||
]
|
||||
|
||||
uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||
$000, ; 0 = black
|
||||
$FFF, ; 1 = white
|
||||
$833, ; 2 = red
|
||||
$7cc, ; 3 = cyan
|
||||
$839, ; 4 = purple
|
||||
$5a4, ; 5 = green
|
||||
$229, ; 6 = blue
|
||||
$ef7, ; 7 = yellow
|
||||
$852, ; 8 = orange
|
||||
$530, ; 9 = brown
|
||||
$c67, ; 10 = light red
|
||||
$444, ; 11 = dark grey
|
||||
$777, ; 12 = medium grey
|
||||
$af9, ; 13 = light green
|
||||
$76e, ; 14 = light blue
|
||||
$bbb ; 15 = light grey
|
||||
]
|
||||
|
||||
uword[] C64_colorpalette_light = [ ; this is a lighter palette
|
||||
$000, ; 0 = black
|
||||
$FFF, ; 1 = white
|
||||
$944, ; 2 = red
|
||||
$7CC, ; 3 = cyan
|
||||
$95A, ; 4 = purple
|
||||
$6A5, ; 5 = green
|
||||
$549, ; 6 = blue
|
||||
$CD8, ; 7 = yellow
|
||||
$963, ; 8 = orange
|
||||
$650, ; 9 = brown
|
||||
$C77, ; 10 = light red
|
||||
$666, ; 11 = dark grey
|
||||
$888, ; 12 = medium grey
|
||||
$AE9, ; 13 = light green
|
||||
$87C, ; 14 = light blue
|
||||
$AAA ; 15 = light grey
|
||||
]
|
||||
|
||||
sub set_c64pepto() {
|
||||
vera_palette_ptr = $fa00
|
||||
repeat 16 {
|
||||
for c in 0 to 15 {
|
||||
uword cc = C64_colorpalette_pepto[c]
|
||||
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub set_c64light() {
|
||||
vera_palette_ptr = $fa00
|
||||
repeat 16 {
|
||||
for c in 0 to 15 {
|
||||
uword cc = C64_colorpalette_light[c]
|
||||
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub set_c64dark() {
|
||||
vera_palette_ptr = $fa00
|
||||
repeat 16 {
|
||||
for c in 0 to 15 {
|
||||
uword cc = C64_colorpalette_dark[c]
|
||||
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
930
compiler/res/prog8lib/cx16/syslib.p8
Normal file
930
compiler/res/prog8lib/cx16/syslib.p8
Normal file
@ -0,0 +1,930 @@
|
||||
; Prog8 definitions for the CommanderX16
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
c64 {
|
||||
|
||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||
|
||||
; STROUT --> use txt.print
|
||||
; CLEARSCR -> use txt.clear_screen
|
||||
; HOMECRSR -> use txt.plot
|
||||
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- utility
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.STOP
|
||||
beq +
|
||||
plx
|
||||
lda #0
|
||||
rts
|
||||
+ plx
|
||||
lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
; irq and hardware vectors:
|
||||
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
|
||||
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
|
||||
&uword r0 = $0002
|
||||
&uword r1 = $0004
|
||||
&uword r2 = $0006
|
||||
&uword r3 = $0008
|
||||
&uword r4 = $000a
|
||||
&uword r5 = $000c
|
||||
&uword r6 = $000e
|
||||
&uword r7 = $0010
|
||||
&uword r8 = $0012
|
||||
&uword r9 = $0014
|
||||
&uword r10 = $0016
|
||||
&uword r11 = $0018
|
||||
&uword r12 = $001a
|
||||
&uword r13 = $001c
|
||||
&uword r14 = $001e
|
||||
&uword r15 = $0020
|
||||
|
||||
&word r0s = $0002
|
||||
&word r1s = $0004
|
||||
&word r2s = $0006
|
||||
&word r3s = $0008
|
||||
&word r4s = $000a
|
||||
&word r5s = $000c
|
||||
&word r6s = $000e
|
||||
&word r7s = $0010
|
||||
&word r8s = $0012
|
||||
&word r9s = $0014
|
||||
&word r10s = $0016
|
||||
&word r11s = $0018
|
||||
&word r12s = $001a
|
||||
&word r13s = $001c
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
&ubyte r2L = $0006
|
||||
&ubyte r3L = $0008
|
||||
&ubyte r4L = $000a
|
||||
&ubyte r5L = $000c
|
||||
&ubyte r6L = $000e
|
||||
&ubyte r7L = $0010
|
||||
&ubyte r8L = $0012
|
||||
&ubyte r9L = $0014
|
||||
&ubyte r10L = $0016
|
||||
&ubyte r11L = $0018
|
||||
&ubyte r12L = $001a
|
||||
&ubyte r13L = $001c
|
||||
&ubyte r14L = $001e
|
||||
&ubyte r15L = $0020
|
||||
|
||||
&ubyte r0H = $0003
|
||||
&ubyte r1H = $0005
|
||||
&ubyte r2H = $0007
|
||||
&ubyte r3H = $0009
|
||||
&ubyte r4H = $000b
|
||||
&ubyte r5H = $000d
|
||||
&ubyte r6H = $000f
|
||||
&ubyte r7H = $0011
|
||||
&ubyte r8H = $0013
|
||||
&ubyte r9H = $0015
|
||||
&ubyte r10H = $0017
|
||||
&ubyte r11H = $0019
|
||||
&ubyte r12H = $001b
|
||||
&ubyte r13H = $001d
|
||||
&ubyte r14H = $001f
|
||||
&ubyte r15H = $0021
|
||||
|
||||
&byte r0sL = $0002
|
||||
&byte r1sL = $0004
|
||||
&byte r2sL = $0006
|
||||
&byte r3sL = $0008
|
||||
&byte r4sL = $000a
|
||||
&byte r5sL = $000c
|
||||
&byte r6sL = $000e
|
||||
&byte r7sL = $0010
|
||||
&byte r8sL = $0012
|
||||
&byte r9sL = $0014
|
||||
&byte r10sL = $0016
|
||||
&byte r11sL = $0018
|
||||
&byte r12sL = $001a
|
||||
&byte r13sL = $001c
|
||||
&byte r14sL = $001e
|
||||
&byte r15sL = $0020
|
||||
|
||||
&byte r0sH = $0003
|
||||
&byte r1sH = $0005
|
||||
&byte r2sH = $0007
|
||||
&byte r3sH = $0009
|
||||
&byte r4sH = $000b
|
||||
&byte r5sH = $000d
|
||||
&byte r6sH = $000f
|
||||
&byte r7sH = $0011
|
||||
&byte r8sH = $0013
|
||||
&byte r9sH = $0015
|
||||
&byte r10sH = $0017
|
||||
&byte r11sH = $0019
|
||||
&byte r12sH = $001b
|
||||
&byte r13sH = $001d
|
||||
&byte r14sH = $001f
|
||||
&byte r15sH = $0021
|
||||
|
||||
; VERA registers
|
||||
|
||||
const uword VERA_BASE = $9F20
|
||||
&ubyte VERA_ADDR_L = VERA_BASE + $0000
|
||||
&ubyte VERA_ADDR_M = VERA_BASE + $0001
|
||||
&ubyte VERA_ADDR_H = VERA_BASE + $0002
|
||||
&ubyte VERA_DATA0 = VERA_BASE + $0003
|
||||
&ubyte VERA_DATA1 = VERA_BASE + $0004
|
||||
&ubyte VERA_CTRL = VERA_BASE + $0005
|
||||
&ubyte VERA_IEN = VERA_BASE + $0006
|
||||
&ubyte VERA_ISR = VERA_BASE + $0007
|
||||
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
||||
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
|
||||
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
|
||||
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
|
||||
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
|
||||
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
|
||||
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
|
||||
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
|
||||
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
|
||||
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
||||
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
||||
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
||||
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
|
||||
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
|
||||
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
|
||||
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
|
||||
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
|
||||
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
|
||||
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
|
||||
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
|
||||
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
|
||||
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
|
||||
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
|
||||
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
|
||||
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
|
||||
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
|
||||
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
|
||||
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
|
||||
; VERA_PSG_BASE = $1F9C0
|
||||
; VERA_PALETTE_BASE = $1FA00
|
||||
; VERA_SPRITES_BASE = $1FC00
|
||||
|
||||
; I/O
|
||||
|
||||
const uword via1 = $9f00 ;VIA 6522 #1
|
||||
&ubyte d1prb = via1+0
|
||||
&ubyte d1pra = via1+1
|
||||
&ubyte d1ddrb = via1+2
|
||||
&ubyte d1ddra = via1+3
|
||||
&ubyte d1t1l = via1+4
|
||||
&ubyte d1t1h = via1+5
|
||||
&ubyte d1t1ll = via1+6
|
||||
&ubyte d1t1lh = via1+7
|
||||
&ubyte d1t2l = via1+8
|
||||
&ubyte d1t2h = via1+9
|
||||
&ubyte d1sr = via1+10
|
||||
&ubyte d1acr = via1+11
|
||||
&ubyte d1pcr = via1+12
|
||||
&ubyte d1ifr = via1+13
|
||||
&ubyte d1ier = via1+14
|
||||
&ubyte d1ora = via1+15
|
||||
|
||||
const uword via2 = $9f10 ;VIA 6522 #2
|
||||
&ubyte d2prb = via2+0
|
||||
&ubyte d2pra = via2+1
|
||||
&ubyte d2ddrb = via2+2
|
||||
&ubyte d2ddra = via2+3
|
||||
&ubyte d2t1l = via2+4
|
||||
&ubyte d2t1h = via2+5
|
||||
&ubyte d2t1ll = via2+6
|
||||
&ubyte d2t1lh = via2+7
|
||||
&ubyte d2t2l = via2+8
|
||||
&ubyte d2t2h = via2+9
|
||||
&ubyte d2sr = via2+10
|
||||
&ubyte d2acr = via2+11
|
||||
&ubyte d2pcr = via2+12
|
||||
&ubyte d2ifr = via2+13
|
||||
&ubyte d2ier = via2+14
|
||||
&ubyte d2ora = via2+15
|
||||
|
||||
&ubyte ym2151adr = $9f40
|
||||
&ubyte ym2151dat = $9f41
|
||||
|
||||
const uword extdev = $9f60
|
||||
|
||||
|
||||
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
||||
; spelling of the names is taken from the Commander X-16 rom sources
|
||||
|
||||
; supported C128 additions
|
||||
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
|
||||
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
|
||||
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
|
||||
romsub $ff5f = screen_set_mode(ubyte mode @A) clobbers(A, X, Y) -> ubyte @Pc
|
||||
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
|
||||
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
|
||||
romsub $ff6e = jsrfar()
|
||||
romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
|
||||
romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||
romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||
romsub $ff7d = primm()
|
||||
|
||||
; X16 additions
|
||||
romsub $ff44 = macptr() clobbers(A,X,Y)
|
||||
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
||||
romsub $ff68 = mouse_config(ubyte shape @A, ubyte scale @X) clobbers (A, X, Y)
|
||||
romsub $ff6b = mouse_get(ubyte zpdataptr @X) clobbers(A)
|
||||
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||
romsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
||||
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
|
||||
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
|
||||
|
||||
; TODO specify the correct clobbers for alle these functions below, we now assume all 3 regs are clobbered
|
||||
|
||||
; high level graphics & fonts
|
||||
romsub $ff20 = GRAPH_init(uword vectors @R0) clobbers(A,X,Y)
|
||||
romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
|
||||
romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
||||
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
|
||||
romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y)
|
||||
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, ubyte fill @Pc) clobbers(A,X,Y)
|
||||
romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y)
|
||||
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, ubyte fill @Pc) clobbers(A,X,Y)
|
||||
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
|
||||
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
|
||||
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
|
||||
|
||||
; framebuffer
|
||||
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
||||
romsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A, uword @R0, uword @R1 ; width=r0, height=r1
|
||||
romsub $fefc = FB_set_palette(uword pointer @R0, ubyte index @A, ubyte bytecount @X) clobbers(A,X,Y)
|
||||
romsub $feff = FB_cursor_position(uword x @R0, uword y @R1) clobbers(A,X,Y)
|
||||
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the previous routine, but avoiding having to respecify both x and y every time
|
||||
romsub $ff02 = FB_cursor_next_line(uword x @R0) clobbers(A,X,Y)
|
||||
romsub $ff05 = FB_get_pixel() clobbers(X,Y) -> ubyte @A
|
||||
romsub $ff08 = FB_get_pixels(uword pointer @R0, uword count @R1) clobbers(A,X,Y)
|
||||
romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y)
|
||||
romsub $ff0e = FB_set_pixels(uword pointer @R0, uword count @R1) clobbers(A,X,Y)
|
||||
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y)
|
||||
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @R0, ubyte mask @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y)
|
||||
romsub $ff17 = FB_fill_pixels(uword count @R0, uword pstep @R1, ubyte color @A) clobbers(A,X,Y)
|
||||
romsub $ff1a = FB_filter_pixels(uword pointer @ R0, uword count @R1) clobbers(A,X,Y)
|
||||
romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y)
|
||||
|
||||
; misc
|
||||
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc
|
||||
romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y)
|
||||
romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y)
|
||||
romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y)
|
||||
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
|
||||
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
|
||||
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
||||
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y)
|
||||
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
|
||||
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
|
||||
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
|
||||
romsub $fed2 = kbdbuf_put(ubyte key @A) clobbers(A,X,Y)
|
||||
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
|
||||
; ---- end of kernal routines ----
|
||||
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
inline asmsub rombank(ubyte rombank @A) {
|
||||
; -- set the rom banks
|
||||
%asm {{
|
||||
sta $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub rambank(ubyte rambank @A) {
|
||||
; -- set the ram bank
|
||||
%asm {{
|
||||
sta $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub numbanks() -> ubyte @A {
|
||||
; -- uses MEMTOP's cx16 extension to query the number of available RAM banks. (each is 8 Kb)
|
||||
%asm {{
|
||||
phx
|
||||
sec
|
||||
jsr c64.MEMTOP
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
||||
; -- get a byte from VERA's video memory
|
||||
; note: inefficient when reading multiple sequential bytes!
|
||||
%asm {{
|
||||
pha
|
||||
lda #1
|
||||
sta cx16.VERA_CTRL
|
||||
pla
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
sty cx16.VERA_ADDR_M
|
||||
stx cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_DATA1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrOrDecrByOne @Y) clobbers(A) {
|
||||
; -- setup the VERA's data address register 0 or 1
|
||||
%asm {{
|
||||
and #1
|
||||
pha
|
||||
lda cx16.r1
|
||||
and #1
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
pla
|
||||
cpy #0
|
||||
bmi ++
|
||||
beq +
|
||||
ora #%00010000
|
||||
+ sta cx16.VERA_ADDR_H
|
||||
rts
|
||||
+ ora #%00011000
|
||||
sta cx16.VERA_ADDR_H
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
||||
; -- write a single byte to VERA's video memory
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
sty cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
||||
; -- or a single byte to the value already in the VERA's video memory at that location
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
tya
|
||||
ora cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
||||
; -- and a single byte to the value already in the VERA's video memory at that location
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
tya
|
||||
and cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
||||
; -- xor a single byte to the value already in the VERA's video memory at that location
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
tya
|
||||
eor cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
; -- like the basic command VLOAD "filename",device,bank,address
|
||||
; loads a file into video memory in the given bank:address, returns success in A
|
||||
; !! NOTE !! the V38 ROMs contain a bug in the LOAD code that makes the load address not work correctly,
|
||||
; it works fine when loading from local filesystem
|
||||
%asm {{
|
||||
; -- load a file into video ram
|
||||
phx
|
||||
pha
|
||||
tya
|
||||
tax
|
||||
lda #1
|
||||
ldy #0
|
||||
jsr c64.SETLFS
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr prog8_lib.strlen
|
||||
tya
|
||||
ldx cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr c64.SETNAM
|
||||
pla
|
||||
clc
|
||||
adc #2
|
||||
ldx cx16.r1
|
||||
ldy cx16.r1+1
|
||||
stz P8ZP_SCRATCH_B1
|
||||
jsr c64.LOAD
|
||||
bcs +
|
||||
inc P8ZP_SCRATCH_B1
|
||||
+ jsr c64.CLRCHN
|
||||
lda #1
|
||||
jsr c64.CLOSE
|
||||
plx
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX {
|
||||
; convenience routine to get the joystick state without requiring inline assembly that deals with the multiple return values.
|
||||
; Also disables interrupts to avoid the IRQ race condition mentioned here: https://github.com/commanderx16/x16-rom/issues/203
|
||||
; TODO once that issue is resolved, this routine can be redefined as: romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX
|
||||
%asm {{
|
||||
sei
|
||||
jsr cx16.joystick_get
|
||||
cli
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub FB_set_pixels_from_buf(uword buffer, uword count) {
|
||||
%asm {{
|
||||
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
||||
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
|
||||
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
|
||||
; See https://github.com/commanderx16/x16-rom/issues/179
|
||||
phx
|
||||
lda buffer
|
||||
ldy buffer+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
jsr _pixels
|
||||
plx
|
||||
rts
|
||||
|
||||
_pixels lda count+1
|
||||
beq +
|
||||
ldx #0
|
||||
- jsr _loop
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
dec count+1
|
||||
bne -
|
||||
|
||||
+ ldx count
|
||||
_loop ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
dex
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- system stuff -----
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
lda #$80
|
||||
sta VERA_CTRL
|
||||
stz $01 ; select rom bank 0 (enable kernal)
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
lda #$90 ; black
|
||||
jsr c64.CHROUT
|
||||
lda #1 ; swap fg/bg
|
||||
jsr c64.CHROUT
|
||||
lda #$9e ; yellow
|
||||
jsr c64.CHROUT
|
||||
lda #147 ; clear screen
|
||||
jsr c64.CHROUT
|
||||
lda #0
|
||||
tax
|
||||
tay
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
sei
|
||||
lda cx16.CINV
|
||||
sta restore_irq._orig_irqvec
|
||||
lda cx16.CINV+1
|
||||
sta restore_irq._orig_irqvec+1
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
sta _use_kernal
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta cx16.CINV
|
||||
lda #>_irq_handler
|
||||
sta cx16.CINV+1
|
||||
lda cx16.VERA_IEN
|
||||
ora #%00000001 ; enable the vsync irq
|
||||
sta cx16.VERA_IEN
|
||||
cli
|
||||
rts
|
||||
|
||||
_irq_handler jsr _irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr _irq_handler_end
|
||||
lda _use_kernal
|
||||
bne +
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
lda cx16.VERA_ISR
|
||||
ora #1
|
||||
sta cx16.VERA_ISR ; clear Vera Vsync irq status
|
||||
ply
|
||||
plx
|
||||
pla
|
||||
rti
|
||||
+ jmp (restore_irq._orig_irqvec) ; continue with normal kernal irq routine
|
||||
|
||||
_use_kernal .byte 0
|
||||
|
||||
_irq_handler_init
|
||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||
stx IRQ_X_REG
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta IRQ_SCRATCH_ZPB1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta IRQ_SCRATCH_ZPREG
|
||||
lda P8ZP_SCRATCH_W1
|
||||
sta IRQ_SCRATCH_ZPWORD1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sta IRQ_SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
cld
|
||||
rts
|
||||
|
||||
_irq_handler_end
|
||||
; restore all zp scratch registers and the X register
|
||||
lda IRQ_SCRATCH_ZPB1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda IRQ_SCRATCH_ZPREG
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda IRQ_SCRATCH_ZPWORD1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda IRQ_SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda IRQ_SCRATCH_ZPWORD2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda IRQ_SCRATCH_ZPWORD2+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
ldx IRQ_X_REG
|
||||
rts
|
||||
|
||||
IRQ_X_REG .byte 0
|
||||
IRQ_SCRATCH_ZPB1 .byte 0
|
||||
IRQ_SCRATCH_ZPREG .byte 0
|
||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda _orig_irqvec
|
||||
sta cx16.CINV
|
||||
lda _orig_irqvec+1
|
||||
sta cx16.CINV+1
|
||||
lda cx16.VERA_IEN
|
||||
and #%11110000 ; disable all Vera IRQs
|
||||
ora #%00000001 ; enable only the vsync Irq
|
||||
sta cx16.VERA_IEN
|
||||
cli
|
||||
rts
|
||||
_orig_irqvec .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
sei
|
||||
lda cx16.VERA_IEN
|
||||
and #%11110000 ; clear other IRQs
|
||||
ora #%00000010 ; enable the line (raster) irq
|
||||
sta cx16.VERA_IEN
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr set_rasterline
|
||||
lda #<_raster_irq_handler
|
||||
sta cx16.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cx16.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irq._irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr set_irq._irq_handler_end
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
lda cx16.VERA_ISR
|
||||
ora #%00000010
|
||||
sta cx16.VERA_ISR ; clear Vera line irq status
|
||||
ply
|
||||
plx
|
||||
pla
|
||||
rti
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
%asm {{
|
||||
sta cx16.VERA_IRQ_LINE_L
|
||||
lda cx16.VERA_IEN
|
||||
and #%01111111
|
||||
sta cx16.VERA_IEN
|
||||
tya
|
||||
lsr a
|
||||
ror a
|
||||
and #%10000000
|
||||
ora cx16.VERA_IEN
|
||||
sta cx16.VERA_IEN
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 16 ; compilation target specifier. 64 = C64, 16 = CommanderX16.
|
||||
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
stz $01 ; bank the kernal in
|
||||
jmp (cx16.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||
; note: regular system vsync irq handler must be running, and no nother irqs
|
||||
%asm {{
|
||||
- wai ; wait for irq (assume it was vsync)
|
||||
cmp #0
|
||||
bne +
|
||||
dey
|
||||
+ dec a
|
||||
bne -
|
||||
cpy #0
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub waitvsync() {
|
||||
; --- suspend execution until the next vsync has occurred, without depending on custom irq handling.
|
||||
; note: system vsync irq handler has to be active for this routine to work (and no other irqs-- which is the default).
|
||||
; note: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
|
||||
%asm {{
|
||||
wai
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
sta cx16.r2
|
||||
sty cx16.r2+1
|
||||
jsr cx16.memory_copy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub memset(uword mem @R0, uword numbytes @R1, ubyte value @A) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
jsr cx16.memory_fill
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memsetw(uword mem @R0, uword numwords @R1, uword value @AY) clobbers (A,X,Y) {
|
||||
%asm {{
|
||||
ldx cx16.r0
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx cx16.r0+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
stx P8ZP_SCRATCH_W2
|
||||
ldx cx16.r1+1
|
||||
stx P8ZP_SCRATCH_W2+1
|
||||
jmp prog8_lib.memsetw
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub rsave() {
|
||||
; save cpu status flag and all registers A, X, Y.
|
||||
; see http://6502.org/tutorials/register_preservation.html
|
||||
%asm {{
|
||||
php
|
||||
pha
|
||||
phy
|
||||
phx
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub rrestore() {
|
||||
; restore all registers and cpu status flag
|
||||
%asm {{
|
||||
plx
|
||||
ply
|
||||
pla
|
||||
plp
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub read_flags() -> ubyte @A {
|
||||
%asm {{
|
||||
php
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_carry() {
|
||||
%asm {{
|
||||
clc
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_carry() {
|
||||
%asm {{
|
||||
sec
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_irqd() {
|
||||
%asm {{
|
||||
cli
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_irqd() {
|
||||
%asm {{
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
jsr c64.CLRCHN ; reset i/o channels
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progend() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_end
|
||||
ldy #>prog8_program_end
|
||||
}}
|
||||
}
|
||||
}
|
748
compiler/res/prog8lib/cx16/textio.p8
Normal file
748
compiler/res/prog8lib/cx16/textio.p8
Normal file
@ -0,0 +1,748 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
|
||||
txt {
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
const ubyte DEFAULT_HEIGHT = 60
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
txt.chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
txt.chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
txt.chrout(' ')
|
||||
}
|
||||
|
||||
asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||
%asm {{
|
||||
sec
|
||||
jsr c64.PLOT
|
||||
tay
|
||||
clc
|
||||
jmp c64.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
%asm {{
|
||||
sty _ly+1
|
||||
phx
|
||||
pha
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
sta _lx+1
|
||||
stz cx16.VERA_CTRL
|
||||
lda #%00010000
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||
stz cx16.VERA_ADDR_L ; start at (0,0)
|
||||
stz cx16.VERA_ADDR_M
|
||||
pla
|
||||
_lx ldx #0 ; modified
|
||||
phy
|
||||
_ly ldy #1 ; modified
|
||||
- sta cx16.VERA_DATA0
|
||||
sty cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sty cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sty cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sty cx16.VERA_DATA0
|
||||
dex
|
||||
bne -
|
||||
ply
|
||||
dey
|
||||
beq +
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
sta _lx+1
|
||||
stz cx16.VERA_CTRL
|
||||
lda #%00100000
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 2, bank 0.
|
||||
stz cx16.VERA_ADDR_L ; start at (0,0)
|
||||
stz cx16.VERA_ADDR_M
|
||||
pla
|
||||
_lx ldx #0 ; modified
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
dex
|
||||
bne -
|
||||
dey
|
||||
beq +
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
phx
|
||||
sta _la+1
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
sta _lx+1
|
||||
stz cx16.VERA_CTRL
|
||||
lda #%00100000
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 2, bank 0.
|
||||
lda #1
|
||||
sta cx16.VERA_ADDR_L ; start at (1,0)
|
||||
stz cx16.VERA_ADDR_M
|
||||
_lx ldx #0 ; modified
|
||||
_la lda #0 ; modified
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
dex
|
||||
bne -
|
||||
dey
|
||||
beq +
|
||||
lda #1
|
||||
sta cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b]
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
txtcol &= 15
|
||||
c64.CHROUT(color_to_charcode[txtcol])
|
||||
}
|
||||
|
||||
sub color2 (ubyte txtcol, ubyte bgcol) {
|
||||
txtcol &= 15
|
||||
bgcol &= 15
|
||||
c64.CHROUT(color_to_charcode[bgcol])
|
||||
c64.CHROUT(1) ; switch fg and bg colors
|
||||
c64.CHROUT(color_to_charcode[txtcol])
|
||||
}
|
||||
|
||||
sub lowercase() {
|
||||
cx16.screen_set_charset(3, 0) ; lowercase charset
|
||||
}
|
||||
|
||||
sub uppercase() {
|
||||
cx16.screen_set_charset(2, 0) ; uppercase charset
|
||||
}
|
||||
|
||||
asmsub scroll_left() clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||
|
||||
_nextline
|
||||
stz cx16.VERA_CTRL ; data port 0: source column
|
||||
lda #%00010000 ; auto increment 1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda #2
|
||||
sta cx16.VERA_ADDR_L ; begin in column 1
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda #1
|
||||
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||
lda #%00010000 ; auto increment 1
|
||||
sta cx16.VERA_ADDR_H
|
||||
stz cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
|
||||
_lx ldx #0 ; modified
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy color
|
||||
dex
|
||||
bne -
|
||||
dec P8ZP_SCRATCH_B1
|
||||
bpl _nextline
|
||||
|
||||
lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right() clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
txa
|
||||
asl a
|
||||
dea
|
||||
sta _rcol+1
|
||||
ina
|
||||
ina
|
||||
sta _rcol2+1
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||
|
||||
_nextline
|
||||
stz cx16.VERA_CTRL ; data port 0: source column
|
||||
lda #%00011000 ; auto decrement 1
|
||||
sta cx16.VERA_ADDR_H
|
||||
_rcol lda #79*2-1 ; modified
|
||||
sta cx16.VERA_ADDR_L ; begin in rightmost column minus one
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda #1
|
||||
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||
lda #%00011000 ; auto decrement 1
|
||||
sta cx16.VERA_ADDR_H
|
||||
_rcol2 lda #79*2+1 ; modified
|
||||
sta cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
|
||||
_lx ldx #0 ; modified
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy color
|
||||
dex
|
||||
bne -
|
||||
dec P8ZP_SCRATCH_B1
|
||||
bpl _nextline
|
||||
|
||||
lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up() clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_CTRL ; data port 0 is source
|
||||
lda #1
|
||||
sta cx16.VERA_ADDR_M ; start at second line
|
||||
stz cx16.VERA_ADDR_L
|
||||
lda #%00010000
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||
|
||||
lda #1
|
||||
sta cx16.VERA_CTRL ; data port 1 is destination
|
||||
stz cx16.VERA_ADDR_M ; start at top line
|
||||
stz cx16.VERA_ADDR_L
|
||||
lda #%00010000
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||
|
||||
_nextline
|
||||
ldx #80 ; modified
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy color
|
||||
dex
|
||||
bne -
|
||||
dec P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
stz cx16.VERA_CTRL ; data port 0
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M
|
||||
lda #1
|
||||
sta cx16.VERA_CTRL ; data port 1
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M
|
||||
bra _nextline
|
||||
|
||||
+ lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down() clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_CTRL ; data port 0 is source
|
||||
dey
|
||||
sty cx16.VERA_ADDR_M ; start at line before bottom line
|
||||
stz cx16.VERA_ADDR_L
|
||||
lda #%00010000
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||
|
||||
lda #1
|
||||
sta cx16.VERA_CTRL ; data port 1 is destination
|
||||
iny
|
||||
sty cx16.VERA_ADDR_M ; start at bottom line
|
||||
stz cx16.VERA_ADDR_L
|
||||
lda #%00010000
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||
|
||||
_nextline
|
||||
ldx #80 ; modified
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy color
|
||||
dex
|
||||
bne -
|
||||
dec P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
stz cx16.VERA_CTRL ; data port 0
|
||||
stz cx16.VERA_ADDR_L
|
||||
dec cx16.VERA_ADDR_M
|
||||
lda #1
|
||||
sta cx16.VERA_CTRL ; data port 1
|
||||
stz cx16.VERA_ADDR_L
|
||||
dec cx16.VERA_ADDR_M
|
||||
bra _nextline
|
||||
|
||||
+ lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
txa
|
||||
jsr c64.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
bra _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr c64.CHROUT
|
||||
_ones txa
|
||||
jsr c64.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
bra print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
phx
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr c64.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr c64.CHROUT
|
||||
dey
|
||||
bne -
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubbin
|
||||
pla
|
||||
clc
|
||||
bra print_ubbin
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubhex
|
||||
pla
|
||||
clc
|
||||
bra print_ubhex
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
plx
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
iny
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp c64.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
pla
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
bcc +
|
||||
iny
|
||||
+ bra print_uw
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr c64.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
iny
|
||||
bne -
|
||||
+ lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||
rts
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A) {
|
||||
; ---- sets the character in the screen matrix at the given position
|
||||
%asm {{
|
||||
pha
|
||||
txa
|
||||
asl a
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
sta cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
pla
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||
; ---- get the character in the screen matrix at the given location
|
||||
%asm {{
|
||||
asl a
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
sta cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
|
||||
; ---- set the color in A on the screen matrix at the given position
|
||||
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||
; use the high nybble in A to set the Bg color!
|
||||
%asm {{
|
||||
pha
|
||||
txa
|
||||
asl a
|
||||
ina
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
sta cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
pla
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||
; ---- get the color in the screen color matrix at the given location
|
||||
%asm {{
|
||||
asl a
|
||||
ina
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
sta cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: color handling is the same as on the C64: it only sets the foreground color.
|
||||
; use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors.
|
||||
%asm {{
|
||||
phx
|
||||
lda column
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
lda charcolor
|
||||
and #$0f
|
||||
sta P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda char
|
||||
sta cx16.VERA_DATA0
|
||||
inx
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda cx16.VERA_DATA0
|
||||
and #$f0
|
||||
ora P8ZP_SCRATCH_B1
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||
; use the high nybble in A to set the Bg color!
|
||||
%asm {{
|
||||
phx
|
||||
lda column
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda char
|
||||
sta cx16.VERA_DATA0
|
||||
inx
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda colors
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
%asm {{
|
||||
phx
|
||||
tax
|
||||
clc
|
||||
jsr c64.PLOT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
; -- returns the text screen width (number of columns)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
txa
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||
; -- returns the text screen height (number of rows)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
31
compiler/res/prog8lib/cx16logo.p8
Normal file
31
compiler/res/prog8lib/cx16logo.p8
Normal file
@ -0,0 +1,31 @@
|
||||
; routine to draw the Commander X16's log in petscii.
|
||||
|
||||
%import textio
|
||||
|
||||
cx16logo {
|
||||
sub logo_at(ubyte column, ubyte row) {
|
||||
uword strptr
|
||||
for strptr in logo_lines {
|
||||
txt.plot(column, row)
|
||||
txt.print(strptr)
|
||||
row++
|
||||
}
|
||||
}
|
||||
|
||||
sub logo() {
|
||||
uword strptr
|
||||
for strptr in logo_lines
|
||||
txt.print(strptr)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
str[] logo_lines = [
|
||||
"\uf10d\uf11a\uf139\uf11b \uf11a\uf13a\uf11b\n",
|
||||
"\uf10b\uf11a▎\uf139\uf11b \uf11a\uf13a\uf130\uf11b\n",
|
||||
"\uf10f\uf11a▌ \uf139\uf11b \uf11a\uf13a \uf11b▌\n",
|
||||
"\uf102 \uf132\uf11a▖\uf11b \uf11a▗\uf11b\uf132\n",
|
||||
"\uf10e ▂\uf11a▘\uf11b \uf11a▝\uf11b▂\n",
|
||||
"\uf104 \uf11a \uf11b\uf13a\uf11b \uf139\uf11a \uf11b\n",
|
||||
"\uf101\uf130\uf13a \uf139▎\uf100"
|
||||
]
|
||||
}
|
492
compiler/res/prog8lib/diskio.p8
Normal file
492
compiler/res/prog8lib/diskio.p8
Normal file
@ -0,0 +1,492 @@
|
||||
; C64 and Cx16 disk drive I/O routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%import textio
|
||||
%import string
|
||||
%import syslib
|
||||
|
||||
diskio {
|
||||
|
||||
sub directory(ubyte drivenumber) -> ubyte {
|
||||
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(13, drivenumber, 0)
|
||||
void c64.OPEN() ; open 13,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(13) ; use #13 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
||||
}
|
||||
|
||||
; while not key pressed / EOF encountered, read data.
|
||||
ubyte status = c64.READST()
|
||||
while not status {
|
||||
ubyte low = c64.CHRIN()
|
||||
ubyte high = c64.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.spc()
|
||||
ubyte @zp char
|
||||
repeat {
|
||||
char = c64.CHRIN()
|
||||
if char==0
|
||||
break
|
||||
txt.chrout(char)
|
||||
}
|
||||
txt.nl()
|
||||
void c64.CHRIN() ; skip 2 bytes
|
||||
void c64.CHRIN()
|
||||
status = c64.READST()
|
||||
if c64.STOP2()
|
||||
break
|
||||
}
|
||||
|
||||
io_error:
|
||||
status = c64.READST()
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(13)
|
||||
|
||||
if status and status & $40 == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
txt.print_ub(status)
|
||||
txt.nl()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
; internal variables for the iterative file lister / loader
|
||||
ubyte list_skip_disk_name
|
||||
uword list_pattern
|
||||
uword list_blocks
|
||||
ubyte iteration_in_progress = false
|
||||
ubyte @zp first_byte
|
||||
ubyte have_first_byte
|
||||
str list_filename = "?" * 32
|
||||
|
||||
|
||||
; ----- get a list of files (uses iteration functions internally) -----
|
||||
|
||||
sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte {
|
||||
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
|
||||
uword names_buffer = memory("filenames", 512)
|
||||
uword buffer_start = names_buffer
|
||||
ubyte files_found = 0
|
||||
if lf_start_list(drivenumber, pattern_ptr) {
|
||||
while lf_next_entry() {
|
||||
@(name_ptrs) = lsb(names_buffer)
|
||||
name_ptrs++
|
||||
@(name_ptrs) = msb(names_buffer)
|
||||
name_ptrs++
|
||||
names_buffer += string.copy(diskio.list_filename, names_buffer) + 1
|
||||
files_found++
|
||||
if names_buffer - buffer_start > 512-18
|
||||
break
|
||||
if files_found == max_names
|
||||
break
|
||||
}
|
||||
lf_end_list()
|
||||
}
|
||||
return files_found
|
||||
}
|
||||
|
||||
; ----- iterative file lister functions (uses io channel 12) -----
|
||||
|
||||
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> ubyte {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
lf_end_list()
|
||||
list_pattern = pattern_ptr
|
||||
list_skip_disk_name = true
|
||||
iteration_in_progress = true
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(12, drivenumber, 0)
|
||||
void c64.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
||||
}
|
||||
|
||||
if c64.READST()==0
|
||||
return true
|
||||
|
||||
io_error:
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_next_entry() -> ubyte {
|
||||
; -- retrieve the next entry from an iterative file listing session.
|
||||
; results will be found in list_blocks and list_filename.
|
||||
; if it returns false though, there are no more entries (or an error occurred).
|
||||
|
||||
if not iteration_in_progress
|
||||
return false
|
||||
|
||||
repeat {
|
||||
void c64.CHKIN(12) ; use #12 as input channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = c64.CHRIN()
|
||||
ubyte blocks_msb = c64.CHRIN()
|
||||
|
||||
if c64.READST()
|
||||
goto close_end
|
||||
|
||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||
|
||||
; read until the filename starts after the first "
|
||||
while c64.CHRIN()!='\"' {
|
||||
if c64.READST()
|
||||
goto close_end
|
||||
}
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = c64.CHRIN()
|
||||
if char==0
|
||||
break
|
||||
if char=='\"'
|
||||
break
|
||||
@(nameptr) = char
|
||||
nameptr++
|
||||
}
|
||||
|
||||
@(nameptr) = 0
|
||||
|
||||
while c64.CHRIN() {
|
||||
; read the rest of the entry until the end
|
||||
}
|
||||
|
||||
void c64.CHRIN() ; skip 2 bytes
|
||||
void c64.CHRIN()
|
||||
|
||||
if not list_skip_disk_name {
|
||||
if not list_pattern
|
||||
return true
|
||||
if prog8_lib.pattern_match(list_filename, list_pattern)
|
||||
return true
|
||||
}
|
||||
list_skip_disk_name = false
|
||||
}
|
||||
|
||||
close_end:
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_end_list() {
|
||||
; -- end an iterative file listing session (close channels).
|
||||
if iteration_in_progress {
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(12)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file loader functions (uses io channel 11) -----
|
||||
|
||||
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte {
|
||||
; -- open a file for iterative reading with f_read
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
f_close()
|
||||
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
c64.SETLFS(11, drivenumber, 0)
|
||||
void c64.OPEN() ; open 11,8,0,"filename"
|
||||
if_cc {
|
||||
iteration_in_progress = true
|
||||
have_first_byte = false
|
||||
void c64.CHKIN(11) ; use #11 as input channel
|
||||
if_cc {
|
||||
first_byte = c64.CHRIN() ; read first byte to test for file not found
|
||||
if not c64.READST() {
|
||||
have_first_byte = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
f_close()
|
||||
return false
|
||||
}
|
||||
|
||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
if not iteration_in_progress or not num_bytes
|
||||
return 0
|
||||
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
if have_first_byte {
|
||||
have_first_byte=false
|
||||
@(bufferpointer) = first_byte
|
||||
bufferpointer++
|
||||
list_blocks++
|
||||
num_bytes--
|
||||
}
|
||||
|
||||
void c64.CHKIN(11) ; use #11 as input channel again
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta _in_buffer+1
|
||||
lda bufferpointer+1
|
||||
sta _in_buffer+2
|
||||
}}
|
||||
repeat num_bytes {
|
||||
%asm {{
|
||||
jsr c64.CHRIN
|
||||
sta cx16.r5
|
||||
_in_buffer sta $ffff
|
||||
inc _in_buffer+1
|
||||
bne +
|
||||
inc _in_buffer+2
|
||||
+ inc list_blocks
|
||||
bne +
|
||||
inc list_blocks+1
|
||||
+
|
||||
}}
|
||||
|
||||
if cx16.r5==$0d { ; chance on I/o error status?
|
||||
first_byte = c64.READST()
|
||||
if first_byte & $40
|
||||
f_close() ; end of file, close it
|
||||
if first_byte
|
||||
return list_blocks
|
||||
}
|
||||
}
|
||||
return list_blocks
|
||||
}
|
||||
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
if have_first_byte {
|
||||
have_first_byte=false
|
||||
@(bufferpointer) = first_byte
|
||||
bufferpointer++
|
||||
list_blocks++
|
||||
}
|
||||
|
||||
while not c64.READST() {
|
||||
list_blocks += f_read(bufferpointer, 256)
|
||||
bufferpointer += 256
|
||||
}
|
||||
return list_blocks
|
||||
}
|
||||
|
||||
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
|
||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||
; Reads characters from the input file UNTIL a newline or return character (or EOF).
|
||||
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
|
||||
; I/O error status should be checked by the caller itself via READST() routine.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx #11
|
||||
jsr c64.CHKIN ; use channel 11 again for input
|
||||
ldy #0
|
||||
lda have_first_byte
|
||||
beq _loop
|
||||
lda #0
|
||||
sta have_first_byte
|
||||
lda first_byte
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
_loop jsr c64.CHRIN
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
beq _end
|
||||
iny
|
||||
cmp #$0a
|
||||
beq _line_end
|
||||
cmp #$0d
|
||||
bne _loop
|
||||
_line_end dey ; get rid of the trailing end-of-line char
|
||||
lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
_end rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub f_close() {
|
||||
; -- end an iterative file loading session (close channels).
|
||||
if iteration_in_progress {
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(11)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file saver functions (uses io channel 14) -----
|
||||
|
||||
sub f_open_w(ubyte drivenumber, uword filenameptr) -> ubyte {
|
||||
; -- open a file for iterative writing with f_write
|
||||
f_close_w()
|
||||
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
c64.SETLFS(14, drivenumber, 1)
|
||||
void c64.OPEN() ; open 14,8,1,"filename"
|
||||
if_cc {
|
||||
void c64.CHKOUT(14) ; use #14 as input channel
|
||||
return not c64.READST()
|
||||
}
|
||||
f_close_w()
|
||||
return false
|
||||
}
|
||||
|
||||
sub f_write(uword bufferpointer, uword num_bytes) -> ubyte {
|
||||
; -- write the given umber of bytes to the currently open file
|
||||
if num_bytes!=0 {
|
||||
void c64.CHKOUT(14) ; use #14 as input channel again
|
||||
repeat num_bytes {
|
||||
c64.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
}
|
||||
return not c64.READST()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub f_close_w() {
|
||||
; -- end an iterative file writing session (close channels).
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(14)
|
||||
}
|
||||
|
||||
|
||||
; ---- other functions ----
|
||||
|
||||
sub status(ubyte drivenumber) -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
uword messageptr = &filename
|
||||
c64.SETNAM(0, filename)
|
||||
c64.SETLFS(15, drivenumber, 15)
|
||||
void c64.OPEN() ; open 15,8,15
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(15) ; use #15 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while not c64.READST() {
|
||||
@(messageptr) = c64.CHRIN()
|
||||
messageptr++
|
||||
}
|
||||
|
||||
@(messageptr) = 0
|
||||
done:
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(15)
|
||||
return filename
|
||||
|
||||
io_error:
|
||||
filename = "?disk error"
|
||||
goto done
|
||||
}
|
||||
|
||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
c64.SETLFS(1, drivenumber, 0)
|
||||
uword end_address = address + size
|
||||
first_byte = 0 ; result var reuse
|
||||
|
||||
%asm {{
|
||||
lda address
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<P8ZP_SCRATCH_W1
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
jsr c64.SAVE
|
||||
php
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
plp
|
||||
}}
|
||||
|
||||
if_cc
|
||||
first_byte = c64.READST()==0
|
||||
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
|
||||
return first_byte
|
||||
}
|
||||
|
||||
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
uword end_of_load = 0
|
||||
if address_override
|
||||
secondary = 0
|
||||
c64.SETLFS(1, drivenumber, secondary)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
ldx address_override
|
||||
ldy address_override+1
|
||||
jsr c64.LOAD
|
||||
bcs +
|
||||
stx end_of_load
|
||||
sty end_of_load+1
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
}}
|
||||
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
|
||||
if end_of_load
|
||||
return end_of_load - address_override
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
str filename = "0:??????????????????????????????????????"
|
||||
|
||||
sub delete(ubyte drivenumber, uword filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
filename[0] = 's'
|
||||
filename[1] = ':'
|
||||
ubyte flen = string.copy(filenameptr, &filename+2)
|
||||
c64.SETNAM(flen+2, filename)
|
||||
c64.SETLFS(1, drivenumber, 15)
|
||||
void c64.OPEN()
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
}
|
||||
|
||||
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
filename[0] = 'r'
|
||||
filename[1] = ':'
|
||||
ubyte flen_new = string.copy(newfileptr, &filename+2)
|
||||
filename[flen_new+2] = '='
|
||||
ubyte flen_old = string.copy(oldfileptr, &filename+3+flen_new)
|
||||
c64.SETNAM(3+flen_new+flen_old, filename)
|
||||
c64.SETLFS(1, drivenumber, 15)
|
||||
void c64.OPEN()
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,7 @@
|
||||
; Prog8 internal Math library routines - always included by the compiler
|
||||
; Internal Math library routines - always included by the compiler
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import c64lib
|
||||
|
||||
~ math {
|
||||
%asminclude "library:math.asm", ""
|
||||
math {
|
||||
%asminclude "library:math.asm"
|
||||
}
|
||||
|
1250
compiler/res/prog8lib/prog8_funcs.asm
Normal file
1250
compiler/res/prog8lib/prog8_funcs.asm
Normal file
File diff suppressed because it is too large
Load Diff
1085
compiler/res/prog8lib/prog8_lib.asm
Normal file
1085
compiler/res/prog8lib/prog8_lib.asm
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user