mirror of
https://github.com/irmen/prog8.git
synced 2025-06-15 18:23:35 +00:00
Compare commits
653 Commits
v6.0-beta
...
version7.2
Author | SHA1 | Date | |
---|---|---|---|
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 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -26,8 +26,9 @@ parser.out
|
|||||||
parsetab.py
|
parsetab.py
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
.attach_pid*
|
.attach_pid*
|
||||||
|
compiler/lib/
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
/prog8compiler.jar
|
/prog8compiler.jar
|
||||||
sd*.img
|
sd*.img
|
||||||
|
*.d64
|
||||||
|
8
.idea/codeInsightSettings.xml
generated
Normal file
8
.idea/codeInsightSettings.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaProjectCodeInsightSettings">
|
||||||
|
<excluded-names>
|
||||||
|
<name>kotlin.Result</name>
|
||||||
|
</excluded-names>
|
||||||
|
</component>
|
||||||
|
</project>
|
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
@ -3,14 +3,11 @@
|
|||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
<Languages>
|
<Languages>
|
||||||
<language minSize="100" isEnabled="false" name="JavaScript" />
|
|
||||||
<language isEnabled="false" name="Groovy" />
|
|
||||||
<language isEnabled="false" name="Style Sheets" />
|
|
||||||
<language minSize="70" name="Kotlin" />
|
<language minSize="70" name="Kotlin" />
|
||||||
<language isEnabled="false" name="TypeScript" />
|
<language isEnabled="false" name="Groovy" />
|
||||||
<language isEnabled="false" name="ActionScript" />
|
|
||||||
</Languages>
|
</Languages>
|
||||||
</inspection_tool>
|
</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">
|
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||||
<option name="processCode" value="false" />
|
<option name="processCode" value="false" />
|
||||||
<option name="processLiterals" value="true" />
|
<option name="processLiterals" value="true" />
|
||||||
|
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_9_complete.xml
generated
9
.idea/libraries/antlr_4_9_complete.xml
generated
@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="antlr-4.9-complete">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-4.9-complete.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
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_9.xml
generated
9
.idea/libraries/antlr_runtime_4_9.xml
generated
@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="antlr-runtime-4.9">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/parser/antlr/lib/antlr-runtime-4.9.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
9
.idea/libraries/dbus_java_3_2_4.xml
generated
9
.idea/libraries/dbus_java_3_2_4.xml
generated
@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="dbus-java-3.2.4">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/dbusCompilerService/lib/dbus-java-3.2.4.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
Normal file
25
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="github.hypfvieh.dbus.java" type="repository">
|
||||||
|
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.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>
|
10
.idea/libraries/javax_json_api_1_1_4.xml
generated
10
.idea/libraries/javax_json_api_1_1_4.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="javax.json-api-1.1.4">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/javax.json-api-1.1.4.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/javax.json-1.1.4.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
10
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
Normal file
10
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||||
|
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.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>
|
9
.idea/libraries/kotlinx_cli_jvm.xml
generated
9
.idea/libraries/kotlinx_cli_jvm.xml
generated
@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="kotlinx-cli-jvm">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/compiler/lib/kotlinx-cli-jvm-0.3.1.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
15
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
Normal file
15
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||||
|
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.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>
|
10
.idea/libraries/slf4j_api_1_7_30.xml
generated
10
.idea/libraries/slf4j_api_1_7_30.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="slf4j-api-1.7.30">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/slf4j-api-1.7.30.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/slf4j-simple-1.7.30.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
11
.idea/libraries/slf4j_simple.xml
generated
Normal file
11
.idea/libraries/slf4j_simple.xml
generated
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="slf4j.simple" type="repository">
|
||||||
|
<properties maven-id="org.slf4j:slf4j-simple: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>
|
12
.idea/libraries/takes_http.xml
generated
12
.idea/libraries/takes_http.xml
generated
@ -1,12 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="takes-http">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/cactoos-0.42.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/commons-lang3-3.7.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/commons-text-1.4.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/httpCompilerService/lib/takes-1.19.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
10
.idea/libraries/unittest_libs.xml
generated
10
.idea/libraries/unittest_libs.xml
generated
@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="unittest-libs">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="file://$PROJECT_DIR$/compiler/lib" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
<jarDirectory url="file://$PROJECT_DIR$/compiler/lib" recursive="false" />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
6
.idea/misc.xml
generated
6
.idea/misc.xml
generated
@ -4,7 +4,7 @@
|
|||||||
<option name="perGrammarGenerationSettings">
|
<option name="perGrammarGenerationSettings">
|
||||||
<list>
|
<list>
|
||||||
<PerGrammarGenerationSettings>
|
<PerGrammarGenerationSettings>
|
||||||
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/prog8.g4" />
|
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" />
|
||||||
<option name="autoGen" value="true" />
|
<option name="autoGen" value="true" />
|
||||||
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
|
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
|
||||||
<option name="libDir" value="" />
|
<option name="libDir" value="" />
|
||||||
@ -16,7 +16,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
4
.idea/modules.xml
generated
4
.idea/modules.xml
generated
@ -2,7 +2,11 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/codeGeneration/codeGeneration.iml" filepath="$PROJECT_DIR$/codeGeneration/codeGeneration.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/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$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||||
|
11
.travis.yml
11
.travis.yml
@ -1,11 +0,0 @@
|
|||||||
language: java
|
|
||||||
sudo: false
|
|
||||||
# jdk: openjdk8
|
|
||||||
# dist: xenial
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- chmod +x ./gradlew
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./gradlew test
|
|
||||||
|
|
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
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
34
README.md
34
README.md
@ -1,5 +1,3 @@
|
|||||||
[](https://saythanks.io/to/irmen)
|
|
||||||
[](https://travis-ci.org/irmen/prog8)
|
|
||||||
[](https://prog8.readthedocs.io/)
|
[](https://prog8.readthedocs.io/)
|
||||||
|
|
||||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||||
@ -7,9 +5,6 @@ Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
|||||||
|
|
||||||
*Written by Irmen de Jong (irmen@razorvine.net)*
|
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||||
|
|
||||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
|
||||||
|
|
||||||
|
|
||||||
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
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,
|
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).
|
||||||
@ -19,27 +14,36 @@ Documentation
|
|||||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||||
https://prog8.readthedocs.io/
|
https://prog8.readthedocs.io/
|
||||||
|
|
||||||
|
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?
|
What does Prog8 provide?
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
- big reduction of source code length over raw assembly
|
- 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
|
- modularity, symbol scoping, subroutines
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- automatic variable allocations, automatic string and array variables and string sharing
|
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||||
- subroutines with an input- and output parameter signature
|
- 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.
|
||||||
- no stack frame allocations because parameters and local variables are automatically allocated statically
|
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||||
- constant folding in expressions and other high-level program optimizations
|
- subroutines with input parameters and result values
|
||||||
|
- high-level program optimizations
|
||||||
|
- small program boilerplate/compilersupport overhead
|
||||||
|
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||||
- conditional branches
|
- conditional branches
|
||||||
- floating point operations (requires the C64 Basic ROM routines for this)
|
|
||||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||||
- structs to group together sets of variables and manipulate them at once
|
|
||||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
- 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
|
- 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
|
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||||
- fast execution speed due to compilation to native assembly code
|
|
||||||
- inline assembly allows you to have full control when every cycle or byte matters
|
- inline assembly allows you to have full control when every cycle or byte matters
|
||||||
- supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, and provides them also on the C64.
|
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, 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:*
|
||||||
|
|
||||||
@ -52,7 +56,7 @@ What does Prog8 provide?
|
|||||||
|
|
||||||
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
||||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||||
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
- 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)!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
56
codeGeneration/build.gradle
Normal file
56
codeGeneration/build.gradle
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
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"
|
||||||
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
|
||||||
|
|
||||||
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
|
||||||
|
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/test"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
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" scope="TEST" name="hamcrest" level="project" />
|
||||||
|
<orderEntry type="library" name="junit.jupiter" 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,13 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.c64
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.compiler.target.cbm.viceMonListPostfix
|
||||||
import prog8.compiler.*
|
import prog8.compilerinterface.*
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.IMachineDefinition
|
|
||||||
import prog8.compiler.target.IMachineFloat
|
|
||||||
import prog8.parser.ModuleImporter
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
internal object C64MachineDefinition: IMachineDefinition {
|
object C64MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
@ -30,16 +27,23 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
importer.importLibraryModule(program, "syslib")
|
listOf("syslib")
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(programName: String) {
|
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")) {
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "$programName.vice-mon-list",
|
val cmdline = listOf(emulator, "-silent", "-moncommands", "${programNameWithPath}.$viceMonListPostfix",
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programName + ".prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
val process: Process
|
val process: Process
|
||||||
try {
|
try {
|
||||||
@ -70,7 +74,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
||||||
|
|
||||||
|
|
||||||
internal class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
|
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_REG = 0x03 // temp storage for a register, must be B1+1
|
||||||
@ -79,13 +83,12 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats && options.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
if (options.floats && options.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||||
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
if (options.zeropage == ZeropageType.FULL) {
|
if (options.zeropage == ZeropageType.FULL) {
|
||||||
free.addAll(0x04..0xf9)
|
free.addAll(0x04..0xf9)
|
||||||
free.add(0xff)
|
free.add(0xff)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||||
} else {
|
} else {
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
@ -102,13 +105,14 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
// 0x90-0xfa is 'kernel work storage area'
|
// 0x90-0xfa is 'kernal work storage area'
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
// remove the zero page locations used for floating point operations from the free list
|
// remove the zeropage locations used for floating point operations from the free list
|
||||||
free.removeAll(listOf(
|
free.removeAll(listOf(
|
||||||
|
0x22, 0x23, 0x24, 0x25,
|
||||||
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
@ -117,7 +121,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.zeropage!=ZeropageType.DONTUSE) {
|
if(options.zeropage!= ZeropageType.DONTUSE) {
|
||||||
// add the free Zp addresses
|
// 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*
|
// 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,
|
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||||
@ -128,17 +132,13 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
free.clear()
|
free.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
require(SCRATCH_B1 !in free)
|
|
||||||
require(SCRATCH_REG !in free)
|
|
||||||
require(SCRATCH_W1 !in free)
|
|
||||||
require(SCRATCH_W2 !in free)
|
|
||||||
|
|
||||||
for (reserved in options.zpReserved)
|
removeReservedFromFreePool()
|
||||||
reserve(reserved)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short): IMachineFloat {
|
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short):
|
||||||
|
IMachineFloat {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||||
@ -149,7 +149,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
val flt = num.toDouble()
|
val flt = num.toDouble()
|
||||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||||
throw CompilerException("floating point number out of 5-byte mflpt range: $this")
|
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||||
if (flt == 0.0)
|
if (flt == 0.0)
|
||||||
return zero
|
return zero
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
return when {
|
return when {
|
||||||
exponent < 0 -> zero // underflow, use zero instead
|
exponent < 0 -> zero // underflow, use zero instead
|
||||||
exponent > 255 -> throw CompilerException("floating point overflow: $this")
|
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
||||||
exponent == 0 -> zero
|
exponent == 0 -> zero
|
||||||
else -> {
|
else -> {
|
||||||
val mantLong = mantissa.toLong()
|
val mantLong = mantissa.toLong()
|
@ -1,34 +1,50 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.cbm
|
||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
import com.github.michaelbull.result.Ok
|
||||||
import prog8.compiler.OutputType
|
import com.github.michaelbull.result.Result
|
||||||
import prog8.compiler.target.CompilationTarget
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.compiler.target.IAssemblyProgram
|
import prog8.compilerinterface.CompilationOptions
|
||||||
import prog8.compiler.target.generatedLabelPrefix
|
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 java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
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 {
|
||||||
|
|
||||||
class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyProgram {
|
|
||||||
private val assemblyFile = outputDir.resolve("$name.asm")
|
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||||
private val prgFile = outputDir.resolve("$name.prg")
|
private val prgFile = outputDir.resolve("$name.prg")
|
||||||
private val binFile = outputDir.resolve("$name.bin")
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
private val viceMonListFile = outputDir.resolve("$name.$viceMonListPostfix")
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions) {
|
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)
|
// 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",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
||||||
|
|
||||||
|
if(quiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
val outFile = when (options.output) {
|
||||||
OutputType.PRG -> {
|
OutputType.PRG -> {
|
||||||
command.add("--cbm-prg")
|
command.add("--cbm-prg")
|
||||||
println("\nCreating prg for target ${CompilationTarget.instance.name}.")
|
println("\nCreating prg for target $compTarget.")
|
||||||
prgFile
|
prgFile
|
||||||
}
|
}
|
||||||
OutputType.RAW -> {
|
OutputType.RAW -> {
|
||||||
command.add("--nostart")
|
command.add("--nostart")
|
||||||
println("\nCreating raw binary for target ${CompilationTarget.instance.name}.")
|
println("\nCreating raw binary for target $compTarget.")
|
||||||
binFile
|
binFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,13 +52,11 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
|
|||||||
|
|
||||||
val proc = ProcessBuilder(command).inheritIO().start()
|
val proc = ProcessBuilder(command).inheritIO().start()
|
||||||
val result = proc.waitFor()
|
val result = proc.waitFor()
|
||||||
if (result != 0) {
|
if (result == 0) {
|
||||||
System.err.println("assembler failed with returncode $result")
|
removeGeneratedLabelsFromMonlist()
|
||||||
exitProcess(result)
|
generateBreakpointList()
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
removeGeneratedLabelsFromMonlist()
|
|
||||||
generateBreakpointList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeGeneratedLabelsFromMonlist() {
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
@ -72,3 +86,18 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
|
|||||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
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
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
||||||
@ -74,7 +74,7 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
|||||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||||
|
|
||||||
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// the when statement (on bytes) generates a sequence of:
|
// when statement (on bytes) generates a sequence of:
|
||||||
// lda $ce01,x
|
// lda $ce01,x
|
||||||
// cmp #$20
|
// cmp #$20
|
||||||
// beq check_prog8_s72choice_32
|
// beq check_prog8_s72choice_32
|
||||||
@ -180,7 +180,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
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
|
// 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)
|
// 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>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (pair in linesByFour) {
|
for (pair in linesByFour) {
|
||||||
val first = pair[0].value.trimStart()
|
val first = pair[0].value.trimStart()
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,15 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ArrayElementTypes
|
import prog8.ast.base.ArrayToElementTypes
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.RegisterOrPair
|
import prog8.ast.base.RegisterOrPair
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.RangeExpr
|
import prog8.ast.expressions.RangeExpr
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.target.AssemblyError
|
||||||
|
import prog8.compilerinterface.toConstantIntegerRange
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -21,13 +22,13 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
is RangeExpr -> {
|
is RangeExpr -> {
|
||||||
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||||
if(range==null) {
|
if(range==null) {
|
||||||
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as RangeExpr)
|
translateForOverNonconstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), stmt.iterable as RangeExpr)
|
||||||
} else {
|
} else {
|
||||||
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), range)
|
translateForOverConstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as 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")
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||||
}
|
}
|
||||||
@ -40,6 +41,13 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
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) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
if (stepsize==1 || stepsize==-1) {
|
if (stepsize==1 || stepsize==-1) {
|
||||||
@ -49,17 +57,17 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
$incdec $varname
|
$incdec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -67,8 +75,8 @@ $endLabel""")
|
|||||||
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
if(stepsize>0) {
|
if(stepsize>0) {
|
||||||
@ -117,16 +125,15 @@ $modifiedLabel2 cmp #0 ; modified
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1
|
inc $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
""")
|
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname
|
+ dec $varname""")
|
||||||
jmp $loopLabel""")
|
asmgen.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
@ -239,7 +246,7 @@ $endLabel""")
|
|||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val iterableName = asmgen.asmVariableName(ident)
|
val iterableName = asmgen.asmVariableName(ident)
|
||||||
val decl = ident.targetVarDecl(program.namespace)!!
|
val decl = ident.targetVarDecl(program)!!
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -282,7 +289,7 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
||||||
// allocate index var on ZP
|
// allocate index var on ZP
|
||||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||||
@ -321,7 +328,7 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
||||||
// allocate index var on ZP
|
// allocate index var on ZP
|
||||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||||
@ -369,7 +376,7 @@ $loopLabel""")
|
|||||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||||
}
|
}
|
||||||
2 -> {
|
2 -> {
|
||||||
if(range.last==255) {
|
if(range.last==255 || range.last==254) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inc $varname
|
inc $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
@ -377,34 +384,34 @@ $loopLabel""")
|
|||||||
bne $loopLabel""")
|
bne $loopLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
|
inc $varname
|
||||||
|
inc $varname
|
||||||
lda $varname
|
lda $varname
|
||||||
cmp #${range.last}
|
cmp #${range.last+2}
|
||||||
beq $endLabel
|
bne $loopLabel""")
|
||||||
inc $varname
|
|
||||||
inc $varname
|
|
||||||
jmp $loopLabel""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-2 -> {
|
-2 -> {
|
||||||
when (range.last) {
|
when (range.last) {
|
||||||
0 -> asmgen.out("""
|
0 -> {
|
||||||
lda $varname
|
asmgen.out("""
|
||||||
beq $endLabel
|
lda $varname
|
||||||
dec $varname
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname
|
||||||
jmp $loopLabel""")
|
dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
}
|
||||||
1 -> asmgen.out("""
|
1 -> asmgen.out("""
|
||||||
dec $varname
|
dec $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname
|
||||||
bne $loopLabel""")
|
bne $loopLabel""")
|
||||||
else -> asmgen.out("""
|
else -> asmgen.out("""
|
||||||
lda $varname
|
dec $varname
|
||||||
cmp #${range.last}
|
dec $varname
|
||||||
beq $endLabel
|
lda $varname
|
||||||
dec $varname
|
cmp #${range.last-2}
|
||||||
dec $varname
|
bne $loopLabel""")
|
||||||
jmp $loopLabel""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -415,8 +422,8 @@ $loopLabel""")
|
|||||||
beq $endLabel
|
beq $endLabel
|
||||||
clc
|
clc
|
||||||
adc #${range.step}
|
adc #${range.step}
|
||||||
sta $varname
|
sta $varname""")
|
||||||
jmp $loopLabel""")
|
asmgen.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
@ -452,9 +459,9 @@ $loopLabel""")
|
|||||||
sta $varname
|
sta $varname
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
adc #>${range.step}
|
adc #>${range.step}
|
||||||
sta $varname+1
|
sta $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -480,11 +487,10 @@ $loopLabel""")
|
|||||||
$endLabel""")
|
$endLabel""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
|
||||||
cmp #${range.last}
|
|
||||||
beq $endLabel
|
|
||||||
inc $varname
|
inc $varname
|
||||||
jmp $loopLabel
|
lda $varname
|
||||||
|
cmp #${range.last+1}
|
||||||
|
bne $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
@ -505,23 +511,22 @@ $loopLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
dec $varname
|
dec $varname
|
||||||
jmp $loopLabel
|
bne $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
|
||||||
cmp #${range.last}
|
|
||||||
beq $endLabel
|
|
||||||
dec $varname
|
dec $varname
|
||||||
jmp $loopLabel
|
lda $varname
|
||||||
|
cmp #${range.last-1}
|
||||||
|
bne $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,13 +551,12 @@ $loopLabel""")
|
|||||||
bne +
|
bne +
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
cmp #>${range.last}
|
cmp #>${range.last}
|
||||||
bne +
|
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1
|
inc $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,17 +578,16 @@ $loopLabel""")
|
|||||||
bne +
|
bne +
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
cmp #>${range.last}
|
cmp #>${range.last}
|
||||||
bne +
|
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname
|
+ dec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
||||||
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine())
|
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).getOr(DataType.UNDEFINED), stmt.definingSubroutine)
|
||||||
}
|
}
|
@ -1,15 +1,20 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.InlineAssembly
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.ast.statements.RegisterOrStatusflag
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.compiler.target.c64.codegen.assignment.*
|
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 class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -22,62 +27,79 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun saveXbeforeCall(stmt: IFunctionCall) {
|
internal fun saveXbeforeCall(stmt: IFunctionCall) {
|
||||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
if(sub.shouldSaveX()) {
|
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
|
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
|
||||||
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
|
|
||||||
if(regSaveOnStack)
|
if(regSaveOnStack)
|
||||||
asmgen.saveRegisterStack(CpuRegister.X, keepAonEntry)
|
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
|
||||||
else
|
else
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine()!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun restoreXafterCall(stmt: IFunctionCall) {
|
internal fun restoreXafterCall(stmt: IFunctionCall) {
|
||||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
if(sub.shouldSaveX()) {
|
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
|
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
|
||||||
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
|
|
||||||
|
|
||||||
if(regSaveOnStack)
|
if(regSaveOnStack)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.X, keepAonReturn)
|
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
|
||||||
else
|
else
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
||||||
// Output only the code to setup the parameters and perform the actual call
|
// 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 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!!
|
// 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)
|
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||||
|
|
||||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
val subName = asmgen.asmSymbolName(stmt.target)
|
val subName = asmgen.asmSymbolName(stmt.target)
|
||||||
if(stmt.args.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
|
|
||||||
if(sub.asmParameterRegisters.isEmpty()) {
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
// via variables
|
// via variables
|
||||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
argumentViaVariable(sub, arg.first, arg.second)
|
argumentViaVariable(sub, arg.first, arg.second)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// via registers
|
require(sub.isAsmSubroutine)
|
||||||
if(sub.parameters.size==1) {
|
if(sub.parameters.size==1) {
|
||||||
// just a single parameter, no risk of clobbering registers
|
// just a single parameter, no risk of clobbering registers
|
||||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
|
||||||
} else {
|
} 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 {
|
when {
|
||||||
stmt.args.all {it is AddressOf ||
|
stmt.args.all {isNoClobberRisk(it)} -> {
|
||||||
it is NumericLiteralValue ||
|
|
||||||
it is StringLiteralValue ||
|
|
||||||
it is ArrayLiteralValue ||
|
|
||||||
it is IdentifierReference} -> {
|
|
||||||
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
// 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 argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
||||||
val (vregsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
val (cx16virtualRegs, args2) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
||||||
for(arg in vregsArgsInfo)
|
val (cpuRegs, statusRegs) = args2.partition { it.second.registerOrPair!=null }
|
||||||
|
for(arg in cx16virtualRegs)
|
||||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||||
for(arg in otherRegsArgsInfo)
|
for(arg in cpuRegs)
|
||||||
|
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||||
|
for(arg in statusRegs)
|
||||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -89,17 +111,22 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sub.inline && asmgen.options.optimize) {
|
if(!sub.inline || !asmgen.options.optimize) {
|
||||||
if(sub.containsDefinedVariables())
|
|
||||||
throw AssemblyError("can't inline sub with vars")
|
|
||||||
if(!sub.isAsmSubroutine && sub.parameters.isNotEmpty())
|
|
||||||
throw AssemblyError("can't inline a non-asm subroutine with parameters")
|
|
||||||
asmgen.out(" \t; inlined routine follows: ${sub.name} from ${sub.position}")
|
|
||||||
val statements = sub.statements.filter { it !is ParameterVarDecl && it !is Directive }
|
|
||||||
statements.forEach { asmgen.translate(it) }
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
asmgen.out(" jsr $subName")
|
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
|
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||||
@ -108,14 +135,25 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||||
// this is called when one or more of the arguments are 'complex' and
|
// 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.
|
// 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())
|
if(sub.parameters.isEmpty())
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
// 1. load all arguments reversed onto the stack: first arg goes last (is on top).
|
// 1. load all arguments reversed onto the stack: first arg goes last (is on top).
|
||||||
|
|
||||||
for (arg in stmt.args.reversed())
|
for (arg in stmt.args.reversed())
|
||||||
asmgen.translateExpression(arg)
|
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 argForCarry: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
@ -130,11 +168,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
argForCarry = argi
|
argForCarry = argi
|
||||||
}
|
}
|
||||||
argi.value.second.statusflag != null -> throw AssemblyError("can only use Carry as status flag parameter")
|
argi.value.second.statusflag != null -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
argi.value.second.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
|
argi.value.second.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
|
||||||
require(argForXregister==null)
|
require(argForXregister==null)
|
||||||
argForXregister = argi
|
argForXregister = argi
|
||||||
}
|
}
|
||||||
argi.value.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
|
argi.value.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
|
||||||
require(argForAregister == null)
|
require(argForAregister == null)
|
||||||
argForAregister = argi
|
argForAregister = argi
|
||||||
}
|
}
|
||||||
@ -146,21 +184,31 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
when (sub.parameters[argi.index].type) {
|
when (sub.parameters[argi.index].type) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
// only load the lsb of the virtual register
|
// only load the lsb of the virtual register
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
lda P8ESTACK_LO$plusIdxStr,x
|
lda P8ESTACK_LO$plusIdxStr,x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||||
""")
|
""")
|
||||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
asmgen.out(
|
||||||
|
" stz cx16.${
|
||||||
|
argi.value.second.registerOrPair.toString().lowercase()
|
||||||
|
}+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
asmgen.out(
|
||||||
|
" lda #0 | sta cx16.${
|
||||||
|
argi.value.second.registerOrPair.toString().lowercase()
|
||||||
|
}+1")
|
||||||
}
|
}
|
||||||
in WordDatatypes ->
|
in WordDatatypes, in IterableDatatypes ->
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
lda P8ESTACK_LO$plusIdxStr,x
|
lda P8ESTACK_LO$plusIdxStr,x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||||
lda P8ESTACK_HI$plusIdxStr,x
|
lda P8ESTACK_HI$plusIdxStr,x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1
|
sta cx16.${
|
||||||
|
argi.value.second.registerOrPair.toString().lowercase()
|
||||||
|
}+1
|
||||||
""")
|
""")
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -215,7 +263,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val valueIDt = value.inferType(program)
|
val valueIDt = value.inferType(program)
|
||||||
if(!valueIDt.isKnown)
|
if(!valueIDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueIDt.getOr(DataType.UNDEFINED)
|
||||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
@ -228,7 +276,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val valueIDt = value.inferType(program)
|
val valueIDt = value.inferType(program)
|
||||||
if(!valueIDt.isKnown)
|
if(!valueIDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueIDt.getOr(DataType.UNDEFINED)
|
||||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
@ -240,71 +288,66 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
if(valueDt largerThan requiredDt)
|
if(valueDt largerThan requiredDt)
|
||||||
throw AssemblyError("can only convert byte values to word param types")
|
throw AssemblyError("can only convert byte values to word param types")
|
||||||
}
|
}
|
||||||
when {
|
if (statusflag!=null) {
|
||||||
statusflag!=null -> {
|
if(requiredDt!=valueDt)
|
||||||
if(requiredDt!=valueDt)
|
throw AssemblyError("for statusflag, byte value is required")
|
||||||
throw AssemblyError("for statusflag, byte value is required")
|
if (statusflag == Statusflag.Pc) {
|
||||||
if (statusflag == Statusflag.Pc) {
|
// this param needs to be set last, right before the jsr
|
||||||
// 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
|
||||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
when(value) {
|
||||||
when(value) {
|
is NumericLiteralValue -> {
|
||||||
is NumericLiteralValue -> {
|
val carrySet = value.number.toInt() != 0
|
||||||
val carrySet = value.number.toInt() != 0
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
asmgen.out(if(carrySet) " sec" else " clc")
|
}
|
||||||
}
|
is IdentifierReference -> {
|
||||||
is IdentifierReference -> {
|
val sourceName = asmgen.asmVariableName(value)
|
||||||
val sourceName = asmgen.asmVariableName(value)
|
asmgen.out("""
|
||||||
asmgen.out("""
|
pha
|
||||||
pha
|
lda $sourceName
|
||||||
lda $sourceName
|
beq +
|
||||||
beq +
|
sec
|
||||||
sec
|
bcs ++
|
||||||
bcs ++
|
+ clc
|
||||||
+ clc
|
+ pla
|
||||||
+ pla
|
""")
|
||||||
""")
|
}
|
||||||
}
|
else -> {
|
||||||
else -> {
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.out("""
|
||||||
asmgen.out("""
|
beq +
|
||||||
beq +
|
sec
|
||||||
sec
|
bcs ++
|
||||||
bcs ++
|
+ clc
|
||||||
+ clc
|
+""")
|
||||||
+""")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else throw AssemblyError("can only use Carry as status flag parameter")
|
} else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
}
|
}
|
||||||
else -> {
|
else {
|
||||||
// via register or register pair
|
// via register or register pair
|
||||||
register!!
|
register!!
|
||||||
if(requiredDt largerThan valueDt) {
|
if(requiredDt largerThan valueDt) {
|
||||||
// we need to sign extend the source, do this via temporary word variable
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1")
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
|
||||||
asmgen.assignExpressionToVariable(value, scratchVar, DataType.UBYTE, sub)
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt)
|
||||||
asmgen.signExtendVariableLsb(scratchVar, valueDt)
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
|
||||||
asmgen.assignVariableToRegister(scratchVar, register)
|
} else {
|
||||||
}
|
val target: AsmAssignTarget =
|
||||||
else {
|
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||||
val target: AsmAssignTarget =
|
AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register)
|
||||||
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
else
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register)
|
AsmAssignTarget.fromRegisters(register, false, sub, program, asmgen)
|
||||||
else
|
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||||
AsmAssignTarget.fromRegisters(register, sub, program, asmgen)
|
if(value is IdentifierReference) {
|
||||||
val src = if(valueDt in PassByReferenceDatatypes) {
|
val addr = AddressOf(value, Position.DUMMY)
|
||||||
if(value is IdentifierReference) {
|
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||||
val addr = AddressOf(value, Position.DUMMY)
|
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
|
||||||
} else {
|
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
} else {
|
||||||
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.target.AssemblyError
|
||||||
|
|
||||||
|
|
||||||
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -15,11 +15,11 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
val targetIdent = stmt.target.identifier
|
val targetIdent = stmt.target.identifier
|
||||||
val targetMemory = stmt.target.memoryAddress
|
val targetMemory = stmt.target.memoryAddress
|
||||||
val targetArrayIdx = stmt.target.arrayindexed
|
val targetArrayIdx = stmt.target.arrayindexed
|
||||||
val scope = stmt.definingSubroutine()
|
val scope = stmt.definingSubroutine
|
||||||
when {
|
when {
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val what = asmgen.asmVariableName(targetIdent)
|
val what = asmgen.asmVariableName(targetIdent)
|
||||||
when (stmt.target.inferType(program).typeOrElse(DataType.STRUCT)) {
|
when (stmt.target.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
if(incr)
|
if(incr)
|
||||||
@ -65,9 +65,10 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
||||||
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = targetArrayIdx.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
if(targetArrayIdx.indexer.indexNum!=null) {
|
val constIndex = targetArrayIdx.indexer.constIndex()
|
||||||
val indexValue = targetArrayIdx.indexer.constIndex()!! * elementDt.memorySize()
|
if(constIndex!=null) {
|
||||||
|
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
@ -1,11 +1,12 @@
|
|||||||
package prog8.compiler.target.c64.codegen.assignment
|
package prog8.compiler.target.cpu6502.codegen.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compilerinterface.IMemSizer
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.AssemblyError
|
||||||
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
|
|
||||||
|
|
||||||
internal enum class TargetStorageKind {
|
internal enum class TargetStorageKind {
|
||||||
@ -40,11 +41,12 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
||||||
val asmVarname: String
|
val asmVarname: String by lazy {
|
||||||
get() = if(array==null)
|
if (array == null)
|
||||||
variableAsmName!!
|
variableAsmName!!
|
||||||
else
|
else
|
||||||
asmgen.asmVariableName(array.arrayvar)
|
asmgen.asmVariableName(array.arrayvar)
|
||||||
|
}
|
||||||
|
|
||||||
lateinit var origAssign: AsmAssignment
|
lateinit var origAssign: AsmAssignment
|
||||||
|
|
||||||
@ -58,23 +60,23 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
val idt = inferType(program)
|
val idt = inferType(program)
|
||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val dt = idt.typeOrElse(DataType.STRUCT)
|
val dt = idt.getOr(DataType.UNDEFINED)
|
||||||
when {
|
when {
|
||||||
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
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)
|
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)
|
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRegisters(registers: RegisterOrPair, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, scope, register = registers)
|
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
|
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
|
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
|
||||||
RegisterOrPair.R0,
|
RegisterOrPair.R0,
|
||||||
@ -92,7 +94,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R12,
|
RegisterOrPair.R12,
|
||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
|
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +106,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
private val variableAsmName: String? = null,
|
private val variableAsmName: String? = null,
|
||||||
val array: ArrayIndexedExpression? = null,
|
val array: ArrayIndexedExpression? = null,
|
||||||
val memory: DirectMemoryRead? = null,
|
val memory: DirectMemoryRead? = null,
|
||||||
val register: CpuRegister? = null,
|
val register: RegisterOrPair? = null,
|
||||||
val number: NumericLiteralValue? = null,
|
val number: NumericLiteralValue? = null,
|
||||||
val expression: Expression? = null
|
val expression: Expression? = null
|
||||||
)
|
)
|
||||||
@ -119,13 +121,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
asmgen.asmVariableName(array.arrayvar)
|
asmgen.asmVariableName(array.arrayvar)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource {
|
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
|
||||||
return when {
|
|
||||||
indexer.indexNum!=null -> fromAstSource(indexer.indexNum!!, program, asmgen)
|
|
||||||
indexer.indexVar!=null -> fromAstSource(indexer.indexVar!!, program, asmgen)
|
|
||||||
else -> throw AssemblyError("weird indexer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
||||||
val cv = value.constValue(program)
|
val cv = value.constValue(program)
|
||||||
@ -137,18 +133,26 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
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 ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = asmgen.asmVariableName(value))
|
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 -> {
|
is DirectMemoryRead -> {
|
||||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
}
|
}
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
||||||
}
|
}
|
||||||
is FunctionCall -> {
|
is FunctionCall -> {
|
||||||
when (val sub = value.target.targetStatement(program.namespace)) {
|
when (val sub = value.target.targetStatement(program)) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
|
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")
|
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||||
@ -159,7 +163,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
val returnType = value.inferType(program)
|
val returnType = value.inferType(program)
|
||||||
if(!returnType.isKnown)
|
if(!returnType.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.typeOrElse(DataType.STRUCT), expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOr(DataType.UNDEFINED), expression = value)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw AssemblyError("weird call")
|
throw AssemblyError("weird call")
|
||||||
@ -170,7 +174,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
val dt = value.inferType(program)
|
val dt = value.inferType(program)
|
||||||
if(!dt.isKnown)
|
if(!dt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.typeOrElse(DataType.STRUCT), expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.getOr(DataType.UNDEFINED), expression = value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,12 +202,13 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
internal class AsmAssignment(val source: AsmAssignSource,
|
internal class AsmAssignment(val source: AsmAssignSource,
|
||||||
val target: AsmAssignTarget,
|
val target: AsmAssignTarget,
|
||||||
val isAugmentable: Boolean,
|
val isAugmentable: Boolean,
|
||||||
|
memsizer: IMemSizer,
|
||||||
val position: Position) {
|
val position: Position) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||||
require(source.datatype != DataType.STRUCT) { "must not be placeholder datatype" }
|
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
|
||||||
require(source.datatype.memorySize() <= target.datatype.memorySize()) {
|
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||||
"source storage size must be less or equal to target datatype storage size"
|
"source storage size must be less or equal to target datatype storage size"
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,13 @@
|
|||||||
package prog8.compiler.target.cx16
|
package prog8.compiler.target.cx16
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.compiler.*
|
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.IMachineDefinition
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.parser.ModuleImporter
|
import prog8.compiler.target.cbm.viceMonListPostfix
|
||||||
|
import prog8.compilerinterface.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
internal object CX16MachineDefinition: IMachineDefinition {
|
|
||||||
|
object CX16MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
override val cpu = CpuType.CPU65c02
|
override val cpu = CpuType.CPU65c02
|
||||||
|
|
||||||
@ -27,16 +26,35 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
listOf("syslib")
|
||||||
importer.importLibraryModule(program, "syslib")
|
else
|
||||||
|
emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(programName: String) {
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
for(emulator in listOf("x16emu")) {
|
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...")
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "$programName.prg")
|
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
val process: Process
|
val process: Process
|
||||||
try {
|
try {
|
||||||
@ -69,7 +87,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
"rmb", "smb", "stp", "wai")
|
"rmb", "smb", "stp", "wai")
|
||||||
|
|
||||||
|
|
||||||
internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x7a // temp storage for a single byte
|
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_REG = 0x7b // temp storage for a register, must be B1+1
|
||||||
@ -78,38 +96,29 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats && options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||||
throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or '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.
|
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||||
|
|
||||||
when (options.zeropage) {
|
when (options.zeropage) {
|
||||||
ZeropageType.FULL -> {
|
ZeropageType.FULL -> {
|
||||||
free.addAll(0x22..0xff)
|
free.addAll(0x22..0xff)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
}
|
}
|
||||||
ZeropageType.KERNALSAFE -> {
|
ZeropageType.KERNALSAFE -> {
|
||||||
free.addAll(0x22..0x7f)
|
free.addAll(0x22..0x7f)
|
||||||
free.addAll(0xa9..0xff)
|
free.addAll(0xa9..0xff)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
}
|
}
|
||||||
ZeropageType.BASICSAFE -> {
|
ZeropageType.BASICSAFE -> {
|
||||||
free.addAll(0x22..0x7f)
|
free.addAll(0x22..0x7f)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
}
|
}
|
||||||
ZeropageType.DONTUSE -> {
|
ZeropageType.DONTUSE -> {
|
||||||
free.clear() // don't use zeropage at all
|
free.clear() // don't use zeropage at all
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||||
}
|
}
|
||||||
|
|
||||||
require(SCRATCH_B1 !in free)
|
removeReservedFromFreePool()
|
||||||
require(SCRATCH_REG !in free)
|
|
||||||
require(SCRATCH_W1 !in free)
|
|
||||||
require(SCRATCH_W2 !in free)
|
|
||||||
|
|
||||||
for (reserved in options.zpReserved)
|
|
||||||
reserve(reserved)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
151
codeGeneration/test/AsmGenTests.kt
Normal file
151
codeGeneration/test/AsmGenTests.kt
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package prog8tests.asmgen
|
||||||
|
|
||||||
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
|
import org.hamcrest.Matchers.equalTo
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.base.RegisterOrPair
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestAsmGen6502 {
|
||||||
|
|
||||||
|
private 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
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSymbolNameFromStrings() {
|
||||||
|
val program = createTestProgram()
|
||||||
|
val asmgen = createTestAsmGen(program)
|
||||||
|
|
||||||
|
assertThat(asmgen.asmSymbolName("name"), equalTo("name"))
|
||||||
|
assertThat(asmgen.asmSymbolName("<name>"), equalTo("prog8_name"))
|
||||||
|
assertThat(asmgen.asmSymbolName(RegisterOrPair.R15), equalTo("cx16.r15"))
|
||||||
|
assertThat(asmgen.asmSymbolName(listOf("a", "b", "name")), equalTo("a.b.name"))
|
||||||
|
assertThat(asmgen.asmVariableName("name"), equalTo("name"))
|
||||||
|
assertThat(asmgen.asmVariableName("<name>"), equalTo("prog8_name"))
|
||||||
|
assertThat(asmgen.asmVariableName(listOf("a", "b", "name")), equalTo("a.b.name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSymbolNameFromVarIdentifier() {
|
||||||
|
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
|
||||||
|
assertThat(asmgen.asmSymbolName(localvarIdent), equalTo("localvar"))
|
||||||
|
assertThat(asmgen.asmVariableName(localvarIdent), equalTo("localvar"))
|
||||||
|
val localvarIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main", "start", "localvar") }.value as AddressOf).identifier
|
||||||
|
assertThat(asmgen.asmSymbolName(localvarIdentScoped), equalTo("main.start.localvar"))
|
||||||
|
assertThat(asmgen.asmVariableName(localvarIdentScoped), equalTo("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
|
||||||
|
assertThat(asmgen.asmSymbolName(scopedVarIdent), equalTo("main.var_outside"))
|
||||||
|
assertThat(asmgen.asmVariableName(scopedVarIdent), equalTo("var_outside"))
|
||||||
|
val scopedVarIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main", "var_outside") }.value as AddressOf).identifier
|
||||||
|
assertThat(asmgen.asmSymbolName(scopedVarIdentScoped), equalTo("main.var_outside"))
|
||||||
|
assertThat(asmgen.asmVariableName(scopedVarIdentScoped), equalTo("main.var_outside"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSymbolNameFromLabelIdentifier() {
|
||||||
|
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
|
||||||
|
assertThat(asmgen.asmSymbolName(localLabelIdent), equalTo("_locallabel"))
|
||||||
|
assertThat("as a variable it uses different naming rules (no underscore prefix)", asmgen.asmVariableName(localLabelIdent), equalTo("locallabel"))
|
||||||
|
val localLabelIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main","start","locallabel") }.value as AddressOf).identifier
|
||||||
|
assertThat(asmgen.asmSymbolName(localLabelIdentScoped), equalTo("main.start._locallabel"))
|
||||||
|
assertThat("as a variable it uses different naming rules (no underscore prefix)", asmgen.asmVariableName(localLabelIdentScoped), equalTo("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
|
||||||
|
assertThat(asmgen.asmSymbolName(scopedLabelIdent), equalTo("main._label_outside"))
|
||||||
|
assertThat("as a variable it uses different naming rules (no underscore prefix)", asmgen.asmVariableName(scopedLabelIdent), equalTo("label_outside"))
|
||||||
|
val scopedLabelIdentScoped = (sub.statements.filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("main","label_outside") }.value as AddressOf).identifier
|
||||||
|
assertThat(asmgen.asmSymbolName(scopedLabelIdentScoped), equalTo("main._label_outside"))
|
||||||
|
assertThat("as a variable it uses different naming rules (no underscore prefix)", asmgen.asmVariableName(scopedLabelIdentScoped), equalTo("main.label_outside"))
|
||||||
|
}
|
||||||
|
}
|
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()
|
||||||
|
}
|
||||||
|
}
|
54
codeOptimizers/build.gradle
Normal file
54
codeOptimizers/build.gradle
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
|
||||||
|
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/test"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
18
codeOptimizers/codeOptimizers.iml
Normal file
18
codeOptimizers/codeOptimizers.iml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="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" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
|
||||||
|
<orderEntry type="library" name="junit.jupiter" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -1,20 +1,24 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.expressions.BinaryExpression
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.expressions.augmentAssignmentOperators
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
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
|
||||||
|
|
||||||
|
|
||||||
internal class BinExprSplitter(private val program: Program) : AstWalker() {
|
class BinExprSplitter(private val program: Program, private val options: CompilationOptions, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:
|
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...: [ IS THIS STILL TRUE AFTER ALL CHANGES? ]
|
||||||
// if(decl.type==VarDeclType.VAR ) {
|
// if(decl.type==VarDeclType.VAR ) {
|
||||||
// val binExpr = decl.value as? BinaryExpression
|
// val binExpr = decl.value as? BinaryExpression
|
||||||
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
||||||
@ -36,6 +40,11 @@ internal class BinExprSplitter(private val program: Program) : AstWalker() {
|
|||||||
|
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
if (binExpr != null) {
|
if (binExpr != null) {
|
||||||
|
|
||||||
|
if(binExpr.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
||||||
@ -53,17 +62,18 @@ X = BinExpr X = LeftExpr
|
|||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) {
|
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target)) {
|
||||||
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
|
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
if(isSimpleExpression(binExpr.right) && !assignment.isAugmentable) {
|
if(binExpr.right.isSimple && !assignment.isAugmentable) {
|
||||||
val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position)
|
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, binExpr.left.position)
|
||||||
val targetExpr = assignment.target.toExpression()
|
val targetExpr = assignment.target.toExpression()
|
||||||
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, firstAssign, assignment.definingScope()),
|
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
|
||||||
IAstModification.ReplaceNode(assignment.value, augExpr, assignment))
|
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,12 +85,9 @@ X = BinExpr X = LeftExpr
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSimpleExpression(expr: Expression) =
|
private fun isSimpleTarget(target: AssignTarget) =
|
||||||
expr is IdentifierReference || expr is NumericLiteralValue || expr is AddressOf || expr is DirectMemoryRead || expr is StringLiteralValue || expr is ArrayLiteralValue || expr is RangeExpr
|
|
||||||
|
|
||||||
private fun isSimpleTarget(target: AssignTarget, namespace: INameScope) =
|
|
||||||
if (target.identifier!=null || target.memoryAddress!=null)
|
if (target.identifier!=null || target.memoryAddress!=null)
|
||||||
target.isInRegularRAM(namespace)
|
target.isInRegularRAMof(compTarget.machine)
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
|
@ -9,28 +9,32 @@ import kotlin.math.pow
|
|||||||
class ConstExprEvaluator {
|
class ConstExprEvaluator {
|
||||||
|
|
||||||
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
|
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
|
||||||
return when(operator) {
|
try {
|
||||||
"+" -> plus(left, right)
|
return when(operator) {
|
||||||
"-" -> minus(left, right)
|
"+" -> plus(left, right)
|
||||||
"*" -> multiply(left, right)
|
"-" -> minus(left, right)
|
||||||
"/" -> divide(left, right)
|
"*" -> multiply(left, right)
|
||||||
"%" -> remainder(left, right)
|
"/" -> divide(left, right)
|
||||||
"**" -> power(left, right)
|
"%" -> remainder(left, right)
|
||||||
"&" -> bitwiseand(left, right)
|
"**" -> power(left, right)
|
||||||
"|" -> bitwiseor(left, right)
|
"&" -> bitwiseand(left, right)
|
||||||
"^" -> bitwisexor(left, right)
|
"|" -> bitwiseor(left, right)
|
||||||
"and" -> logicaland(left, right)
|
"^" -> bitwisexor(left, right)
|
||||||
"or" -> logicalor(left, right)
|
"and" -> logicaland(left, right)
|
||||||
"xor" -> logicalxor(left, right)
|
"or" -> logicalor(left, right)
|
||||||
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
|
"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)
|
||||||
"==" -> 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)
|
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
|
||||||
">>" -> shiftedright(left, right)
|
"<<" -> shiftedleft(left, right)
|
||||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
">>" -> shiftedright(left, right)
|
||||||
|
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||||
|
}
|
||||||
|
} catch (ax: FatalAstException) {
|
||||||
|
throw ExpressionError(ax.message, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,14 +4,15 @@ import prog8.ast.Node
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.ast.walk.AstWalker
|
||||||
|
import prog8.ast.walk.IAstModification
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
// @( &thing ) --> thing
|
// @( &thing ) --> thing
|
||||||
@ -104,7 +105,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
// optimize various simple cases of ** :
|
// optimize various simple cases of ** :
|
||||||
// optimize away 1 ** x into just 1 and 0 ** x into just 0
|
// optimize away 1 ** x into just 1 and 0 ** x into just 0
|
||||||
// optimize 2 ** x into (1<<x) if both operands are integer.
|
// optimize 2 ** x into (1<<x) if both operands are integer.
|
||||||
val leftDt = leftconst.inferType(program).typeOrElse(DataType.STRUCT)
|
val leftDt = leftconst.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
when (leftconst.number.toDouble()) {
|
when (leftconst.number.toDouble()) {
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
val value = NumericLiteralValue(leftDt, 0, expr.position)
|
val value = NumericLiteralValue(leftDt, 0, expr.position)
|
||||||
@ -119,11 +120,11 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
|
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
|
||||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||||
} else {
|
} else {
|
||||||
val rightDt = expr.right.inferType(program).typeOrElse(DataType.STRUCT)
|
val rightDt = expr.right.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val targetDt =
|
val targetDt =
|
||||||
when (parent) {
|
when (parent) {
|
||||||
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.STRUCT)
|
is Assignment -> parent.target.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
is VarDecl -> parent.datatype
|
is VarDecl -> parent.datatype
|
||||||
else -> leftDt
|
else -> leftDt
|
||||||
}
|
}
|
||||||
@ -136,7 +137,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(expr.inferType(program).istype(DataType.FLOAT)) {
|
if(expr.inferType(program) istype DataType.FLOAT) {
|
||||||
val subExpr: BinaryExpression? = when {
|
val subExpr: BinaryExpression? = when {
|
||||||
leftconst != null -> expr.right as? BinaryExpression
|
leftconst != null -> expr.right as? BinaryExpression
|
||||||
rightconst != null -> expr.left as? BinaryExpression
|
rightconst != null -> expr.left as? BinaryExpression
|
||||||
@ -185,7 +186,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
} else {
|
} else {
|
||||||
val arrayDt = array.guessDatatype(program)
|
val arrayDt = array.guessDatatype(program)
|
||||||
if (arrayDt.isKnown) {
|
if (arrayDt.isKnown) {
|
||||||
val newArray = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
val newArray = array.cast(arrayDt.getOr(DataType.UNDEFINED))
|
||||||
if (newArray != null && newArray != array)
|
if (newArray != null && newArray != array)
|
||||||
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||||
}
|
}
|
||||||
@ -230,7 +231,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
val rangeTo = iterableRange.to as? NumericLiteralValue
|
val rangeTo = iterableRange.to as? NumericLiteralValue
|
||||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||||
|
|
||||||
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace) ?: throw UndefinedSymbolError(forLoop.loopVar)
|
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar)
|
||||||
|
|
||||||
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
||||||
when(loopvar.datatype) {
|
when(loopvar.datatype) {
|
||||||
@ -276,7 +277,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
val numval = decl.value as? NumericLiteralValue
|
val numval = decl.value as? NumericLiteralValue
|
||||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||||
val valueDt = numval.inferType(program)
|
val valueDt = numval.inferType(program)
|
||||||
if(!valueDt.istype(decl.datatype)) {
|
if(valueDt isnot decl.datatype) {
|
||||||
val cast = numval.cast(decl.datatype)
|
val cast = numval.cast(decl.datatype)
|
||||||
if(cast.isValid)
|
if(cast.isValid)
|
||||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
@ -4,23 +4,27 @@ import prog8.ast.Node
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.statements.ArrayIndex
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.compilerinterface.size
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compilerinterface.toConstantIntegerRange
|
||||||
|
|
||||||
// Fix up the literal value's type to match that of the vardecl
|
// Fix up the literal value's type to match that of the vardecl
|
||||||
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
// (also check range literal operands types before they get expanded into arrays for instance)
|
||||||
private val noModifications = emptyList<IAstModification>()
|
class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
if(decl.parent is AnonymousScope)
|
||||||
|
throw FatalAstException("vardecl may no longer occur in anonymousscope")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val declConstValue = decl.value?.constValue(program)
|
val declConstValue = decl.value?.constValue(program)
|
||||||
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||||
&& !declConstValue.inferType(program).istype(decl.datatype)) {
|
&& declConstValue.inferType(program) isnot decl.datatype) {
|
||||||
// cast the numeric literal to the appropriate datatype of the variable
|
// cast the numeric literal to the appropriate datatype of the variable
|
||||||
val cast = declConstValue.cast(decl.datatype)
|
val cast = declConstValue.cast(decl.datatype)
|
||||||
if(cast.isValid)
|
if(cast.isValid)
|
||||||
@ -32,14 +36,42 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
|
|||||||
|
|
||||||
return noModifications
|
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,
|
// Replace all constant identifiers with their actual value,
|
||||||
// and the array var initializer values and sizes.
|
// and the array var initializer values and sizes.
|
||||||
// This is needed because further constant optimizations depend on those.
|
// This is needed because further constant optimizations depend on those.
|
||||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
// replace identifiers that refer to const value, with the value itself
|
// replace identifiers that refer to const value, with the value itself
|
||||||
@ -74,7 +106,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO: use call graph for this?
|
// TODO: use call graph for this?
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true) {
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true) {
|
||||||
errors.err("recursive var declaration", decl.position)
|
errors.err("recursive var declaration", decl.position)
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -92,19 +124,6 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
decl
|
decl
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else if(arraysize.constIndex()==null) {
|
|
||||||
// see if we can calculate the size from other fields
|
|
||||||
try {
|
|
||||||
val cval = arraysize.indexVar?.constValue(program) ?: arraysize.origExpression?.constValue(program)
|
|
||||||
if (cval != null) {
|
|
||||||
arraysize.indexVar = null
|
|
||||||
arraysize.origExpression = null
|
|
||||||
arraysize.indexNum = cval
|
|
||||||
}
|
|
||||||
} catch (x: UndefinedSymbolError) {
|
|
||||||
errors.err(x.message, x.position)
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +137,6 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
val numericLv = decl.value as? NumericLiteralValue
|
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array
|
// convert the initializer range expression to an actual array
|
||||||
@ -127,7 +145,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||||
val newValue = if(eltType in ByteDatatypes) {
|
val newValue = if(eltType in ByteDatatypes) {
|
||||||
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
||||||
@ -140,6 +158,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val numericLv = decl.value as? NumericLiteralValue
|
||||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||||
errors.err("arraysize requires only integers here", numericLv.position)
|
errors.err("arraysize requires only integers here", numericLv.position)
|
||||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||||
@ -166,20 +185,18 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
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)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
|
||||||
val litval = decl.value as? NumericLiteralValue
|
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array of floats
|
// convert the initializer range expression to an actual array of floats
|
||||||
val declArraySize = decl.arraysize?.constIndex()
|
val declArraySize = decl.arraysize?.constIndex()
|
||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||||
@ -188,22 +205,24 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(rangeExpr==null && litval!=null) {
|
|
||||||
|
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.
|
// arraysize initializer is a single int, and we know the size.
|
||||||
val fillvalue = litval.number.toDouble()
|
val fillvalue = numericLv.number.toDouble()
|
||||||
if (fillvalue < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE)
|
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||||
errors.err("float value overflow", litval.position)
|
errors.err("float value overflow", numericLv.position)
|
||||||
else {
|
else {
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) }.toTypedArray<Expression>()
|
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 = litval.position)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// nothing to do for this type
|
// nothing to do for this type
|
||||||
// this includes strings and structs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,27 +2,40 @@ package prog8.optimizer
|
|||||||
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
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.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
|
||||||
import prog8.ast.processing.IAstModification
|
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.walk.AstWalker
|
||||||
|
import prog8.ast.walk.IAstModification
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
/*
|
/*
|
||||||
todo add more expression optimizations
|
todo add more peephole expression optimizations
|
||||||
|
|
||||||
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
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))
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val mods = mutableListOf<IAstModification>()
|
val mods = mutableListOf<IAstModification>()
|
||||||
@ -43,7 +56,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (typecast.expression.inferType(program).istype(typecast.type)) {
|
if (typecast.expression.inferType(program) istype typecast.type) {
|
||||||
// remove duplicate cast
|
// remove duplicate cast
|
||||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||||
}
|
}
|
||||||
@ -132,8 +145,8 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
|
||||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
|
||||||
|
|
||||||
if (expr.operator == "+" || expr.operator == "-"
|
if (expr.operator == "+" || expr.operator == "-"
|
||||||
&& leftVal == null && rightVal == null
|
&& leftVal == null && rightVal == null
|
||||||
@ -287,13 +300,13 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val arg = functionCall.args[0]
|
val arg = functionCall.args[0]
|
||||||
if(arg is TypecastExpression) {
|
if(arg is TypecastExpression) {
|
||||||
val valueDt = arg.expression.inferType(program)
|
val valueDt = arg.expression.inferType(program)
|
||||||
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
|
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
|
||||||
// useless lsb() of byte value that was casted to word
|
// useless lsb() of byte value that was typecasted to word
|
||||||
return listOf(IAstModification.ReplaceNode(functionCall, arg.expression, parent))
|
return listOf(IAstModification.ReplaceNode(functionCall, arg.expression, parent))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val argDt = arg.inferType(program)
|
val argDt = arg.inferType(program)
|
||||||
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
|
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||||
// useless lsb() of byte value
|
// useless lsb() of byte value
|
||||||
return listOf(IAstModification.ReplaceNode(functionCall, arg, parent))
|
return listOf(IAstModification.ReplaceNode(functionCall, arg, parent))
|
||||||
}
|
}
|
||||||
@ -303,20 +316,20 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val arg = functionCall.args[0]
|
val arg = functionCall.args[0]
|
||||||
if(arg is TypecastExpression) {
|
if(arg is TypecastExpression) {
|
||||||
val valueDt = arg.expression.inferType(program)
|
val valueDt = arg.expression.inferType(program)
|
||||||
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
|
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
|
||||||
// useless msb() of byte value that was casted to word, replace with 0
|
// useless msb() of byte value that was typecasted to word, replace with 0
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCall,
|
functionCall,
|
||||||
NumericLiteralValue(valueDt.typeOrElse(DataType.UBYTE), 0, arg.expression.position),
|
NumericLiteralValue(valueDt.getOr(DataType.UBYTE), 0, arg.expression.position),
|
||||||
parent))
|
parent))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val argDt = arg.inferType(program)
|
val argDt = arg.inferType(program)
|
||||||
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
|
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
|
||||||
// useless msb() of byte value, replace with 0
|
// useless msb() of byte value, replace with 0
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCall,
|
functionCall,
|
||||||
NumericLiteralValue(argDt.typeOrElse(DataType.UBYTE), 0, arg.position),
|
NumericLiteralValue(argDt.getOr(DataType.UBYTE), 0, arg.position),
|
||||||
parent))
|
parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -487,10 +500,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val idt = expr.inferType(program)
|
val idt = expr.inferType(program)
|
||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
throw FatalAstException("unknown dt")
|
throw FatalAstException("unknown dt")
|
||||||
return NumericLiteralValue(idt.typeOrElse(DataType.STRUCT), 0, expr.position)
|
return NumericLiteralValue(idt.getOr(DataType.UNDEFINED), 0, expr.position)
|
||||||
} else if (cv == 2.0) {
|
} else if (cv in powersOfTwo) {
|
||||||
expr.operator = "&"
|
expr.operator = "&"
|
||||||
expr.right = NumericLiteralValue.optimalInteger(1, expr.position)
|
expr.right = NumericLiteralValue.optimalInteger(cv!!.toInt()-1, expr.position)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,7 +524,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val leftIDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
if (!leftIDt.isKnown)
|
if (!leftIDt.isKnown)
|
||||||
return null
|
return null
|
||||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
|
||||||
when (cv) {
|
when (cv) {
|
||||||
-1.0 -> {
|
-1.0 -> {
|
||||||
// '/' -> -left
|
// '/' -> -left
|
||||||
@ -588,14 +601,14 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
return expr2.left
|
return expr2.left
|
||||||
}
|
}
|
||||||
in powersOfTwo -> {
|
in powersOfTwo -> {
|
||||||
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
if (leftValue.inferType(program).isInteger) {
|
||||||
// times a power of two => shift left
|
// times a power of two => shift left
|
||||||
val numshifts = log2(cv).toInt()
|
val numshifts = log2(cv).toInt()
|
||||||
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in negativePowersOfTwo -> {
|
in negativePowersOfTwo -> {
|
||||||
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
if (leftValue.inferType(program).isInteger) {
|
||||||
// times a negative power of two => negate, then shift left
|
// times a negative power of two => negate, then shift left
|
||||||
val numshifts = log2(-cv).toInt()
|
val numshifts = log2(-cv).toInt()
|
||||||
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
@ -619,7 +632,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val targetIDt = expr.left.inferType(program)
|
val targetIDt = expr.left.inferType(program)
|
||||||
if(!targetIDt.isKnown)
|
if(!targetIDt.isKnown)
|
||||||
throw FatalAstException("unknown dt")
|
throw FatalAstException("unknown dt")
|
||||||
when (val targetDt = targetIDt.typeOrElse(DataType.STRUCT)) {
|
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
return NumericLiteralValue(targetDt, 0, expr.position)
|
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||||
@ -654,7 +667,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val idt = expr.left.inferType(program)
|
val idt = expr.left.inferType(program)
|
||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
throw FatalAstException("unknown dt")
|
throw FatalAstException("unknown dt")
|
||||||
when (idt.typeOrElse(DataType.STRUCT)) {
|
when (idt.getOr(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
@ -1,31 +1,34 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ErrorReporter
|
import prog8.compilerinterface.CompilationOptions
|
||||||
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: ErrorReporter) {
|
fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||||
valuetypefixer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
if(errors.isEmpty()) {
|
if(errors.noErrors()) {
|
||||||
valuetypefixer.applyModifications()
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
val replacer = ConstantIdentifierReplacer(this, errors)
|
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
|
||||||
replacer.visit(this)
|
replacer.visit(this)
|
||||||
if (errors.isEmpty()) {
|
if (errors.noErrors()) {
|
||||||
replacer.applyModifications()
|
replacer.applyModifications()
|
||||||
|
|
||||||
valuetypefixer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
if(errors.isEmpty()) {
|
if(errors.noErrors()) {
|
||||||
valuetypefixer.applyModifications()
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
val optimizer = ConstantFoldingOptimizer(this)
|
val optimizer = ConstantFoldingOptimizer(this)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
while (errors.noErrors() && optimizer.applyModifications() > 0) {
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.isEmpty()) {
|
if (errors.noErrors()) {
|
||||||
replacer.visit(this)
|
replacer.visit(this)
|
||||||
replacer.applyModifications()
|
replacer.applyModifications()
|
||||||
}
|
}
|
||||||
@ -33,13 +36,16 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errors.isEmpty())
|
if(errors.noErrors())
|
||||||
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.optimizeStatements(errors: ErrorReporter): Int {
|
fun Program.optimizeStatements(errors: IErrorReporter,
|
||||||
val optimizer = StatementOptimizer(this, errors)
|
functions: IBuiltinFunctions,
|
||||||
|
compTarget: ICompilationTarget
|
||||||
|
): Int {
|
||||||
|
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
val optimizationCount = optimizer.applyModifications()
|
val optimizationCount = optimizer.applyModifications()
|
||||||
|
|
||||||
@ -48,14 +54,14 @@ internal fun Program.optimizeStatements(errors: ErrorReporter): Int {
|
|||||||
return optimizationCount
|
return optimizationCount
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.simplifyExpressions() : Int {
|
fun Program.simplifyExpressions() : Int {
|
||||||
val opti = ExpressionSimplifier(this)
|
val opti = ExpressionSimplifier(this)
|
||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.splitBinaryExpressions() : Int {
|
fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICompilationTarget) : Int {
|
||||||
val opti = BinExprSplitter(this)
|
val opti = BinExprSplitter(this, options, compTarget)
|
||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
@ -1,159 +1,163 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.IBuiltinFunctions
|
||||||
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
|
||||||
import prog8.ast.processing.IAstModification
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.functions.BuiltinFunctions
|
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
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
internal const val retvarName = "prog8_retval"
|
||||||
|
|
||||||
internal class StatementOptimizer(private val program: Program,
|
|
||||||
private val errors: ErrorReporter) : AstWalker() {
|
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
class StatementOptimizer(private val program: Program,
|
||||||
private val callgraph = CallGraph(program)
|
private val errors: IErrorReporter,
|
||||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
private val functions: IBuiltinFunctions,
|
||||||
|
private val compTarget: ICompilationTarget
|
||||||
|
) : AstWalker() {
|
||||||
|
|
||||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
private val subsThatNeedReturnVariable = mutableSetOf<Triple<IStatementContainer, DataType, Position>>()
|
||||||
if("force_output" !in block.options()) {
|
|
||||||
if (block.containsNoCodeNorVars()) {
|
|
||||||
errors.warn("removing empty block '${block.name}'", block.position)
|
|
||||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block !in callgraph.usedSymbols) {
|
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
|
||||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
for(returnvar in subsThatNeedReturnVariable) {
|
||||||
if(subroutine.asmAddress==null && !forceOutput) {
|
val decl = VarDecl(VarDeclType.VAR, returnvar.second, ZeropageWish.DONTCARE, null, retvarName, null,
|
||||||
if(subroutine.containsNoCodeNorVars() && !subroutine.inline) {
|
isArray = false,
|
||||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
autogeneratedDontRemove = true,
|
||||||
val removals = callgraph.calledBy.getValue(subroutine).map {
|
sharedWithAsm = false,
|
||||||
IAstModification.Remove(it, it.definingScope())
|
position = returnvar.third
|
||||||
}.toMutableList()
|
)
|
||||||
removals += IAstModification.Remove(subroutine, subroutine.definingScope())
|
returnvar.first.statements.add(0, decl)
|
||||||
return removals
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
subsThatNeedReturnVariable.clear()
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
|
||||||
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
// if the first instruction in the called subroutine is a return statement with a simple value,
|
||||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
// remove the jump altogeter and inline the returnvalue directly.
|
||||||
if(decl.type == VarDeclType.VAR)
|
val subroutine = functionCall.target.targetSubroutine(program)
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
if(subroutine!=null) {
|
||||||
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
return listOf(IAstModification.Remove(decl, decl.definingScope()))
|
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
|
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> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in functions.names) {
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
if (functionName in pureBuiltinFunctions) {
|
if (functionName in functions.purefunctionNames) {
|
||||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||||
return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope()))
|
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
|
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||||
// this is a C-64 specific optimization
|
// only do this optimization if the arg is a known-constant string literal instead of a user defined variable.
|
||||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print")) {
|
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||||
val arg = functionCallStatement.args.single()
|
val arg = functionCallStatement.args.single()
|
||||||
val stringVar: IdentifierReference?
|
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||||
stringVar = if(arg is AddressOf) {
|
|
||||||
arg.identifier
|
arg.identifier
|
||||||
} else {
|
} else {
|
||||||
arg as? IdentifierReference
|
arg as? IdentifierReference
|
||||||
}
|
}
|
||||||
if(stringVar!=null) {
|
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
|
||||||
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
val string = stringVar.targetVarDecl(program)?.value as? StringLiteralValue
|
||||||
val string = vardecl.value as? StringLiteralValue
|
|
||||||
if(string!=null) {
|
if(string!=null) {
|
||||||
val pos = functionCallStatement.position
|
val pos = functionCallStatement.position
|
||||||
if (string.value.length == 1) {
|
if (string.value.length == 1) {
|
||||||
val firstCharEncoded = CompilationTarget.instance.encodeString(string.value, string.altEncoding)[0]
|
val firstCharEncoded = compTarget.encodeString(string.value, string.altEncoding)[0]
|
||||||
val chrout = FunctionCallStatement(
|
val chrout = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||||
functionCallStatement.void, pos
|
functionCallStatement.void, pos
|
||||||
)
|
)
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||||
} else if (string.value.length == 2) {
|
} else if (string.value.length == 2) {
|
||||||
val firstTwoCharsEncoded = CompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding)
|
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||||
val chrout1 = FunctionCallStatement(
|
val chrout1 = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||||
functionCallStatement.void, pos
|
functionCallStatement.void, pos
|
||||||
)
|
)
|
||||||
val chrout2 = FunctionCallStatement(
|
val chrout2 = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||||
functionCallStatement.void, pos
|
functionCallStatement.void, pos
|
||||||
|
)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as IStatementContainer),
|
||||||
|
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
||||||
)
|
)
|
||||||
val anonscope = AnonymousScope(mutableListOf(), pos)
|
|
||||||
anonscope.statements.add(chrout1)
|
|
||||||
anonscope.statements.add(chrout2)
|
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
||||||
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
val subroutine = functionCallStatement.target.targetSubroutine(program)
|
||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Return)
|
if(first is Return)
|
||||||
return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope()))
|
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
// 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
|
// // 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.namespace)
|
// val subroutine = functionCall.target.targetSubroutine(program)
|
||||||
if(subroutine!=null) {
|
// if(subroutine!=null) {
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
// val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Return && first.value!=null) {
|
// if(first is Return && first.value!=null) {
|
||||||
val constval = first.value?.constValue(program)
|
// val constval = first.value?.constValue(program)
|
||||||
if(constval!=null)
|
// if(constval!=null)
|
||||||
return listOf(IAstModification.ReplaceNode(functionCall, constval, parent))
|
// return listOf(IAstModification.ReplaceNode(functionCall, constval, parent))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return noModifications
|
// return noModifications
|
||||||
}
|
// }
|
||||||
|
|
||||||
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||||
// remove empty if statements
|
// remove empty if statements
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars())
|
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isEmpty())
|
||||||
return listOf(IAstModification.Remove(ifStatement, ifStatement.definingScope()))
|
return listOf(IAstModification.Remove(ifStatement, parent as IStatementContainer))
|
||||||
|
|
||||||
// empty true part? switch with the else part
|
// empty true part? switch with the else part
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isNotEmpty()) {
|
||||||
val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
||||||
val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
||||||
val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position)
|
val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position)
|
||||||
@ -181,20 +185,20 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
if(forLoop.body.containsNoCodeNorVars()) {
|
if(forLoop.body.isEmpty()) {
|
||||||
errors.warn("removing empty for loop", forLoop.position)
|
errors.warn("removing empty for loop", forLoop.position)
|
||||||
return listOf(IAstModification.Remove(forLoop, forLoop.definingScope()))
|
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||||
} else if(forLoop.body.statements.size==1) {
|
} else if(forLoop.body.statements.size==1) {
|
||||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||||
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
||||||
// remove empty for loop (only loopvar decl in it)
|
// remove empty for loop (only loopvar decl in it)
|
||||||
return listOf(IAstModification.Remove(forLoop, forLoop.definingScope()))
|
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val range = forLoop.iterable as? RangeExpr
|
val range = forLoop.iterable as? RangeExpr
|
||||||
if(range!=null) {
|
if(range!=null) {
|
||||||
if(range.size()==1) {
|
if (range.size() == 1) {
|
||||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||||
// loopvar/reg = range value , follow by block
|
// loopvar/reg = range value , follow by block
|
||||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
@ -203,14 +207,14 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program.namespace)
|
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
|
||||||
if(iterable!=null) {
|
if(iterable!=null) {
|
||||||
if(iterable.datatype==DataType.STR) {
|
if(iterable.datatype==DataType.STR) {
|
||||||
val sv = iterable.value as StringLiteralValue
|
val sv = iterable.value as StringLiteralValue
|
||||||
val size = sv.value.length
|
val size = sv.value.length
|
||||||
if(size==1) {
|
if(size==1) {
|
||||||
// loop over string of length 1 -> just assign the single character
|
// loop over string of length 1 -> just assign the single character
|
||||||
val character = CompilationTarget.instance.encodeString(sv.value, sv.altEncoding)[0]
|
val character = compTarget.encodeString(sv.value, sv.altEncoding)[0]
|
||||||
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
||||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
||||||
@ -265,7 +269,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
// always false -> remove the while statement altogether
|
// always false -> remove the while statement altogether
|
||||||
errors.warn("condition is always false", whileLoop.condition.position)
|
errors.warn("condition is always false", whileLoop.condition.position)
|
||||||
listOf(IAstModification.Remove(whileLoop, whileLoop.definingScope()))
|
listOf(IAstModification.Remove(whileLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -274,14 +278,14 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
||||||
val iter = repeatLoop.iterations
|
val iter = repeatLoop.iterations
|
||||||
if(iter!=null) {
|
if(iter!=null) {
|
||||||
if(repeatLoop.body.containsNoCodeNorVars()) {
|
if(repeatLoop.body.isEmpty()) {
|
||||||
errors.warn("empty loop removed", repeatLoop.position)
|
errors.warn("empty loop removed", repeatLoop.position)
|
||||||
return listOf(IAstModification.Remove(repeatLoop, repeatLoop.definingScope()))
|
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
val iterations = iter.constValue(program)?.number?.toInt()
|
val iterations = iter.constValue(program)?.number?.toInt()
|
||||||
if (iterations == 0) {
|
if (iterations == 0) {
|
||||||
errors.warn("iterations is always 0, removed loop", iter.position)
|
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||||
return listOf(IAstModification.Remove(repeatLoop, repeatLoop.definingScope()))
|
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
if (iterations == 1) {
|
if (iterations == 1) {
|
||||||
errors.warn("iterations is always 1", iter.position)
|
errors.warn("iterations is always 1", iter.position)
|
||||||
@ -291,24 +295,12 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
|
||||||
// remove empty choices
|
|
||||||
class ChoiceRemover(val choice: WhenChoice) : IAstModification {
|
|
||||||
override fun perform() {
|
|
||||||
whenStatement.choices.remove(choice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return whenStatement.choices
|
|
||||||
.filter { !it.statements.containsCodeOrVars() }
|
|
||||||
.map { ChoiceRemover(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
// if the jump is to the next statement, remove the jump
|
// if the jump is to the next statement, remove the jump
|
||||||
val scope = jump.definingScope()
|
val scope = jump.parent as IStatementContainer
|
||||||
val label = jump.identifier?.targetStatement(scope)
|
val label = jump.identifier?.targetStatement(program)
|
||||||
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
||||||
return listOf(IAstModification.Remove(jump, jump.definingScope()))
|
return listOf(IAstModification.Remove(jump, scope))
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -341,7 +333,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
)
|
)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
IAstModification.InsertAfter(assignment, addConstant, assignment.definingScope()))
|
IAstModification.InsertAfter(assignment, addConstant, parent as IStatementContainer))
|
||||||
} else if (op2 == "-") {
|
} else if (op2 == "-") {
|
||||||
// A = A +/- B - N
|
// A = A +/- B - N
|
||||||
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||||
@ -352,7 +344,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
)
|
)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
IAstModification.InsertAfter(assignment, subConstant, assignment.definingScope()))
|
IAstModification.InsertAfter(assignment, subConstant, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,7 +366,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
if(assignment.target isSameAs assignment.value) {
|
if(assignment.target isSameAs assignment.value) {
|
||||||
// remove assignment to self
|
// remove assignment to self
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
val targetIDt = assignment.target.inferType(program)
|
val targetIDt = assignment.target.inferType(program)
|
||||||
@ -382,7 +374,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
throw FatalAstException("can't infer type of assignment target")
|
throw FatalAstException("can't infer type of assignment target")
|
||||||
|
|
||||||
// optimize binary expressions a bit
|
// optimize binary expressions a bit
|
||||||
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
val targetDt = targetIDt.getOr(DataType.UNDEFINED)
|
||||||
val bexpr=assignment.value as? BinaryExpression
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
if(bexpr!=null) {
|
if(bexpr!=null) {
|
||||||
val rightCv = bexpr.right.constValue(program)?.number?.toDouble()
|
val rightCv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||||
@ -390,17 +382,17 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
// assignments of the form: X = X <operator> <expr>
|
// assignments of the form: X = X <operator> <expr>
|
||||||
// remove assignments that have no effect (such as X=X+0)
|
// remove assignments that have no effect (such as X=X+0)
|
||||||
// optimize/rewrite some other expressions
|
// optimize/rewrite some other expressions
|
||||||
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type
|
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program))?.type
|
||||||
when (bexpr.operator) {
|
when (bexpr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
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)
|
// 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)
|
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
||||||
}
|
}
|
||||||
return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
|
return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
|
||||||
}
|
}
|
||||||
@ -408,30 +400,30 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
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)
|
// 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)
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
decs.statements.add(PostIncrDecr(assignment.target.copy(), "--", assignment.position))
|
||||||
}
|
}
|
||||||
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
if (rightCv == 0.0)
|
if (rightCv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if (rightCv == 0.0)
|
if (rightCv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,23 +434,19 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
fun returnViaIntermediary(value: Expression): Iterable<IAstModification>? {
|
fun returnViaIntermediaryVar(value: Expression): Iterable<IAstModification>? {
|
||||||
val returnDt = returnStmt.definingSubroutine()!!.returntypes.single()
|
val subr = returnStmt.definingSubroutine!!
|
||||||
|
val returnDt = subr.returntypes.single()
|
||||||
if (returnDt in IntegerDatatypes) {
|
if (returnDt in IntegerDatatypes) {
|
||||||
// first assign to intermediary, then return that register
|
// first assign to intermediary variable, then return that
|
||||||
val returnValueIntermediary =
|
subsThatNeedReturnVariable.add(Triple(subr, returnDt, returnStmt.position))
|
||||||
when(returnDt) {
|
val returnValueIntermediary1 = IdentifierReference(listOf(retvarName), returnStmt.position)
|
||||||
DataType.UBYTE -> IdentifierReference(listOf("prog8_lib", "retval_interm_ub"), returnStmt.position)
|
val returnValueIntermediary2 = IdentifierReference(listOf(retvarName), returnStmt.position)
|
||||||
DataType.BYTE -> IdentifierReference(listOf("prog8_lib", "retval_interm_b"), returnStmt.position)
|
val tgt = AssignTarget(returnValueIntermediary1, null, null, returnStmt.position)
|
||||||
DataType.UWORD -> IdentifierReference(listOf("prog8_lib", "retval_interm_uw"), returnStmt.position)
|
|
||||||
DataType.WORD -> IdentifierReference(listOf("prog8_lib", "retval_interm_w"), returnStmt.position)
|
|
||||||
else -> throw FatalAstException("weird return dt")
|
|
||||||
}
|
|
||||||
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
|
||||||
val assign = Assignment(tgt, value, returnStmt.position)
|
val assign = Assignment(tgt, value, returnStmt.position)
|
||||||
val returnReplacement = Return(returnValueIntermediary, returnStmt.position)
|
val returnReplacement = Return(returnValueIntermediary2, returnStmt.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(returnStmt, assign, parent as INameScope),
|
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -467,12 +455,12 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
when(returnStmt.value) {
|
when(returnStmt.value) {
|
||||||
is PrefixExpression -> {
|
is PrefixExpression -> {
|
||||||
val mod = returnViaIntermediary(returnStmt.value!!)
|
val mod = returnViaIntermediaryVar(returnStmt.value!!)
|
||||||
if(mod!=null)
|
if(mod!=null)
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
val mod = returnViaIntermediary(returnStmt.value!!)
|
val mod = returnViaIntermediaryVar(returnStmt.value!!)
|
||||||
if(mod!=null)
|
if(mod!=null)
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
@ -482,7 +470,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return super.after(returnStmt, parent)
|
return super.after(returnStmt, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasBreak(scope: INameScope): Boolean {
|
private fun hasBreak(scope: IStatementContainer): Boolean {
|
||||||
|
|
||||||
class Searcher: IAstVisitor
|
class Searcher: IAstVisitor
|
||||||
{
|
{
|
144
codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt
Normal file
144
codeOptimizers/src/prog8/optimizer/UnusedCodeRemover.kt
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
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) {
|
||||||
|
if (callgraph.unused(decl)) {
|
||||||
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
|
return 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
|
||||||
|
}
|
||||||
|
}
|
2
codeOptimizers/test/readme.txt
Normal file
2
codeOptimizers/test/readme.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.
|
@ -1,51 +1,47 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id "org.jetbrains.kotlin.jvm" version "1.4.21"
|
id "org.jetbrains.kotlin.jvm"
|
||||||
id 'org.jetbrains.dokka' version "0.9.18"
|
id 'com.github.johnrengelman.shadow' version '7.1.0'
|
||||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
targetCompatibility = 11
|
java {
|
||||||
sourceCompatibility = 11
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
repositories {
|
}
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 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-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation 'org.antlr:antlr4-runtime:4.8'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.3'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.1'
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
|
||||||
// implementation 'net.razorvine:ksim65:1.8'
|
|
||||||
// implementation "com.github.hypfvieh:dbus-java:3.2.4"
|
|
||||||
implementation project(':parser')
|
|
||||||
|
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
|
||||||
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
|
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
configurations.all {
|
||||||
kotlinOptions {
|
exclude group: 'com.ibm.icu', module: 'icu4j'
|
||||||
jvmTarget = "11"
|
exclude group: "org.antlr", module: "antlr4"
|
||||||
// verbose = true
|
}
|
||||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
|
||||||
|
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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
@ -58,7 +54,8 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
test {
|
test {
|
||||||
java {
|
java {
|
||||||
srcDirs = ["${project.projectDir}/test"]
|
srcDir "${project.projectDir}/test"
|
||||||
|
srcDir "${project(':compilerAst').projectDir}/test/helpers"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,15 +64,9 @@ startScripts.enabled = true
|
|||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass = 'prog8.CompilerMainKt'
|
mainClass = 'prog8.CompilerMainKt'
|
||||||
mainClassName = 'prog8.CompilerMainKt' // deprecated
|
|
||||||
applicationName = 'p8compile'
|
applicationName = 'p8compile'
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
|
||||||
archives shadowJar
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archiveBaseName = 'prog8compiler'
|
archiveBaseName = 'prog8compiler'
|
||||||
archiveVersion = prog8version
|
archiveVersion = prog8version
|
||||||
@ -96,12 +87,4 @@ test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
build.finalizedBy installDist, installShadowDist
|
||||||
dokka {
|
|
||||||
outputFormat = 'html'
|
|
||||||
outputDirectory = "$buildDir/kdoc"
|
|
||||||
}
|
|
||||||
|
|
||||||
task wrapper(type: Wrapper) {
|
|
||||||
gradleVersion = '6.7'
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module type="JAVA_MODULE" version="4">
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="Python" name="Python">
|
||||||
|
<configuration sdkName="Python 3.9" />
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
@ -8,12 +13,17 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="11" jdkType="JavaSDK" />
|
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="module" module-name="parser" />
|
<orderEntry type="module" module-name="compilerAst" />
|
||||||
<orderEntry type="library" name="unittest-libs" level="project" />
|
<orderEntry type="library" name="Python 3.9 interpreter library" level="application" />
|
||||||
<orderEntry type="library" name="kotlinx-cli-jvm" level="project" />
|
<orderEntry type="library" name="hamcrest" level="project" />
|
||||||
<orderEntry type="library" name="antlr-runtime-4.9" level="project" />
|
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||||
|
<orderEntry type="library" name="junit.jupiter" 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" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</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.
Binary file not shown.
@ -428,7 +428,9 @@ var_fac1_greater_f .proc
|
|||||||
cmp #1
|
cmp #1
|
||||||
beq +
|
beq +
|
||||||
lda #0
|
lda #0
|
||||||
+ rts
|
rts
|
||||||
|
+ lda #1
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
var_fac1_greatereq_f .proc
|
var_fac1_greatereq_f .proc
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%target c64
|
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
floats {
|
floats {
|
||||||
@ -13,6 +12,7 @@ floats {
|
|||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
|
|
||||||
|
ubyte[5] tempvar_swap_float ; used for some swap() operations
|
||||||
|
|
||||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ romsub $bc58 = ABS() ; fac1 = ABS(fac1)
|
|||||||
romsub $bf71 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $bf71 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
romsub $bf74 = SQRA() clobbers(A,X,Y) ; fac1 = SQRT(fac2)
|
romsub $bf74 = SQRA() clobbers(A,X,Y) ; fac1 = SQRT(fac2)
|
||||||
romsub $bfed = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
romsub $bfed = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||||
romsub $bfb4 = NEGOP() clobbers(A) ; switch the sign of 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 $e097 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||||
romsub $e264 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
romsub $e264 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||||
romsub $e26b = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
romsub $e26b = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||||
@ -195,7 +195,7 @@ sub print_f (float value) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
%asminclude "library:c64/floats.asm", ""
|
%asminclude "library:c64/floats.asm"
|
||||||
%asminclude "library:c64/floats_funcs.asm", ""
|
%asminclude "library:c64/floats_funcs.asm"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
%target c64
|
|
||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
; bitmap pixel graphics module for the C64
|
; bitmap pixel graphics module for the C64
|
||||||
; only black/white monchrome 320x200 for now
|
; only black/white monochrome 320x200 for now
|
||||||
; assumes bitmap screen memory is $2000-$3fff
|
; assumes bitmap screen memory is $2000-$3fff
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
@ -39,30 +38,28 @@ graphics {
|
|||||||
swap(x1, x2)
|
swap(x1, x2)
|
||||||
swap(y1, y2)
|
swap(y1, y2)
|
||||||
}
|
}
|
||||||
word @zp dx = x2-x1 as word
|
word @zp dx = (x2 as word)-x1
|
||||||
word @zp dy = y2-y1
|
word @zp dy = (y2 as word)-y1
|
||||||
|
|
||||||
if dx==0 {
|
if dx==0 {
|
||||||
vertical_line(x1, y1, abs(dy)+1 as ubyte)
|
vertical_line(x1, y1, abs(dy) as ubyte +1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dy==0 {
|
if dy==0 {
|
||||||
if x1>x2
|
if x1>x2
|
||||||
x1=x2
|
x1=x2
|
||||||
horizontal_line(x1, y1, abs(dx)+1 as uword)
|
horizontal_line(x1, y1, abs(dx) as uword +1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
; TODO rewrite the rest in optimized assembly
|
|
||||||
|
|
||||||
word @zp d = 0
|
word @zp d = 0
|
||||||
ubyte positive_ix = true
|
ubyte positive_ix = true
|
||||||
if dx < 0 {
|
if dx < 0 {
|
||||||
dx = -dx
|
dx = -dx
|
||||||
positive_ix = false
|
positive_ix = false
|
||||||
}
|
}
|
||||||
dx *= 2
|
word @zp dx2 = dx*2
|
||||||
dy *= 2
|
word @zp dy2 = dy*2
|
||||||
internal_plotx = x1
|
internal_plotx = x1
|
||||||
|
|
||||||
if dx >= dy {
|
if dx >= dy {
|
||||||
@ -72,10 +69,10 @@ graphics {
|
|||||||
if internal_plotx==x2
|
if internal_plotx==x2
|
||||||
return
|
return
|
||||||
internal_plotx++
|
internal_plotx++
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -84,10 +81,10 @@ graphics {
|
|||||||
if internal_plotx==x2
|
if internal_plotx==x2
|
||||||
return
|
return
|
||||||
internal_plotx--
|
internal_plotx--
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,10 +96,10 @@ graphics {
|
|||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
internal_plotx++
|
internal_plotx++
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -111,10 +108,10 @@ graphics {
|
|||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
internal_plotx--
|
internal_plotx--
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,11 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%target c64
|
|
||||||
|
|
||||||
c64 {
|
c64 {
|
||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
&ubyte STATUS = $90 ; kernel status variable for I/O
|
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||||
|
|
||||||
@ -178,7 +176,7 @@ c64 {
|
|||||||
|
|
||||||
; ---- C64 ROM kernal routines ----
|
; ---- C64 ROM kernal routines ----
|
||||||
|
|
||||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
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 $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of 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 $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||||
@ -202,7 +200,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
|
|||||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
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 $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
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 $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 $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 $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
@ -211,10 +209,10 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
|
|||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
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 $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 $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
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 $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 $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
|
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 $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 $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 $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
@ -246,7 +244,7 @@ asmsub STOP2() -> ubyte @A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub RDTIM16() -> uword @AY {
|
asmsub RDTIM16() -> uword @AY {
|
||||||
; -- like RDTIM() but only returning the lower 16 bits for convenience
|
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr c64.RDTIM
|
jsr c64.RDTIM
|
||||||
@ -294,6 +292,12 @@ asmsub init_system() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub init_system_phase2() {
|
||||||
|
%asm {{
|
||||||
|
rts ; no phase 2 steps on the C64
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #$80
|
lda #$80
|
||||||
@ -304,27 +308,13 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_irqvec_excl() clobbers(A) {
|
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
|
||||||
sei
|
|
||||||
lda #<_irq_handler
|
|
||||||
sta c64.CINV
|
|
||||||
lda #>_irq_handler
|
|
||||||
sta c64.CINV+1
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
_irq_handler jsr set_irqvec._irq_handler_init
|
|
||||||
jsr irq.irq
|
|
||||||
jsr set_irqvec._irq_handler_end
|
|
||||||
lda #$ff
|
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
|
||||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
|
||||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub set_irqvec() clobbers(A) {
|
|
||||||
%asm {{
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda #0
|
||||||
|
adc #0
|
||||||
|
sta _use_kernal
|
||||||
sei
|
sei
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
sta c64.CINV
|
sta c64.CINV
|
||||||
@ -333,9 +323,23 @@ asmsub set_irqvec() clobbers(A) {
|
|||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
_irq_handler jsr _irq_handler_init
|
_irq_handler jsr _irq_handler_init
|
||||||
jsr irq.irq
|
_modified jsr $ffff ; modified
|
||||||
jsr _irq_handler_end
|
jsr _irq_handler_end
|
||||||
jmp c64.IRQDFRT ; continue with normal kernel irq routine
|
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
|
_irq_handler_init
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||||
@ -388,7 +392,7 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub restore_irqvec() clobbers(A) {
|
asmsub restore_irq() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda #<c64.IRQDFRT
|
lda #<c64.IRQDFRT
|
||||||
@ -404,8 +408,15 @@ asmsub restore_irqvec() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda #0
|
||||||
|
adc #0
|
||||||
|
sta set_irq._use_kernal
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
sei
|
sei
|
||||||
jsr _setup_raster_irq
|
jsr _setup_raster_irq
|
||||||
lda #<_raster_irq_handler
|
lda #<_raster_irq_handler
|
||||||
@ -416,12 +427,21 @@ asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
|||||||
rts
|
rts
|
||||||
|
|
||||||
_raster_irq_handler
|
_raster_irq_handler
|
||||||
jsr set_irqvec._irq_handler_init
|
jsr set_irq._irq_handler_init
|
||||||
jsr irq.irq
|
_modified jsr $ffff ; modified
|
||||||
jsr set_irqvec._irq_handler_end
|
jsr set_irq._irq_handler_end
|
||||||
lda #$ff
|
lda #$ff
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
jmp c64.IRQDFRT
|
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
|
_setup_raster_irq
|
||||||
pha
|
pha
|
||||||
@ -445,28 +465,6 @@ _setup_raster_irq
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_rasterirq_excl(uword rasterpos @ AY) clobbers(A) {
|
|
||||||
%asm {{
|
|
||||||
sei
|
|
||||||
jsr set_rasterirq._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_irqvec._irq_handler_init
|
|
||||||
jsr irq.irq
|
|
||||||
jsr set_irqvec._irq_handler_end
|
|
||||||
lda #$ff
|
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
|
||||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
|
||||||
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
; ---- end of C64 specific system utility routines ----
|
; ---- end of C64 specific system utility routines ----
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -478,7 +476,7 @@ sys {
|
|||||||
|
|
||||||
|
|
||||||
asmsub reset_system() {
|
asmsub reset_system() {
|
||||||
; Soft-reset the system back to Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda #14
|
lda #14
|
||||||
@ -489,6 +487,7 @@ sys {
|
|||||||
|
|
||||||
sub wait(uword jiffies) {
|
sub wait(uword jiffies) {
|
||||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
; --- 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 {
|
repeat jiffies {
|
||||||
ubyte jiff = lsb(c64.RDTIM16())
|
ubyte jiff = lsb(c64.RDTIM16())
|
||||||
while jiff==lsb(c64.RDTIM16()) {
|
while jiff==lsb(c64.RDTIM16()) {
|
||||||
@ -497,22 +496,47 @@ sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||||
%asm {{
|
%asm {{
|
||||||
ldx cx16.r0
|
ldx cx16.r0
|
||||||
stx P8ZP_SCRATCH_W1
|
stx P8ZP_SCRATCH_W1 ; source in ZP
|
||||||
ldx cx16.r0+1
|
ldx cx16.r0+1
|
||||||
stx P8ZP_SCRATCH_W1+1
|
stx P8ZP_SCRATCH_W1+1
|
||||||
ldx cx16.r1
|
ldx cx16.r1
|
||||||
stx P8ZP_SCRATCH_W2
|
stx P8ZP_SCRATCH_W2 ; target in ZP
|
||||||
ldx cx16.r1+1
|
ldx cx16.r1+1
|
||||||
stx P8ZP_SCRATCH_W2+1
|
stx P8ZP_SCRATCH_W2+1
|
||||||
cpy #0
|
cpy #0
|
||||||
bne _longcopy
|
bne _longcopy
|
||||||
; copy <= 255
|
|
||||||
tay
|
|
||||||
|
|
||||||
_remainder
|
; 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
|
lda P8ZP_SCRATCH_W1
|
||||||
bne +
|
bne +
|
||||||
dec P8ZP_SCRATCH_W1+1
|
dec P8ZP_SCRATCH_W1+1
|
||||||
@ -521,18 +545,19 @@ _remainder
|
|||||||
bne +
|
bne +
|
||||||
dec P8ZP_SCRATCH_W2+1
|
dec P8ZP_SCRATCH_W2+1
|
||||||
+ dec P8ZP_SCRATCH_W2
|
+ dec P8ZP_SCRATCH_W2
|
||||||
- lda (P8ZP_SCRATCH_W1), y
|
|
||||||
sta (P8ZP_SCRATCH_W2), y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
sta (P8ZP_SCRATCH_W2),y
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_longcopy
|
_longcopy
|
||||||
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder
|
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder in last page
|
||||||
tya
|
tya
|
||||||
tax ; x = num pages (1+)
|
tax ; x = num pages (1+)
|
||||||
ldy #0
|
ldy #0
|
||||||
- lda (P8ZP_SCRATCH_W1),y ; copy a page at a time
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
sta (P8ZP_SCRATCH_W2),y
|
sta (P8ZP_SCRATCH_W2),y
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
@ -541,7 +566,8 @@ _longcopy
|
|||||||
dex
|
dex
|
||||||
bne -
|
bne -
|
||||||
ldy P8ZP_SCRATCH_B1
|
ldy P8ZP_SCRATCH_B1
|
||||||
jmp _remainder
|
bne _copyshort
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -673,4 +699,37 @@ cx16 {
|
|||||||
&uword r14 = $cf1c
|
&uword r14 = $cf1c
|
||||||
&uword r15 = $cf1e
|
&uword r15 = $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
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%target c64
|
|
||||||
%import syslib
|
%import syslib
|
||||||
%import conv
|
%import conv
|
||||||
|
|
||||||
@ -19,12 +18,27 @@ sub clear_screen() {
|
|||||||
txt.chrout(147)
|
txt.chrout(147)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub home() {
|
||||||
|
txt.chrout(19)
|
||||||
|
}
|
||||||
|
|
||||||
sub nl() {
|
sub nl() {
|
||||||
txt.chrout('\n')
|
txt.chrout('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub home() {
|
sub spc() {
|
||||||
txt.chrout(19)
|
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) {
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
@ -571,7 +585,7 @@ _colormod sta $ffff ; modified
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
tax
|
tax
|
||||||
|
@ -7,239 +7,213 @@ conv {
|
|||||||
|
|
||||||
; ----- number conversions to decimal strings ----
|
; ----- number conversions to decimal strings ----
|
||||||
|
|
||||||
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
|
str string_out = "????????????????" ; result buffer for the string conversion routines
|
||||||
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
|
||||||
|
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 {{
|
%asm {{
|
||||||
ldy #uword2decimal.ASCII_0_OFFSET
|
phx
|
||||||
bne uword2decimal.hex_try200
|
jsr conv.ubyte2decimal
|
||||||
rts
|
sty string_out
|
||||||
|
sta string_out+1
|
||||||
|
stx string_out+2
|
||||||
|
lda #0
|
||||||
|
sta string_out+3
|
||||||
|
plx
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
|
asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
|
||||||
; ---- convert 16 bit uword in A/Y to decimal
|
; ---- convert the ubyte in A in decimal string form, without left padding 0s
|
||||||
; 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 {{
|
%asm {{
|
||||||
|
phx
|
||||||
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
|
ldy #0
|
||||||
;By Omegamatrix Further optimizations by tepples
|
sty P8ZP_SCRATCH_B1
|
||||||
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
|
jsr conv.ubyte2decimal
|
||||||
|
_output_byte_digits
|
||||||
;HexToDec99
|
; hundreds?
|
||||||
; start in A
|
cpy #'0'
|
||||||
; end with A = 10's, decOnes (also in X)
|
beq +
|
||||||
|
|
||||||
;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
|
pha
|
||||||
and #$0f
|
tya
|
||||||
tax
|
ldy P8ZP_SCRATCH_B1
|
||||||
ldy _hex_digits,x
|
sta string_out,y
|
||||||
pla
|
pla
|
||||||
lsr a
|
inc P8ZP_SCRATCH_B1
|
||||||
lsr a
|
; tens?
|
||||||
lsr a
|
+ ldy P8ZP_SCRATCH_B1
|
||||||
lsr a
|
cmp #'0'
|
||||||
tax
|
beq +
|
||||||
lda _hex_digits,x
|
sta string_out,y
|
||||||
ldx P8ZP_SCRATCH_REG
|
iny
|
||||||
rts
|
+ ; ones.
|
||||||
|
txa
|
||||||
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
sta string_out,y
|
||||||
|
iny
|
||||||
|
lda #0
|
||||||
|
sta string_out,y
|
||||||
|
plx
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub uword2hex (uword value @AY) clobbers(A,Y) {
|
asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
; ---- convert the byte in A in decimal string form, without left padding 0s
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_REG
|
phx
|
||||||
tya
|
ldy #0
|
||||||
jsr ubyte2hex
|
sty P8ZP_SCRATCH_B1
|
||||||
sta output
|
cmp #0
|
||||||
sty output+1
|
bpl +
|
||||||
lda P8ZP_SCRATCH_REG
|
pha
|
||||||
jsr ubyte2hex
|
lda #'-'
|
||||||
sta output+2
|
sta string_out
|
||||||
sty output+3
|
inc P8ZP_SCRATCH_B1
|
||||||
rts
|
pla
|
||||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
+ 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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +231,7 @@ asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #0
|
ldy #0
|
||||||
lda (P8ZP_SCRATCH_W1)
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
cmp #'$'
|
cmp #'$'
|
||||||
beq _hex
|
beq _hex
|
||||||
@ -520,4 +494,243 @@ _stop
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ----- 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)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,17 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%target cx16
|
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
floats {
|
floats {
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- 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 PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
|
|
||||||
|
ubyte[5] tempvar_swap_float ; used for some swap() operations
|
||||||
|
|
||||||
|
|
||||||
; ---- ROM float functions ----
|
; ---- ROM float functions ----
|
||||||
|
|
||||||
@ -43,46 +45,44 @@ romsub $fe1e = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
|
|||||||
romsub $fe24 = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
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 $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 $fe2a = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||||
romsub $fe33 = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
romsub $fe30 = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
|
||||||
romsub $fe36 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
romsub $fe33 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||||
romsub $fe3c = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
romsub $fe36 = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||||
romsub $fe3f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
romsub $fe39 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||||
romsub $fe42 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
romsub $fe3c = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||||
|
|
||||||
romsub $fe48 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
romsub $fe42 = MOVFM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1
|
||||||
romsub $fe4b = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
romsub $fe45 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||||
romsub $fe4e = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
romsub $fe48 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||||
romsub $fe51 = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
romsub $fe4b = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||||
romsub $fe54 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
romsub $fe4e = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||||
romsub $fe5a = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
romsub $fe54 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||||
romsub $fe5d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
romsub $fe57 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||||
romsub $fe60 = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
romsub $fe5a = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||||
romsub $fe6c = ABS() ; fac1 = ABS(fac1)
|
romsub $fe66 = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
|
||||||
romsub $fe6f = 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 $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 $fe78 = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
romsub $fe72 = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||||
romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
romsub $fe78 = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
romsub $fe7b = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||||
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $fe81 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
romsub $fe84 = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||||
; note: there is no FPWR() on the Cx16
|
romsub $fe8a = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
||||||
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
|
romsub $fe8d = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||||
romsub $fe96 = 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 $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
|
romsub $fe99 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||||
romsub $fea2 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
romsub $fe9c = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||||
romsub $fea5 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
romsub $fe9f = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||||
romsub $fea8 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
romsub $fea2 = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||||
romsub $feab = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
|
||||||
romsub $feae = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
|
||||||
|
|
||||||
|
|
||||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sta P8ZP_SCRATCH_W2
|
sta _tmp
|
||||||
sty P8ZP_SCRATCH_B1
|
sty P8ZP_SCRATCH_B1
|
||||||
tya
|
tya
|
||||||
ldy P8ZP_SCRATCH_W2
|
ldy _tmp
|
||||||
jsr GIVAYF ; load it as signed... correct afterwards
|
jsr GIVAYF ; load it as signed... correct afterwards
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
bpl +
|
bpl +
|
||||||
@ -91,6 +91,7 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
jsr FADD
|
jsr FADD
|
||||||
+ plx
|
+ plx
|
||||||
rts
|
rts
|
||||||
|
_tmp .byte 0
|
||||||
_flt65536 .byte 145,0,0,0,0 ; 65536.0
|
_flt65536 .byte 145,0,0,0,0 ; 65536.0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -128,6 +129,14 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub FREADUY (ubyte value @Y) {
|
||||||
|
; -- 8 bit unsigned Y -> float in fac1
|
||||||
|
%asm {{
|
||||||
|
lda #0
|
||||||
|
jmp GIVAYF
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
sub print_f (float value) {
|
sub print_f (float value) {
|
||||||
; ---- prints the floating point value (without a newline).
|
; ---- prints the floating point value (without a newline).
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -149,7 +158,7 @@ sub print_f (float value) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
%asminclude "library:c64/floats.asm", ""
|
%asminclude "library:c64/floats.asm"
|
||||||
%asminclude "library:c64/floats_funcs.asm", ""
|
%asminclude "library:c64/floats_funcs.asm"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,41 @@
|
|||||||
%target cx16
|
; Bitmap pixel graphics routines for the CommanderX16
|
||||||
|
|
||||||
; Bitmap pixel graphics module for the CommanderX16
|
|
||||||
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
|
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
|
||||||
; This only works on the Cx16. No text layer is currently shown, text can be drawn as part of the bitmap itself.
|
; (These modes are not supported by the documented GRAPH_xxxx kernal routines)
|
||||||
; Note: for compatible graphics code that words on C64 too, use the "graphics" module instead.
|
;
|
||||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
; 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?
|
; 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 {
|
gfx2 {
|
||||||
|
|
||||||
; read-only control variables:
|
; read-only control variables:
|
||||||
ubyte active_mode = 255
|
ubyte active_mode = 0
|
||||||
uword width = 0
|
uword width = 0
|
||||||
uword height = 0
|
uword height = 0
|
||||||
ubyte bpp = 0
|
ubyte bpp = 0
|
||||||
ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
|
ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
|
||||||
|
|
||||||
sub screen_mode(ubyte mode) {
|
sub screen_mode(ubyte mode) {
|
||||||
; mode 0 = bitmap 320 x 240 x 1c monochrome
|
|
||||||
; mode 1 = bitmap 320 x 240 x 256c
|
|
||||||
; mode 128 = bitmap 640 x 480 x 1c monochrome
|
|
||||||
; ...other modes?
|
|
||||||
|
|
||||||
; copy the lower-case charset to the upper part of the vram, so we can use it later to plot text
|
|
||||||
|
|
||||||
when mode {
|
when mode {
|
||||||
0 -> {
|
1 -> {
|
||||||
; 320 x 240 x 1c
|
; lores monochrome
|
||||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||||
cx16.VERA_DC_HSCALE = 64
|
cx16.VERA_DC_HSCALE = 64
|
||||||
cx16.VERA_DC_VSCALE = 64
|
cx16.VERA_DC_VSCALE = 64
|
||||||
@ -38,8 +46,9 @@ gfx2 {
|
|||||||
height = 240
|
height = 240
|
||||||
bpp = 1
|
bpp = 1
|
||||||
}
|
}
|
||||||
1 -> {
|
; TODO modes 2, 3 not yet implemented
|
||||||
; 320 x 240 x 256c
|
4 -> {
|
||||||
|
; lores 256c
|
||||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||||
cx16.VERA_DC_HSCALE = 64
|
cx16.VERA_DC_HSCALE = 64
|
||||||
cx16.VERA_DC_VSCALE = 64
|
cx16.VERA_DC_VSCALE = 64
|
||||||
@ -50,8 +59,8 @@ gfx2 {
|
|||||||
height = 240
|
height = 240
|
||||||
bpp = 8
|
bpp = 8
|
||||||
}
|
}
|
||||||
128 -> {
|
5 -> {
|
||||||
; 640 x 480 x 1c
|
; highres monochrome
|
||||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||||
cx16.VERA_DC_HSCALE = 128
|
cx16.VERA_DC_HSCALE = 128
|
||||||
cx16.VERA_DC_VSCALE = 128
|
cx16.VERA_DC_VSCALE = 128
|
||||||
@ -62,13 +71,26 @@ gfx2 {
|
|||||||
height = 480
|
height = 480
|
||||||
bpp = 1
|
bpp = 1
|
||||||
}
|
}
|
||||||
255 -> {
|
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
|
; back to default text mode and colors
|
||||||
cx16.VERA_CTRL = %10000000 ; reset VERA and palette
|
cx16.VERA_CTRL = %10000000 ; reset VERA and palette
|
||||||
c64.CINT() ; back to text mode
|
c64.CINT() ; back to text mode
|
||||||
width = 0
|
width = 0
|
||||||
height = 0
|
height = 0
|
||||||
bpp = 0
|
bpp = 0
|
||||||
|
mode = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,21 +103,28 @@ gfx2 {
|
|||||||
monochrome_stipple(false)
|
monochrome_stipple(false)
|
||||||
position(0, 0)
|
position(0, 0)
|
||||||
when active_mode {
|
when active_mode {
|
||||||
0 -> {
|
1 -> {
|
||||||
; 320 x 240 x 1c
|
; lores monochrome
|
||||||
repeat 240/2/8
|
repeat 240/2/8
|
||||||
cs_innerloop640()
|
cs_innerloop640()
|
||||||
}
|
}
|
||||||
1 -> {
|
; TODO mode 2, 3
|
||||||
; 320 x 240 x 256c
|
4 -> {
|
||||||
|
; lores 256c
|
||||||
repeat 240/2
|
repeat 240/2
|
||||||
cs_innerloop640()
|
cs_innerloop640()
|
||||||
}
|
}
|
||||||
128 -> {
|
5 -> {
|
||||||
; 640 x 480 x 1c
|
; highres monochrome
|
||||||
repeat 480/8
|
repeat 480/8
|
||||||
cs_innerloop640()
|
cs_innerloop640()
|
||||||
}
|
}
|
||||||
|
6 -> {
|
||||||
|
; highres 4c
|
||||||
|
repeat 480/4
|
||||||
|
cs_innerloop640()
|
||||||
|
}
|
||||||
|
; modes 7 and 8 not supported due to lack of VRAM
|
||||||
}
|
}
|
||||||
position(0, 0)
|
position(0, 0)
|
||||||
}
|
}
|
||||||
@ -130,35 +159,13 @@ gfx2 {
|
|||||||
if length==0
|
if length==0
|
||||||
return
|
return
|
||||||
when active_mode {
|
when active_mode {
|
||||||
1 -> {
|
1, 5 -> {
|
||||||
; 8bpp mode
|
; monochrome modes, either resolution
|
||||||
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
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
0, 128 -> {
|
|
||||||
; 1 bpp mode
|
|
||||||
ubyte separate_pixels = (8-lsb(x)) & 7
|
ubyte separate_pixels = (8-lsb(x)) & 7
|
||||||
if separate_pixels as uword > length
|
if separate_pixels as uword > length
|
||||||
separate_pixels = lsb(length)
|
separate_pixels = lsb(length)
|
||||||
repeat separate_pixels {
|
repeat separate_pixels {
|
||||||
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size
|
; TODO optimize this by writing a masked byte in 1 go
|
||||||
plot(x, y, color)
|
plot(x, y, color)
|
||||||
x++
|
x++
|
||||||
}
|
}
|
||||||
@ -200,116 +207,188 @@ _loop lda length
|
|||||||
_done
|
_done
|
||||||
}}
|
}}
|
||||||
repeat separate_pixels {
|
repeat separate_pixels {
|
||||||
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size
|
; TODO optimize this by writing a masked byte in 1 go
|
||||||
plot(x, y, color)
|
plot(x, y, color)
|
||||||
x++
|
x++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) ; vera auto-increment off again
|
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) {
|
sub vertical_line(uword x, uword y, uword height, ubyte color) {
|
||||||
position(x,y)
|
when active_mode {
|
||||||
if active_mode==1 {
|
1, 5 -> {
|
||||||
; set vera auto-increment to 320 pixel increment (=next line)
|
; monochrome, lo-res
|
||||||
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) | (14<<4)
|
cx16.r15L = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||||
%asm {{
|
if color {
|
||||||
ldy height
|
if monochrome_dont_stipple_flag {
|
||||||
beq +
|
; draw continuous line.
|
||||||
lda color
|
position2(x,y,true)
|
||||||
- sta cx16.VERA_DATA0
|
if active_mode==1
|
||||||
dey
|
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||||
bne -
|
else
|
||||||
+
|
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||||
}}
|
repeat height {
|
||||||
return
|
%asm {{
|
||||||
}
|
lda cx16.VERA_DATA0
|
||||||
|
ora cx16.r15L
|
||||||
; note for the 1 bpp modes we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
|
sta cx16.VERA_DATA1
|
||||||
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) ; no auto advance
|
}}
|
||||||
cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
}
|
||||||
if active_mode>=128
|
} else {
|
||||||
cx16.r14 = 640/8
|
; draw stippled line.
|
||||||
else
|
if x&1 {
|
||||||
cx16.r14 = 320/8
|
y++
|
||||||
if color {
|
height--
|
||||||
if monochrome_dont_stipple_flag {
|
}
|
||||||
repeat height {
|
position2(x,y,true)
|
||||||
%asm {{
|
if active_mode==1
|
||||||
lda cx16.VERA_DATA0
|
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
|
||||||
ora cx16.r15
|
else
|
||||||
sta cx16.VERA_DATA0
|
set_both_strides(13) ; 160 increment = 2 line in 640 px monochrome
|
||||||
lda cx16.VERA_ADDR_L
|
repeat height/2 {
|
||||||
clc
|
%asm {{
|
||||||
adc cx16.r14 ; advance vera ptr to go to the next line
|
lda cx16.VERA_DATA0
|
||||||
sta cx16.VERA_ADDR_L
|
ora cx16.r15L
|
||||||
lda cx16.VERA_ADDR_M
|
sta cx16.VERA_DATA1
|
||||||
adc #0
|
}}
|
||||||
sta cx16.VERA_ADDR_M
|
}
|
||||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
}
|
||||||
; adc #0
|
} else {
|
||||||
; sta cx16.VERA_ADDR_H
|
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
|
||||||
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
; stippling.
|
4 -> {
|
||||||
height = (height+1)/2
|
; 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 {{
|
%asm {{
|
||||||
lda x
|
|
||||||
eor y
|
|
||||||
and #1
|
|
||||||
bne +
|
|
||||||
lda cx16.VERA_ADDR_L
|
|
||||||
clc
|
|
||||||
adc cx16.r14 ; advance vera ptr to go to the next line for correct stipple pattern
|
|
||||||
sta cx16.VERA_ADDR_L
|
|
||||||
lda cx16.VERA_ADDR_M
|
|
||||||
adc #0
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
+
|
|
||||||
asl cx16.r14
|
|
||||||
ldy height
|
ldy height
|
||||||
beq +
|
beq +
|
||||||
- lda cx16.VERA_DATA0
|
lda color
|
||||||
ora cx16.r15
|
- sta cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
lda cx16.VERA_ADDR_L
|
|
||||||
clc
|
|
||||||
adc cx16.r14 ; advance vera data ptr to go to the next-next line
|
|
||||||
sta cx16.VERA_ADDR_L
|
|
||||||
lda cx16.VERA_ADDR_M
|
|
||||||
adc #0
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
|
||||||
; adc #0
|
|
||||||
; sta cx16.VERA_ADDR_H
|
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
+
|
+
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
} else {
|
6 -> {
|
||||||
cx16.r15 = ~cx16.r15
|
; highres 4c
|
||||||
repeat height {
|
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
|
||||||
%asm {{
|
if height==0
|
||||||
lda cx16.VERA_DATA0
|
return
|
||||||
and cx16.r15
|
position2(x,y,true)
|
||||||
sta cx16.VERA_DATA0
|
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||||
lda cx16.VERA_ADDR_L
|
color &= 3
|
||||||
clc
|
color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||||
adc cx16.r14 ; advance vera data ptr to go to the next line
|
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||||
sta cx16.VERA_ADDR_L
|
repeat height {
|
||||||
lda cx16.VERA_ADDR_M
|
%asm {{
|
||||||
adc #0
|
lda cx16.VERA_DATA0
|
||||||
sta cx16.VERA_ADDR_M
|
and mask
|
||||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
ora color
|
||||||
; adc #0
|
sta cx16.VERA_DATA1
|
||||||
; sta cx16.VERA_ADDR_H
|
}}
|
||||||
}}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
||||||
@ -320,42 +399,41 @@ _done
|
|||||||
swap(x1, x2)
|
swap(x1, x2)
|
||||||
swap(y1, y2)
|
swap(y1, y2)
|
||||||
}
|
}
|
||||||
word @zp dx = x2-x1 as word
|
word @zp dx = (x2 as word)-x1
|
||||||
word @zp dy = y2-y1 as word
|
word @zp dy = (y2 as word)-y1
|
||||||
|
|
||||||
if dx==0 {
|
if dx==0 {
|
||||||
vertical_line(x1, y1, abs(dy)+1 as uword, color)
|
vertical_line(x1, y1, abs(dy) as uword +1, color)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dy==0 {
|
if dy==0 {
|
||||||
if x1>x2
|
if x1>x2
|
||||||
x1=x2
|
x1=x2
|
||||||
horizontal_line(x1, y1, abs(dx)+1 as uword, color)
|
horizontal_line(x1, y1, abs(dx) as uword +1, color)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
; TODO rewrite the rest in optimized assembly (or reuse GRAPH_draw_line if we can get the FB replacement vector layer working)
|
|
||||||
word @zp d = 0
|
word @zp d = 0
|
||||||
ubyte positive_ix = true
|
cx16.r13 = true ; 'positive_ix'
|
||||||
if dx < 0 {
|
if dx < 0 {
|
||||||
dx = -dx
|
dx = -dx
|
||||||
positive_ix = false
|
cx16.r13 = false
|
||||||
}
|
}
|
||||||
dx *= 2
|
word @zp dx2 = dx*2
|
||||||
dy *= 2
|
word @zp dy2 = dy*2
|
||||||
cx16.r14 = x1 ; internal plot X
|
cx16.r14 = x1 ; internal plot X
|
||||||
|
|
||||||
if dx >= dy {
|
if dx >= dy {
|
||||||
if positive_ix {
|
if cx16.r13 {
|
||||||
repeat {
|
repeat {
|
||||||
plot(cx16.r14, y1, color)
|
plot(cx16.r14, y1, color)
|
||||||
if cx16.r14==x2
|
if cx16.r14==x2
|
||||||
return
|
return
|
||||||
cx16.r14++
|
cx16.r14++
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -364,25 +442,25 @@ _done
|
|||||||
if cx16.r14==x2
|
if cx16.r14==x2
|
||||||
return
|
return
|
||||||
cx16.r14--
|
cx16.r14--
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if positive_ix {
|
if cx16.r13 {
|
||||||
repeat {
|
repeat {
|
||||||
plot(cx16.r14, y1, color)
|
plot(cx16.r14, y1, color)
|
||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
cx16.r14++
|
cx16.r14++
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -391,10 +469,10 @@ _done
|
|||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
cx16.r14--
|
cx16.r14--
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,79 +546,160 @@ _done
|
|||||||
|
|
||||||
sub plot(uword @zp x, uword y, ubyte color) {
|
sub plot(uword @zp x, uword y, ubyte color) {
|
||||||
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||||
uword addr
|
ubyte[4] mask4c = [%00111111, %11001111, %11110011, %11111100]
|
||||||
ubyte value
|
ubyte[4] shift4c = [6,4,2,0]
|
||||||
|
|
||||||
when active_mode {
|
when active_mode {
|
||||||
0 -> {
|
|
||||||
%asm {{
|
|
||||||
lda x
|
|
||||||
eor y
|
|
||||||
ora monochrome_dont_stipple_flag
|
|
||||||
and #1
|
|
||||||
}}
|
|
||||||
if_nz {
|
|
||||||
addr = x/8 + y*(320/8)
|
|
||||||
value = bits[lsb(x)&7]
|
|
||||||
if color
|
|
||||||
cx16.vpoke_or(0, addr, value)
|
|
||||||
else {
|
|
||||||
value = ~value
|
|
||||||
cx16.vpoke_and(0, addr, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
128 -> {
|
|
||||||
%asm {{
|
|
||||||
lda x
|
|
||||||
eor y
|
|
||||||
ora monochrome_dont_stipple_flag
|
|
||||||
and #1
|
|
||||||
}}
|
|
||||||
if_nz {
|
|
||||||
addr = x/8 + y*(640/8)
|
|
||||||
value = bits[lsb(x)&7]
|
|
||||||
if color
|
|
||||||
cx16.vpoke_or(0, addr, value)
|
|
||||||
else {
|
|
||||||
value = ~value
|
|
||||||
cx16.vpoke_and(0, addr, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1 -> {
|
1 -> {
|
||||||
void addr_mul_320_add_24(y, x) ; 24 bits result is in r0 and r1L
|
; lores monochrome
|
||||||
value = lsb(cx16.r1)
|
%asm {{
|
||||||
cx16.vpoke(value, cx16.r0, color)
|
lda x
|
||||||
; activate vera auto-increment mode so next_pixel() can be used after this
|
eor y
|
||||||
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) | %00010000
|
ora monochrome_dont_stipple_flag
|
||||||
color = cx16.VERA_DATA0
|
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) {
|
sub position(uword @zp x, uword y) {
|
||||||
|
ubyte bank
|
||||||
when active_mode {
|
when active_mode {
|
||||||
0 -> {
|
1 -> {
|
||||||
|
; lores monochrome
|
||||||
cx16.r0 = y*(320/8) + x/8
|
cx16.r0 = y*(320/8) + x/8
|
||||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||||
}
|
}
|
||||||
128 -> {
|
; 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.r0 = y*(640/8) + x/8
|
||||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||||
}
|
}
|
||||||
1 -> {
|
6 -> {
|
||||||
void addr_mul_320_add_24(y, x) ; 24 bits result is in r0 and r1L
|
; highres 4c
|
||||||
ubyte bank = lsb(cx16.r1)
|
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)
|
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) {
|
inline asmsub next_pixel(ubyte color @A) {
|
||||||
; -- sets the next pixel byte to the graphics chip.
|
; -- sets the next pixel byte to the graphics chip.
|
||||||
; for 8 bpp screens this will plot 1 pixel.
|
; for 8 bpp screens this will plot 1 pixel.
|
||||||
; for 1 bpp screens it will plot 8 pixels at once (color = bit pattern).
|
; 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 {{
|
%asm {{
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
}}
|
}}
|
||||||
@ -550,6 +709,7 @@ _done
|
|||||||
; -- sets the next bunch of pixels from a prepared array of bytes.
|
; -- sets the next bunch of pixels from a prepared array of bytes.
|
||||||
; for 8 bpp screens this will plot 1 pixel per byte.
|
; 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 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 {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
@ -614,13 +774,13 @@ _done
|
|||||||
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
|
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!).
|
; -- 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.
|
; 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 mulitples of 8 !
|
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to multiples of 8 ! TODO allow per-pixel horizontal positioning
|
||||||
uword chardataptr
|
uword chardataptr
|
||||||
when active_mode {
|
when active_mode {
|
||||||
0, 128 -> {
|
1, 5 -> {
|
||||||
; 1-bitplane modes
|
; monochrome mode, either resolution
|
||||||
cx16.r2 = 40
|
cx16.r2 = 40
|
||||||
if active_mode>=128
|
if active_mode==5
|
||||||
cx16.r2 = 80
|
cx16.r2 = 80
|
||||||
while @(sctextptr) {
|
while @(sctextptr) {
|
||||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||||
@ -660,12 +820,13 @@ _done
|
|||||||
sctextptr++
|
sctextptr++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1 -> {
|
4 -> {
|
||||||
; 320 x 240 x 256c
|
; lores 256c
|
||||||
while @(sctextptr) {
|
while @(sctextptr) {
|
||||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||||
repeat 8 {
|
repeat 8 {
|
||||||
|
; TODO rewrite this inner loop fully in assembly
|
||||||
position(x,y)
|
position(x,y)
|
||||||
y++
|
y++
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -689,6 +850,28 @@ _done
|
|||||||
sctextptr++
|
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++
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,48 +892,98 @@ _done
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub addr_mul_320_add_24(uword address @R0, uword value @AY) clobbers(A) -> uword @R0, ubyte @R1 {
|
asmsub addr_mul_24_for_highres_4c(uword yy @R2, uword xx @R3) clobbers(A, Y) -> uword @R0, uword @R1 {
|
||||||
%asm {{
|
; yy * 160 + xx/4 (24 bits calculation)
|
||||||
sta P8ZP_SCRATCH_W1
|
; 24 bits result is in r0 and r1L (highest byte)
|
||||||
sty P8ZP_SCRATCH_W1+1
|
%asm {{
|
||||||
lda cx16.r0
|
ldy #5
|
||||||
sta P8ZP_SCRATCH_B1
|
- asl cx16.r2
|
||||||
lda cx16.r0+1
|
rol cx16.r2+1
|
||||||
sta cx16.r1
|
dey
|
||||||
sta P8ZP_SCRATCH_REG
|
bne -
|
||||||
lda cx16.r0
|
lda cx16.r2
|
||||||
asl a
|
sta cx16.r0
|
||||||
rol P8ZP_SCRATCH_REG
|
lda cx16.r2+1
|
||||||
asl a
|
sta cx16.r0+1
|
||||||
rol P8ZP_SCRATCH_REG
|
asl cx16.r0
|
||||||
asl a
|
rol cx16.r0+1
|
||||||
rol P8ZP_SCRATCH_REG
|
asl cx16.r0
|
||||||
asl a
|
rol cx16.r0+1
|
||||||
rol P8ZP_SCRATCH_REG
|
|
||||||
asl a
|
; xx >>= 2 (xx=R3)
|
||||||
rol P8ZP_SCRATCH_REG
|
lsr cx16.r3+1
|
||||||
asl a
|
ror cx16.r3
|
||||||
rol P8ZP_SCRATCH_REG
|
lsr cx16.r3+1
|
||||||
sta cx16.r0
|
ror cx16.r3
|
||||||
lda P8ZP_SCRATCH_B1
|
|
||||||
clc
|
; add r2 and xx (r3) to r0 (24-bits)
|
||||||
adc P8ZP_SCRATCH_REG
|
stz cx16.r1
|
||||||
sta cx16.r0+1
|
clc
|
||||||
bcc +
|
lda cx16.r0
|
||||||
inc cx16.r1
|
adc cx16.r2
|
||||||
+ ; now add the value to this 24-bits number
|
sta cx16.r0
|
||||||
lda cx16.r0
|
lda cx16.r0+1
|
||||||
clc
|
adc cx16.r2+1
|
||||||
adc P8ZP_SCRATCH_W1
|
sta cx16.r0+1
|
||||||
sta cx16.r0
|
bcc +
|
||||||
lda cx16.r0+1
|
inc cx16.r1
|
||||||
adc P8ZP_SCRATCH_W1+1
|
+ clc
|
||||||
sta cx16.r0+1
|
lda cx16.r0
|
||||||
bcc +
|
adc cx16.r3
|
||||||
inc cx16.r1
|
sta cx16.r0
|
||||||
+ lda cx16.r1
|
lda cx16.r0+1
|
||||||
rts
|
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
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
%target cx16
|
|
||||||
%import syslib
|
%import syslib
|
||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
; Bitmap pixel graphics module for the CommanderX16
|
; Bitmap pixel graphics module for the CommanderX16
|
||||||
; wraps the graphics functions that are in ROM.
|
; wraps the graphics functions that are in ROM.
|
||||||
; only black/white monchrome 320x200 for now. (i.e. truncated at the bottom)
|
; 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)
|
; 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.
|
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
%target cx16
|
|
||||||
|
|
||||||
; Manipulate the Commander X16's display color palette.
|
; Manipulate the Commander X16's display color palette.
|
||||||
; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
|
; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
|
||||||
|
|
||||||
@ -9,34 +7,46 @@ palette {
|
|||||||
ubyte c
|
ubyte c
|
||||||
|
|
||||||
sub set_color(ubyte index, uword color) {
|
sub set_color(ubyte index, uword color) {
|
||||||
cx16.vpoke(1, $fa00+index*2, lsb(color))
|
vera_palette_ptr = $fa00+(index as uword * 2)
|
||||||
cx16.vpoke(1, $fa01+index*2, msb(color))
|
cx16.vpoke(1, vera_palette_ptr, lsb(color))
|
||||||
|
vera_palette_ptr++
|
||||||
|
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_rgb4(uword palletteptr, uword num_colors) {
|
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
|
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat num_colors {
|
repeat num_colors {
|
||||||
cx16.vpoke(1, vera_palette_ptr+1, @(palletteptr))
|
cx16.vpoke(1, vera_palette_ptr+1, @(palette_bytes_ptr))
|
||||||
palletteptr++
|
palette_bytes_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, @(palletteptr))
|
cx16.vpoke(1, vera_palette_ptr, @(palette_bytes_ptr))
|
||||||
palletteptr++
|
palette_bytes_ptr++
|
||||||
vera_palette_ptr+=2
|
vera_palette_ptr+=2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_rgb8(uword palletteptr, uword num_colors) {
|
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.
|
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
ubyte red
|
ubyte red
|
||||||
ubyte greenblue
|
ubyte greenblue
|
||||||
repeat num_colors {
|
repeat num_colors {
|
||||||
red = @(palletteptr) >> 4
|
red = @(palette_bytes_ptr) >> 4
|
||||||
palletteptr++
|
palette_bytes_ptr++
|
||||||
greenblue = @(palletteptr) & %11110000
|
greenblue = @(palette_bytes_ptr) & %11110000
|
||||||
palletteptr++
|
palette_bytes_ptr++
|
||||||
greenblue |= @(palletteptr) >> 4 ; add Blue
|
greenblue |= @(palette_bytes_ptr) >> 4 ; add Blue
|
||||||
palletteptr++
|
palette_bytes_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, greenblue)
|
cx16.vpoke(1, vera_palette_ptr, greenblue)
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, red)
|
cx16.vpoke(1, vera_palette_ptr, red)
|
||||||
@ -44,20 +54,28 @@ palette {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_monochrome() {
|
sub set_monochrome(uword screencolorRGB, uword drawcolorRGB) {
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
cx16.vpoke(1, vera_palette_ptr, 0)
|
cx16.vpoke(1, vera_palette_ptr, lsb(screencolorRGB)) ; G,B
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, 0)
|
cx16.vpoke(1, vera_palette_ptr, msb(screencolorRGB)) ; R
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
repeat 255 {
|
repeat 255 {
|
||||||
cx16.vpoke(1, vera_palette_ptr, 255)
|
cx16.vpoke(1, vera_palette_ptr, lsb(drawcolorRGB)) ; G,B
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, 255)
|
cx16.vpoke(1, vera_palette_ptr, msb(drawcolorRGB)) ; R
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_all_black() {
|
||||||
|
set_monochrome($000, $000)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub set_all_white() {
|
||||||
|
set_monochrome($fff, $fff)
|
||||||
|
}
|
||||||
|
|
||||||
sub set_grayscale() {
|
sub set_grayscale() {
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat 16 {
|
repeat 16 {
|
||||||
@ -88,7 +106,7 @@ palette {
|
|||||||
$666, ; 12 = medium grey
|
$666, ; 12 = medium grey
|
||||||
$9D8, ; 13 = light green
|
$9D8, ; 13 = light green
|
||||||
$65B, ; 14 = light blue
|
$65B, ; 14 = light blue
|
||||||
$999 ; 15 = light grey
|
$999 ; 15 = light grey
|
||||||
]
|
]
|
||||||
|
|
||||||
uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||||
@ -107,7 +125,7 @@ palette {
|
|||||||
$777, ; 12 = medium grey
|
$777, ; 12 = medium grey
|
||||||
$af9, ; 13 = light green
|
$af9, ; 13 = light green
|
||||||
$76e, ; 14 = light blue
|
$76e, ; 14 = light blue
|
||||||
$bbb ; 15 = light grey
|
$bbb ; 15 = light grey
|
||||||
]
|
]
|
||||||
|
|
||||||
uword[] C64_colorpalette_light = [ ; this is a lighter palette
|
uword[] C64_colorpalette_light = [ ; this is a lighter palette
|
||||||
|
@ -5,9 +5,6 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%target cx16
|
|
||||||
|
|
||||||
|
|
||||||
c64 {
|
c64 {
|
||||||
|
|
||||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||||
@ -24,7 +21,7 @@ romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; re
|
|||||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
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 $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 $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 MEMTOP2
|
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 $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 $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
@ -35,7 +32,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
|
|||||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
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 $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
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 $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 $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 $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
@ -44,10 +41,10 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
|
|||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
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 $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 $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
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 $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 $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
|
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 $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 $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 $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
@ -74,7 +71,7 @@ asmsub STOP2() -> ubyte @A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub RDTIM16() -> uword @AY {
|
asmsub RDTIM16() -> uword @AY {
|
||||||
; -- like RDTIM() but only returning the lower 16 bits for convenience
|
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.RDTIM
|
jsr c64.RDTIM
|
||||||
@ -87,25 +84,15 @@ asmsub RDTIM16() -> uword @AY {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub MEMTOP2() -> ubyte @A {
|
|
||||||
; -- uses MEMTOP's cx16 extension to query the number of available RAM banks.
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
sec
|
|
||||||
jsr c64.MEMTOP
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
|
||||||
; 65c02 hardware vectors:
|
; irq and hardware vectors:
|
||||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt 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
|
; the sixteen virtual 16-bit registers
|
||||||
@ -126,52 +113,87 @@ cx16 {
|
|||||||
&uword r14 = $001e
|
&uword r14 = $001e
|
||||||
&uword r15 = $0020
|
&uword r15 = $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
|
||||||
|
|
||||||
|
|
||||||
; VERA registers
|
; VERA registers
|
||||||
|
|
||||||
const uword VERA_BASE = $9F20
|
const uword VERA_BASE = $9F20
|
||||||
&ubyte VERA_ADDR_L = VERA_BASE + $0000
|
&ubyte VERA_ADDR_L = VERA_BASE + $0000
|
||||||
&ubyte VERA_ADDR_M = VERA_BASE + $0001
|
&ubyte VERA_ADDR_M = VERA_BASE + $0001
|
||||||
&ubyte VERA_ADDR_H = VERA_BASE + $0002
|
&ubyte VERA_ADDR_H = VERA_BASE + $0002
|
||||||
&ubyte VERA_DATA0 = VERA_BASE + $0003
|
&ubyte VERA_DATA0 = VERA_BASE + $0003
|
||||||
&ubyte VERA_DATA1 = VERA_BASE + $0004
|
&ubyte VERA_DATA1 = VERA_BASE + $0004
|
||||||
&ubyte VERA_CTRL = VERA_BASE + $0005
|
&ubyte VERA_CTRL = VERA_BASE + $0005
|
||||||
&ubyte VERA_IEN = VERA_BASE + $0006
|
&ubyte VERA_IEN = VERA_BASE + $0006
|
||||||
&ubyte VERA_ISR = VERA_BASE + $0007
|
&ubyte VERA_ISR = VERA_BASE + $0007
|
||||||
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
||||||
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
|
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
|
||||||
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
|
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
|
||||||
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
|
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
|
||||||
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
|
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
|
||||||
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
|
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
|
||||||
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
|
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
|
||||||
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
|
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
|
||||||
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
|
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
|
||||||
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
||||||
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
||||||
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
||||||
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
|
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
|
||||||
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
|
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
|
||||||
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
|
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
|
||||||
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
|
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
|
||||||
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
|
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
|
||||||
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
|
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
|
||||||
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
|
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
|
||||||
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
|
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
|
||||||
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
|
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
|
||||||
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
|
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
|
||||||
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
|
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
|
||||||
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
|
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
|
||||||
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
|
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
|
||||||
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
|
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
|
||||||
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
|
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
|
||||||
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
|
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
|
||||||
; VERA_PSG_BASE = $1F9C0
|
; VERA_PSG_BASE = $1F9C0
|
||||||
; VERA_PALETTE_BASE = $1FA00
|
; VERA_PALETTE_BASE = $1FA00
|
||||||
; VERA_SPRITES_BASE = $1FC00
|
; VERA_SPRITES_BASE = $1FC00
|
||||||
|
|
||||||
; I/O
|
; I/O
|
||||||
|
|
||||||
const uword via1 = $9f60 ;VIA 6522 #1
|
const uword via1 = $9f00 ;VIA 6522 #1
|
||||||
&ubyte d1prb = via1+0
|
&ubyte d1prb = via1+0
|
||||||
&ubyte d1pra = via1+1
|
&ubyte d1pra = via1+1
|
||||||
&ubyte d1ddrb = via1+2
|
&ubyte d1ddrb = via1+2
|
||||||
@ -189,7 +211,7 @@ cx16 {
|
|||||||
&ubyte d1ier = via1+14
|
&ubyte d1ier = via1+14
|
||||||
&ubyte d1ora = via1+15
|
&ubyte d1ora = via1+15
|
||||||
|
|
||||||
const uword via2 = $9f70 ;VIA 6522 #2
|
const uword via2 = $9f10 ;VIA 6522 #2
|
||||||
&ubyte d2prb = via2+0
|
&ubyte d2prb = via2+0
|
||||||
&ubyte d2pra = via2+1
|
&ubyte d2pra = via2+1
|
||||||
&ubyte d2ddrb = via2+2
|
&ubyte d2ddrb = via2+2
|
||||||
@ -207,6 +229,11 @@ cx16 {
|
|||||||
&ubyte d2ier = via2+14
|
&ubyte d2ier = via2+14
|
||||||
&ubyte d2ora = via2+15
|
&ubyte d2ora = via2+15
|
||||||
|
|
||||||
|
&ubyte ym2151adr = $9f40
|
||||||
|
&ubyte ym2151dat = $9f41
|
||||||
|
|
||||||
|
const uword extdev = $9f60
|
||||||
|
|
||||||
|
|
||||||
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
||||||
; spelling of the names is taken from the Commander X-16 rom sources
|
; spelling of the names is taken from the Commander X-16 rom sources
|
||||||
@ -289,6 +316,32 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
|||||||
|
|
||||||
|
|
||||||
; ---- utilities -----
|
; ---- 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 {
|
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
||||||
; -- get a byte from VERA's video memory
|
; -- get a byte from VERA's video memory
|
||||||
; note: inefficient when reading multiple sequential bytes!
|
; note: inefficient when reading multiple sequential bytes!
|
||||||
@ -332,75 +385,126 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
||||||
; -- write a single byte to VERA's video memory
|
; -- write a single byte to VERA's video memory
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
sty cx16.VERA_DATA0
|
sty cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
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
|
; -- or a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
tya
|
tya
|
||||||
ora cx16.VERA_DATA0
|
ora cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
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
|
; -- and a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
tya
|
tya
|
||||||
and cx16.VERA_DATA0
|
and cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
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
|
; -- xor a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
tya
|
tya
|
||||||
eor cx16.VERA_DATA0
|
eor cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
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) {
|
sub FB_set_pixels_from_buf(uword buffer, uword count) {
|
||||||
%asm {{
|
%asm {{
|
||||||
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
||||||
@ -436,17 +540,15 @@ _loop ldy #0
|
|||||||
}
|
}
|
||||||
|
|
||||||
; ---- system stuff -----
|
; ---- system stuff -----
|
||||||
asmsub init_system() {
|
asmsub init_system() {
|
||||||
; Initializes the machine to a sane starting state.
|
; Initializes the machine to a sane starting state.
|
||||||
; Called automatically by the loader program logic.
|
; Called automatically by the loader program logic.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
cld
|
cld
|
||||||
;stz $00
|
|
||||||
;stz $01
|
|
||||||
;stz d1prb ; select rom bank 0
|
|
||||||
lda #$80
|
lda #$80
|
||||||
sta VERA_CTRL
|
sta VERA_CTRL
|
||||||
|
stz $01 ; select rom bank 0 (enable kernal)
|
||||||
jsr c64.IOINIT
|
jsr c64.IOINIT
|
||||||
jsr c64.RESTOR
|
jsr c64.RESTOR
|
||||||
jsr c64.CINT
|
jsr c64.CINT
|
||||||
@ -468,8 +570,177 @@ asmsub init_system() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
sys {
|
||||||
; ------- lowlevel system routines --------
|
; ------- lowlevel system routines --------
|
||||||
|
|
||||||
@ -477,24 +748,37 @@ sys {
|
|||||||
|
|
||||||
|
|
||||||
asmsub reset_system() {
|
asmsub reset_system() {
|
||||||
; Soft-reset the system back to Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda #14
|
stz $01 ; bank the kernal in
|
||||||
sta $01
|
|
||||||
stz cx16.d1prb ; bank the kernal in
|
|
||||||
jmp (cx16.RESET_VEC)
|
jmp (cx16.RESET_VEC)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub wait(uword jiffies) {
|
asmsub wait(uword jiffies @AY) {
|
||||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||||
repeat jiffies {
|
; note: regular system vsync irq handler must be running, and no nother irqs
|
||||||
ubyte jiff = lsb(c64.RDTIM16())
|
%asm {{
|
||||||
while jiff==lsb(c64.RDTIM16()) {
|
- wai ; wait for irq (assume it was vsync)
|
||||||
; wait until 1 jiffy has passed
|
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) {
|
inline asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%target cx16
|
|
||||||
%import syslib
|
%import syslib
|
||||||
%import conv
|
%import conv
|
||||||
|
|
||||||
@ -19,12 +18,27 @@ sub clear_screen() {
|
|||||||
txt.chrout(147)
|
txt.chrout(147)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub home() {
|
||||||
|
txt.chrout(19)
|
||||||
|
}
|
||||||
|
|
||||||
sub nl() {
|
sub nl() {
|
||||||
txt.chrout('\n')
|
txt.chrout('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub home() {
|
sub spc() {
|
||||||
txt.chrout(19)
|
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) {
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
@ -405,7 +419,7 @@ _print_byte_digits
|
|||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
jmp _ones
|
bra _ones
|
||||||
+ pla
|
+ pla
|
||||||
cmp #'0'
|
cmp #'0'
|
||||||
beq _ones
|
beq _ones
|
||||||
@ -428,7 +442,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
|||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
+ pla
|
+ pla
|
||||||
jsr conv.byte2decimal
|
jsr conv.byte2decimal
|
||||||
jmp print_ub._print_byte_digits
|
bra print_ub._print_byte_digits
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,7 +493,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
jsr print_ubbin
|
jsr print_ubbin
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
jmp print_ubbin
|
bra print_ubbin
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,7 +506,7 @@ asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
jsr print_ubhex
|
jsr print_ubhex
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
jmp print_ubhex
|
bra print_ubhex
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,7 +569,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
|||||||
adc #1
|
adc #1
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jmp print_uw
|
+ bra print_uw
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,95 +597,126 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
|||||||
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A) {
|
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A) {
|
||||||
; ---- sets the character in the screen matrix at the given position
|
; ---- sets the character in the screen matrix at the given position
|
||||||
%asm {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
txa
|
txa
|
||||||
asl a
|
asl a
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
stz cx16.VERA_ADDR_H
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
pla
|
pla
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||||
; ---- get the character in the screen matrix at the given location
|
; ---- get the character in the screen matrix at the given location
|
||||||
%asm {{
|
%asm {{
|
||||||
asl a
|
asl a
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
stz cx16.VERA_ADDR_H
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
|
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
|
; ---- 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 {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
txa
|
txa
|
||||||
asl a
|
asl a
|
||||||
ina
|
ina
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
stz cx16.VERA_ADDR_H
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
pla
|
pla
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||||
; ---- get the color in the screen color matrix at the given location
|
; ---- get the color in the screen color matrix at the given location
|
||||||
%asm {{
|
%asm {{
|
||||||
asl a
|
asl a
|
||||||
ina
|
ina
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
stz cx16.VERA_ADDR_H
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||||
; ---- set char+color at the given position on the screen
|
; ---- 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 {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
lda column
|
lda column
|
||||||
asl a
|
asl a
|
||||||
tax
|
tax
|
||||||
ldy row
|
ldy row
|
||||||
lda charcolor
|
lda charcolor
|
||||||
and #$0f
|
and #$0f
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
stz cx16.VERA_ADDR_H
|
stz cx16.VERA_ADDR_H
|
||||||
stx cx16.VERA_ADDR_L
|
stx cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
lda char
|
lda char
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
inx
|
inx
|
||||||
stz cx16.VERA_ADDR_H
|
stz cx16.VERA_ADDR_H
|
||||||
stx cx16.VERA_ADDR_L
|
stx cx16.VERA_ADDR_L
|
||||||
sty cx16.VERA_ADDR_M
|
sty cx16.VERA_ADDR_M
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
and #$f0
|
and #$f0
|
||||||
ora P8ZP_SCRATCH_B1
|
ora P8ZP_SCRATCH_B1
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
plx
|
plx
|
||||||
rts
|
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) {
|
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
tax
|
tax
|
||||||
|
@ -30,7 +30,7 @@ diskio {
|
|||||||
ubyte low = c64.CHRIN()
|
ubyte low = c64.CHRIN()
|
||||||
ubyte high = c64.CHRIN()
|
ubyte high = c64.CHRIN()
|
||||||
txt.print_uw(mkword(high, low))
|
txt.print_uw(mkword(high, low))
|
||||||
txt.chrout(' ')
|
txt.spc()
|
||||||
ubyte @zp char
|
ubyte @zp char
|
||||||
repeat {
|
repeat {
|
||||||
char = c64.CHRIN()
|
char = c64.CHRIN()
|
||||||
@ -72,7 +72,7 @@ io_error:
|
|||||||
str list_filename = "?" * 32
|
str list_filename = "?" * 32
|
||||||
|
|
||||||
|
|
||||||
; ----- get a list of files (uses iteration functions internally -----
|
; ----- get a list of files (uses iteration functions internally) -----
|
||||||
|
|
||||||
sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte {
|
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.
|
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
|
||||||
@ -295,7 +295,8 @@ _in_buffer sta $ffff
|
|||||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
; 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).
|
; 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 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.
|
; 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 {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
@ -335,6 +336,45 @@ _end rts
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ----- 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 {
|
sub status(ubyte drivenumber) -> uword {
|
||||||
; -- retrieve the disk drive's current status message
|
; -- retrieve the disk drive's current status message
|
||||||
uword messageptr = &filename
|
uword messageptr = &filename
|
||||||
@ -352,18 +392,22 @@ _end rts
|
|||||||
messageptr++
|
messageptr++
|
||||||
}
|
}
|
||||||
|
|
||||||
io_error:
|
|
||||||
@(messageptr) = 0
|
@(messageptr) = 0
|
||||||
|
done:
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(15)
|
c64.CLOSE(15)
|
||||||
return filename
|
return filename
|
||||||
}
|
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
filename = "?disk error"
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
|
||||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
c64.SETLFS(1, drivenumber, 0)
|
||||||
uword end_address = address + size
|
uword end_address = address + size
|
||||||
|
first_byte = 0 ; result var reuse
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda address
|
lda address
|
||||||
@ -380,7 +424,6 @@ io_error:
|
|||||||
plp
|
plp
|
||||||
}}
|
}}
|
||||||
|
|
||||||
first_byte = 0 ; result var reuse
|
|
||||||
if_cc
|
if_cc
|
||||||
first_byte = c64.READST()==0
|
first_byte = c64.READST()==0
|
||||||
|
|
||||||
@ -423,10 +466,9 @@ io_error:
|
|||||||
|
|
||||||
sub delete(ubyte drivenumber, uword filenameptr) {
|
sub delete(ubyte drivenumber, uword filenameptr) {
|
||||||
; -- delete a file on the drive
|
; -- delete a file on the drive
|
||||||
ubyte flen = string.length(filenameptr)
|
|
||||||
filename[0] = 's'
|
filename[0] = 's'
|
||||||
filename[1] = ':'
|
filename[1] = ':'
|
||||||
sys.memcopy(filenameptr, &filename+2, flen+1)
|
ubyte flen = string.copy(filenameptr, &filename+2)
|
||||||
c64.SETNAM(flen+2, filename)
|
c64.SETNAM(flen+2, filename)
|
||||||
c64.SETLFS(1, drivenumber, 15)
|
c64.SETLFS(1, drivenumber, 15)
|
||||||
void c64.OPEN()
|
void c64.OPEN()
|
||||||
@ -436,13 +478,11 @@ io_error:
|
|||||||
|
|
||||||
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
||||||
; -- rename a file on the drive
|
; -- rename a file on the drive
|
||||||
ubyte flen_old = string.length(oldfileptr)
|
|
||||||
ubyte flen_new = string.length(newfileptr)
|
|
||||||
filename[0] = 'r'
|
filename[0] = 'r'
|
||||||
filename[1] = ':'
|
filename[1] = ':'
|
||||||
sys.memcopy(newfileptr, &filename+2, flen_new)
|
ubyte flen_new = string.copy(newfileptr, &filename+2)
|
||||||
filename[flen_new+2] = '='
|
filename[flen_new+2] = '='
|
||||||
sys.memcopy(oldfileptr, &filename+3+flen_new, flen_old+1)
|
ubyte flen_old = string.copy(oldfileptr, &filename+3+flen_new)
|
||||||
c64.SETNAM(3+flen_new+flen_old, filename)
|
c64.SETNAM(3+flen_new+flen_old, filename)
|
||||||
c64.SETLFS(1, drivenumber, 15)
|
c64.SETLFS(1, drivenumber, 15)
|
||||||
void c64.OPEN()
|
void c64.OPEN()
|
||||||
|
@ -244,68 +244,45 @@ randseed .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
randbyte .proc
|
randbyte .proc
|
||||||
; -- 8-bit pseudo random number generator into A
|
; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
||||||
lda _seed
|
jmp randword
|
||||||
beq _eor
|
|
||||||
asl a
|
|
||||||
beq _done ; if the input was $80, skip the EOR
|
|
||||||
bcc _done
|
|
||||||
_eor eor #$1d ; xor with magic value see below for possible values
|
|
||||||
_done sta _seed
|
|
||||||
rts
|
|
||||||
|
|
||||||
_seed .byte $3a
|
|
||||||
|
|
||||||
; possible 'magic' eor bytes are:
|
|
||||||
; $1d, $2b, $2d, $4d, $5f, $63, $65, $69
|
|
||||||
; $71, $87, $8d, $a9, $c3, $cf, $e7, $f5
|
|
||||||
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
randword .proc
|
randword .proc
|
||||||
; -- 16 bit pseudo random number generator into AY
|
; -- 16 bit pseudo random number generator into AY
|
||||||
|
|
||||||
magic_eor = $3f1d
|
; rand64k ;Factors of 65535: 3 5 17 257
|
||||||
; possible magic eor words are:
|
lda sr1+1
|
||||||
; $3f1d, $3f81, $3fa5, $3fc5, $4075, $409d, $40cd, $4109
|
asl a
|
||||||
; $413f, $414b, $4153, $4159, $4193, $4199, $41af, $41bb
|
asl a
|
||||||
|
eor sr1+1
|
||||||
|
asl a
|
||||||
|
eor sr1+1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
eor sr1+1
|
||||||
|
asl a
|
||||||
|
rol sr1 ;shift this left, "random" bit comes from low
|
||||||
|
rol sr1+1
|
||||||
|
; rand32k ;Factors of 32767: 7 31 151 are independent and can be combined
|
||||||
|
lda sr2+1
|
||||||
|
asl a
|
||||||
|
eor sr2+1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
ror sr2 ;shift this right, random bit comes from high - nicer when eor with sr1
|
||||||
|
rol sr2+1
|
||||||
|
lda sr1+1 ;can be left out
|
||||||
|
eor sr2+1 ;if you dont use
|
||||||
|
tay ;y as suggested
|
||||||
|
lda sr1 ;mix up lowbytes of SR1
|
||||||
|
eor sr2 ;and SR2 to combine both
|
||||||
|
rts
|
||||||
|
|
||||||
lda _seed
|
sr1 .word $a55a
|
||||||
beq _lowZero ; $0000 and $8000 are special values to test for
|
sr2 .word $7653
|
||||||
|
|
||||||
; Do a normal shift
|
|
||||||
asl _seed
|
|
||||||
lda _seed+1
|
|
||||||
rol a
|
|
||||||
bcc _noEor
|
|
||||||
|
|
||||||
_doEor ; high byte is in A
|
|
||||||
eor #>magic_eor
|
|
||||||
sta _seed+1
|
|
||||||
lda _seed
|
|
||||||
eor #<magic_eor
|
|
||||||
sta _seed
|
|
||||||
ldy _seed+1
|
|
||||||
rts
|
|
||||||
|
|
||||||
_lowZero lda _seed+1
|
|
||||||
beq _doEor ; High byte is also zero, so apply the EOR
|
|
||||||
; For speed, you could store 'magic' into 'seed' directly
|
|
||||||
; instead of running the EORs
|
|
||||||
|
|
||||||
; wasn't zero, check for $8000
|
|
||||||
asl a
|
|
||||||
beq _noEor ; if $00 is left after the shift, then it was $80
|
|
||||||
bcs _doEor ; else, do the EOR based on the carry bit as usual
|
|
||||||
|
|
||||||
_noEor sta _seed+1
|
|
||||||
tay
|
|
||||||
lda _seed
|
|
||||||
rts
|
|
||||||
|
|
||||||
_seed .word $2c9e
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
@ -797,6 +774,13 @@ stack_mul_word_320 .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
stack_mul_word_640 .proc
|
||||||
|
; stackW = (stackLo * 2 * 320) (stackHi doesn't matter)
|
||||||
|
asl P8ESTACK_LO+1,x
|
||||||
|
jmp stack_mul_word_320
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
||||||
mul_byte_3 .proc
|
mul_byte_3 .proc
|
||||||
; A = A + A*2
|
; A = A + A*2
|
||||||
@ -1262,7 +1246,7 @@ mul_word_100 .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
mul_word_320 .proc
|
mul_word_320 .proc
|
||||||
; AY = A * 256 + A * 64 (msb doesn't matter)
|
; AY = A * 256 + A * 64 (msb in Y doesn't matter)
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
ldy #0
|
ldy #0
|
||||||
sty P8ZP_SCRATCH_REG
|
sty P8ZP_SCRATCH_REG
|
||||||
@ -1287,6 +1271,13 @@ mul_word_320 .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
mul_word_640 .proc
|
||||||
|
; AY = (A * 2 * 320) (msb in Y doesn't matter)
|
||||||
|
asl a
|
||||||
|
jmp mul_word_320
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
; ----------- end optimized multiplications -----------
|
; ----------- end optimized multiplications -----------
|
||||||
|
|
||||||
|
|
||||||
@ -1537,3 +1528,71 @@ _negative lsr a
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
square .proc
|
||||||
|
; -- calculate square root of signed word in AY, result in AY
|
||||||
|
; routine by Lee Davsion, source: http://6502.org/source/integers/square.htm
|
||||||
|
; using this routine is about twice as fast as doing a regular multiplication.
|
||||||
|
;
|
||||||
|
; Calculates the 16 bit unsigned integer square of the signed 16 bit integer in
|
||||||
|
; Numberl/Numberh. The result is always in the range 0 to 65025 and is held in
|
||||||
|
; Squarel/Squareh
|
||||||
|
;
|
||||||
|
; The maximum input range is only +/-255 and no checking is done to ensure that
|
||||||
|
; this is so.
|
||||||
|
;
|
||||||
|
; This routine is useful if you are trying to draw circles as for any circle
|
||||||
|
;
|
||||||
|
; x^2+y^2=r^2 where x and y are the co-ordinates of any point on the circle and
|
||||||
|
; r is the circle radius
|
||||||
|
|
||||||
|
numberl = P8ZP_SCRATCH_W1 ; number to square low byte
|
||||||
|
numberh = P8ZP_SCRATCH_W1+1 ; number to square high byte
|
||||||
|
squarel = P8ZP_SCRATCH_W2 ; square low byte
|
||||||
|
squareh = P8ZP_SCRATCH_W2+1 ; square high byte
|
||||||
|
tempsq = P8ZP_SCRATCH_B1 ; temp byte for intermediate result
|
||||||
|
|
||||||
|
sta numberl
|
||||||
|
sty numberh
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
|
||||||
|
lda #$00 ; clear a
|
||||||
|
sta squarel ; clear square low byte
|
||||||
|
; (no need to clear the high byte, it gets shifted out)
|
||||||
|
lda numberl ; get number low byte
|
||||||
|
ldx numberh ; get number high byte
|
||||||
|
bpl _nonneg ; if +ve don't negate it
|
||||||
|
; else do a two's complement
|
||||||
|
eor #$ff ; invert
|
||||||
|
sec ; +1
|
||||||
|
adc #$00 ; and add it
|
||||||
|
|
||||||
|
_nonneg:
|
||||||
|
sta tempsq ; save abs(number)
|
||||||
|
ldx #$08 ; set bit count
|
||||||
|
|
||||||
|
_nextr2bit:
|
||||||
|
asl squarel ; low byte *2
|
||||||
|
rol squareh ; high byte *2+carry from low
|
||||||
|
asl a ; shift number byte
|
||||||
|
bcc _nosqadd ; don't do add if c = 0
|
||||||
|
tay ; save a
|
||||||
|
clc ; clear carry for add
|
||||||
|
lda tempsq ; get number
|
||||||
|
adc squarel ; add number^2 low byte
|
||||||
|
sta squarel ; save number^2 low byte
|
||||||
|
lda #$00 ; clear a
|
||||||
|
adc squareh ; add number^2 high byte
|
||||||
|
sta squareh ; save number^2 high byte
|
||||||
|
tya ; get a back
|
||||||
|
|
||||||
|
_nosqadd:
|
||||||
|
dex ; decrement bit count
|
||||||
|
bne _nextr2bit ; go do next bit
|
||||||
|
|
||||||
|
lda squarel
|
||||||
|
ldy squareh
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
|
||||||
|
.pend
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
|
||||||
math {
|
math {
|
||||||
%asminclude "library:math.asm", ""
|
%asminclude "library:math.asm"
|
||||||
}
|
}
|
||||||
|
@ -432,6 +432,7 @@ func_min_ub_stack .proc
|
|||||||
func_min_b_into_A .proc
|
func_min_b_into_A .proc
|
||||||
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
|
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
|
||||||
tay
|
tay
|
||||||
|
dey
|
||||||
lda #127
|
lda #127
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
@ -548,6 +549,7 @@ func_min_w_stack .proc
|
|||||||
func_max_ub_into_A .proc
|
func_max_ub_into_A .proc
|
||||||
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
|
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
|
||||||
tay
|
tay
|
||||||
|
dey
|
||||||
lda #0
|
lda #0
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
@ -1078,3 +1080,28 @@ _loop_hi ldy _index_first
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
func_peekw .proc
|
||||||
|
; -- read the word value on the address in AY
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
|
pha
|
||||||
|
iny
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
func_pokew .proc
|
||||||
|
; -- store the word value in AY in the address in P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_REG
|
||||||
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
|
iny
|
||||||
|
lda P8ZP_SCRATCH_REG
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
@ -1071,3 +1071,15 @@ sign_extend_AY_byte .proc
|
|||||||
pla
|
pla
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
strlen .proc
|
||||||
|
; -- returns the number of bytes in the string in AY, in Y.
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
beq +
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ rts
|
||||||
|
.pend
|
||||||
|
@ -3,13 +3,15 @@
|
|||||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
|
||||||
prog8_lib {
|
prog8_lib {
|
||||||
%asminclude "library:prog8_lib.asm", ""
|
%asminclude "library:prog8_lib.asm"
|
||||||
%asminclude "library:prog8_funcs.asm", ""
|
%asminclude "library:prog8_funcs.asm"
|
||||||
|
|
||||||
|
; TODO these retval variables are no longer used???
|
||||||
uword @zp retval_interm_uw ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
uword @zp retval_interm_uw ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
word @zp retval_interm_w ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
word @zp retval_interm_w ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
ubyte @zp retval_interm_ub ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
ubyte @zp retval_interm_ub ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
byte @zp retval_interm_b ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
byte @zp retval_interm_b ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
|
; NOTE: these variables are checked in the StatementReorderer (in fun after(decl: VarDecl)), for these exact names!
|
||||||
|
|
||||||
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -178,7 +178,7 @@ _found sty P8ZP_SCRATCH_B1
|
|||||||
|
|
||||||
asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A {
|
asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A {
|
||||||
; Compares two strings for sorting.
|
; Compares two strings for sorting.
|
||||||
; Returns -1 (255), 0 or 1 depeding on wether string1 sorts before, equal or after string2.
|
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2.
|
||||||
; Note that you can also directly compare strings and string values with eachother using
|
; Note that you can also directly compare strings and string values with eachother using
|
||||||
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
|
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -190,8 +190,8 @@ _found sty P8ZP_SCRATCH_B1
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub lower(uword st @AY) {
|
asmsub lower(uword st @AY) -> ubyte @Y {
|
||||||
; Lowercases the petscii string in-place.
|
; Lowercases the petscii string in-place. Returns length of the string.
|
||||||
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
||||||
; but regular text doesn't usually contain those characters anyway.)
|
; but regular text doesn't usually contain those characters anyway.)
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -213,8 +213,8 @@ _done rts
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub upper(uword st @AY) {
|
asmsub upper(uword st @AY) -> ubyte @Y {
|
||||||
; Uppercases the petscii string in-place.
|
; Uppercases the petscii string in-place. Returns length of the string.
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
@ -1 +1 @@
|
|||||||
6.0-BETA
|
7.2
|
||||||
|
@ -6,8 +6,7 @@ import prog8.compiler.CompilationResult
|
|||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.CompilationTarget
|
import java.io.File
|
||||||
import prog8.parser.ParsingFailedError
|
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardWatchEventKinds
|
import java.nio.file.StandardWatchEventKinds
|
||||||
@ -16,45 +15,57 @@ import kotlin.system.exitProcess
|
|||||||
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
printSoftwareHeader("compiler")
|
|
||||||
|
|
||||||
compileMain(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun printSoftwareHeader(what: String) {
|
|
||||||
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
|
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
|
||||||
println("\nProg8 $what v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
||||||
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
||||||
|
|
||||||
|
val succes = compileMain(args)
|
||||||
|
if(!succes)
|
||||||
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
|
fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
|
||||||
|
|
||||||
|
|
||||||
private fun compileMain(args: Array<String>) {
|
private fun compileMain(args: Array<String>): Boolean {
|
||||||
val cli = ArgParser("prog8compiler")
|
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
|
||||||
val startEmulator by cli.option(ArgType.Boolean, shortName="emu", description = "auto-start emulator after successful compilation")
|
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
||||||
val outputDir by cli.option(ArgType.String, shortName = "out", description = "directory for output files instead of current directory").default(".")
|
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
|
||||||
val dontWriteAssembly by cli.option(ArgType.Boolean, shortName = "noasm", description="don't create assembly code")
|
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||||
val dontOptimize by cli.option(ArgType.Boolean, shortName = "noopt", description = "don't perform any optimizations")
|
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||||
val watchMode by cli.option(ArgType.Boolean, shortName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
|
||||||
val slowCodegenWarnings by cli.option(ArgType.Boolean, shortName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
|
||||||
val compilationTarget by cli.option(ArgType.String, shortName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
|
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
||||||
|
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
||||||
|
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||||
|
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
|
||||||
|
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cli.parse(args)
|
cli.parse(args)
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
System.err.println(e.message)
|
System.err.println(e.message)
|
||||||
exitProcess(1)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val outputPath = pathFrom(outputDir)
|
val outputPath = pathFrom(outputDir)
|
||||||
if(!outputPath.toFile().isDirectory) {
|
if(!outputPath.toFile().isDirectory) {
|
||||||
System.err.println("Output path doesn't exist")
|
System.err.println("Output path doesn't exist")
|
||||||
exitProcess(1)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val faultyOption = moduleFiles.firstOrNull { it.startsWith('-') }
|
||||||
|
if(faultyOption!=null) {
|
||||||
|
System.err.println("Unknown command line option given: $faultyOption")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val srcdirs = sourceDirs.toMutableList()
|
||||||
|
if(srcdirs.firstOrNull()!=".")
|
||||||
|
srcdirs.add(0, ".")
|
||||||
|
|
||||||
if(watchMode==true) {
|
if(watchMode==true) {
|
||||||
val watchservice = FileSystems.getDefault().newWatchService()
|
val watchservice = FileSystems.getDefault().newWatchService()
|
||||||
val allImportedFiles = mutableSetOf<Path>()
|
val allImportedFiles = mutableSetOf<Path>()
|
||||||
@ -64,7 +75,10 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val results = mutableListOf<CompilationResult>()
|
val results = mutableListOf<CompilationResult>()
|
||||||
for(filepathRaw in moduleFiles) {
|
for(filepathRaw in moduleFiles) {
|
||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, outputPath)
|
val compilationResult = compileProgram(filepath,
|
||||||
|
dontOptimize!=true, optimizeFloatExpressions==true,
|
||||||
|
dontWriteAssembly!=true, slowCodegenWarnings==true, quietAssembler==true,
|
||||||
|
compilationTarget, srcdirs, outputPath)
|
||||||
results.add(compilationResult)
|
results.add(compilationResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +89,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
for (importedFile in allImportedFiles) {
|
for (importedFile in allImportedFiles) {
|
||||||
print(" ")
|
print(" ")
|
||||||
println(importedFile)
|
println(importedFile)
|
||||||
val watchDir = importedFile.parent ?: Path.of(".")
|
val watchDir = importedFile.parent ?: Path.of("")
|
||||||
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
|
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
|
||||||
}
|
}
|
||||||
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
|
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
|
||||||
@ -101,22 +115,31 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult: CompilationResult
|
val compilationResult: CompilationResult
|
||||||
try {
|
try {
|
||||||
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, outputPath)
|
compilationResult = compileProgram(filepath,
|
||||||
|
dontOptimize!=true, optimizeFloatExpressions==true,
|
||||||
|
dontWriteAssembly!=true, slowCodegenWarnings==true, quietAssembler==true,
|
||||||
|
compilationTarget, srcdirs, outputPath)
|
||||||
if(!compilationResult.success)
|
if(!compilationResult.success)
|
||||||
exitProcess(1)
|
return false
|
||||||
} catch (x: ParsingFailedError) {
|
|
||||||
exitProcess(1)
|
|
||||||
} catch (x: AstException) {
|
} catch (x: AstException) {
|
||||||
exitProcess(1)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startEmulator==true) {
|
if(startEmulator1==true || startEmulator2==true) {
|
||||||
if (compilationResult.programName.isEmpty())
|
if (compilationResult.programName.isEmpty()) {
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
else {
|
return true
|
||||||
CompilationTarget.instance.machine.launchEmulator(compilationResult.programName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val programNameInPath = outputPath.resolve(compilationResult.programName)
|
||||||
|
|
||||||
|
if (startEmulator1==true)
|
||||||
|
compilationResult.compTarget.machine.launchEmulator(1, programNameInPath)
|
||||||
|
else if (startEmulator2==true)
|
||||||
|
compilationResult.compTarget.machine.launchEmulator(2, programNameInPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1,382 +0,0 @@
|
|||||||
package prog8.ast
|
|
||||||
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.Expression
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.processing.AstWalker
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
interface Node {
|
|
||||||
val position: Position
|
|
||||||
var parent: Node // will be linked correctly later (late init)
|
|
||||||
fun linkParents(parent: Node)
|
|
||||||
|
|
||||||
fun definingModule(): Module {
|
|
||||||
if(this is Module)
|
|
||||||
return this
|
|
||||||
return findParentNode<Module>(this)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun definingSubroutine(): Subroutine? = findParentNode<Subroutine>(this)
|
|
||||||
|
|
||||||
fun definingScope(): INameScope {
|
|
||||||
val scope = findParentNode<INameScope>(this)
|
|
||||||
if(scope!=null) {
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
if(this is Label && this.name.startsWith("builtin::")) {
|
|
||||||
return BuiltinFunctionScopePlaceholder
|
|
||||||
}
|
|
||||||
if(this is GlobalNamespace)
|
|
||||||
return this
|
|
||||||
throw FatalAstException("scope missing from $this")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun definingBlock(): Block {
|
|
||||||
if(this is Block)
|
|
||||||
return this
|
|
||||||
return findParentNode<Block>(this)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun containingStatement(): Statement {
|
|
||||||
if(this is Statement)
|
|
||||||
return this
|
|
||||||
return findParentNode<Statement>(this)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun replaceChildNode(node: Node, replacement: Node)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFunctionCall {
|
|
||||||
var target: IdentifierReference
|
|
||||||
var args: MutableList<Expression>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface INameScope {
|
|
||||||
val name: String
|
|
||||||
val position: Position
|
|
||||||
val statements: MutableList<Statement>
|
|
||||||
val parent: Node
|
|
||||||
|
|
||||||
fun linkParents(parent: Node)
|
|
||||||
|
|
||||||
fun subScope(name: String): INameScope? {
|
|
||||||
for(stmt in statements) {
|
|
||||||
when(stmt) {
|
|
||||||
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
|
||||||
is ForLoop -> if(stmt.body.name==name) return stmt.body
|
|
||||||
is UntilLoop -> if(stmt.body.name==name) return stmt.body
|
|
||||||
is WhileLoop -> if(stmt.body.name==name) return stmt.body
|
|
||||||
is BranchStatement -> {
|
|
||||||
if(stmt.truepart.name==name) return stmt.truepart
|
|
||||||
if(stmt.elsepart.containsCodeOrVars() && stmt.elsepart.name==name) return stmt.elsepart
|
|
||||||
}
|
|
||||||
is IfStatement -> {
|
|
||||||
if(stmt.truepart.name==name) return stmt.truepart
|
|
||||||
if(stmt.elsepart.containsCodeOrVars() && stmt.elsepart.name==name) return stmt.elsepart
|
|
||||||
}
|
|
||||||
is WhenStatement -> {
|
|
||||||
val scope = stmt.choices.firstOrNull { it.statements.name==name }
|
|
||||||
if(scope!=null)
|
|
||||||
return scope.statements
|
|
||||||
}
|
|
||||||
is INameScope -> if(stmt.name==name) return stmt
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLabelOrVariable(name: String): Statement? {
|
|
||||||
// this is called A LOT and could perhaps be optimized a bit more,
|
|
||||||
// but adding a memoization cache didn't make much of a practical runtime difference
|
|
||||||
for (stmt in statements) {
|
|
||||||
if (stmt is VarDecl && stmt.name==name) return stmt
|
|
||||||
if (stmt is Label && stmt.name==name) return stmt
|
|
||||||
if (stmt is AnonymousScope) {
|
|
||||||
val sub = stmt.getLabelOrVariable(name)
|
|
||||||
if(sub!=null)
|
|
||||||
return sub
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun allDefinedSymbols(): List<Pair<String, Statement>> {
|
|
||||||
return statements.mapNotNull {
|
|
||||||
when (it) {
|
|
||||||
is Label -> it.name to it
|
|
||||||
is VarDecl -> it.name to it
|
|
||||||
is Subroutine -> it.name to it
|
|
||||||
is Block -> it.name to it
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lookup(scopedName: List<String>, localContext: Node) : Statement? {
|
|
||||||
if(scopedName.size>1) {
|
|
||||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
|
||||||
// try the struct first.
|
|
||||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
|
||||||
val struct = thing?.struct
|
|
||||||
if (struct != null) {
|
|
||||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
|
||||||
// return ref to the mangled name variable
|
|
||||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
|
||||||
return thing.definingScope().getLabelOrVariable(mangled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
|
||||||
for(module in localContext.definingModule().program.modules) {
|
|
||||||
var scope: INameScope? = module
|
|
||||||
for(name in scopedName.dropLast(1)) {
|
|
||||||
scope = scope?.subScope(name)
|
|
||||||
if(scope==null)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if(scope!=null) {
|
|
||||||
val result = scope.getLabelOrVariable(scopedName.last())
|
|
||||||
if(result!=null)
|
|
||||||
return result
|
|
||||||
return scope.subScope(scopedName.last()) as Statement?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
} else {
|
|
||||||
// unqualified name
|
|
||||||
// special case: the do....until statement can also look INSIDE the anonymous scope
|
|
||||||
if(localContext.parent.parent is UntilLoop) {
|
|
||||||
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.getLabelOrVariable(scopedName[0])
|
|
||||||
if(symbolFromInnerScope!=null)
|
|
||||||
return symbolFromInnerScope
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the scope the localContext is in, look in that first
|
|
||||||
var statementScope = localContext
|
|
||||||
while(statementScope !is ParentSentinel) {
|
|
||||||
val localScope = statementScope.definingScope()
|
|
||||||
val result = localScope.getLabelOrVariable(scopedName[0])
|
|
||||||
if (result != null)
|
|
||||||
return result
|
|
||||||
val subscope = localScope.subScope(scopedName[0]) as Statement?
|
|
||||||
if (subscope != null)
|
|
||||||
return subscope
|
|
||||||
// not found in this scope, look one higher up
|
|
||||||
statementScope = statementScope.parent
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun containsDefinedVariables() = statements.any { it is VarDecl && (it !is ParameterVarDecl) }
|
|
||||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
|
||||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
|
||||||
|
|
||||||
fun remove(stmt: Statement) {
|
|
||||||
if(!statements.remove(stmt))
|
|
||||||
throw FatalAstException("stmt to remove wasn't found in scope")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAllLabels(label: String): List<Label> {
|
|
||||||
val result = mutableListOf<Label>()
|
|
||||||
|
|
||||||
fun find(scope: INameScope) {
|
|
||||||
scope.statements.forEach {
|
|
||||||
when(it) {
|
|
||||||
is Label -> result.add(it)
|
|
||||||
is INameScope -> find(it)
|
|
||||||
is IfStatement -> {
|
|
||||||
find(it.truepart)
|
|
||||||
find(it.elsepart)
|
|
||||||
}
|
|
||||||
is UntilLoop -> find(it.body)
|
|
||||||
is RepeatLoop -> find(it.body)
|
|
||||||
is WhileLoop -> find(it.body)
|
|
||||||
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
|
|
||||||
else -> { /* do nothing */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
find(this)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun nextSibling(stmt: Statement): Statement? {
|
|
||||||
val nextIdx = statements.indexOfFirst { it===stmt } + 1
|
|
||||||
return if(nextIdx < statements.size)
|
|
||||||
statements[nextIdx]
|
|
||||||
else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun previousSibling(stmt: Statement): Statement? {
|
|
||||||
val previousIdx = statements.indexOfFirst { it===stmt } - 1
|
|
||||||
return if(previousIdx>=0)
|
|
||||||
statements[previousIdx]
|
|
||||||
else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun indexOfChild(stmt: Statement): Int {
|
|
||||||
val idx = statements.indexOfFirst { it===stmt }
|
|
||||||
if(idx>=0)
|
|
||||||
return idx
|
|
||||||
else
|
|
||||||
throw FatalAstException("attempt to find a non-child")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IAssignable {
|
|
||||||
// just a tag for now
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
|
||||||
|
|
||||||
class Program(val name: String, val modules: MutableList<Module>): Node {
|
|
||||||
val namespace = GlobalNamespace(modules)
|
|
||||||
|
|
||||||
val definedLoadAddress: Int
|
|
||||||
get() = modules.first().loadAddress
|
|
||||||
|
|
||||||
var actualLoadAddress: Int = 0
|
|
||||||
|
|
||||||
fun entrypoint(): Subroutine? {
|
|
||||||
val mainBlocks = allBlocks().filter { it.name=="main" }
|
|
||||||
if(mainBlocks.size > 1)
|
|
||||||
throw FatalAstException("more than one 'main' block")
|
|
||||||
return if(mainBlocks.isEmpty()) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
mainBlocks[0].subScope("start") as Subroutine?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
|
||||||
|
|
||||||
override val position: Position = Position.DUMMY
|
|
||||||
override var parent: Node
|
|
||||||
get() = throw FatalAstException("program has no parent")
|
|
||||||
set(_) = throw FatalAstException("can't set parent of program")
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
modules.forEach {
|
|
||||||
it.linkParents(namespace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
require(node is Module && replacement is Module)
|
|
||||||
val idx = modules.indexOfFirst { it===node }
|
|
||||||
modules[idx] = replacement
|
|
||||||
replacement.parent = this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Module(override val name: String,
|
|
||||||
override var statements: MutableList<Statement>,
|
|
||||||
override val position: Position,
|
|
||||||
val isLibraryModule: Boolean,
|
|
||||||
val source: Path) : Node, INameScope {
|
|
||||||
|
|
||||||
override lateinit var parent: Node
|
|
||||||
lateinit var program: Program
|
|
||||||
val importedBy = mutableListOf<Module>()
|
|
||||||
val imports = mutableSetOf<Module>()
|
|
||||||
|
|
||||||
val loadAddress: Int by lazy {
|
|
||||||
val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0
|
|
||||||
address
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent = parent
|
|
||||||
statements.forEach {it.linkParents(this)}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun definingScope(): INameScope = program.namespace
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
require(node is Statement && replacement is Statement)
|
|
||||||
val idx = statements.indexOfFirst { it===node }
|
|
||||||
statements[idx] = replacement
|
|
||||||
replacement.parent = this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|
||||||
override val name = "<<<global>>>"
|
|
||||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
|
||||||
override val statements = mutableListOf<Statement>() // not used
|
|
||||||
override var parent: Node = ParentSentinel
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
modules.forEach { it.linkParents(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
throw FatalAstException("cannot replace anything in the namespace")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>, localContext: Node): Statement? {
|
|
||||||
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) {
|
|
||||||
// builtin functions always exist, return a dummy localContext for them
|
|
||||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
|
|
||||||
builtinPlaceholder.parent = ParentSentinel
|
|
||||||
return builtinPlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
if(scopedName.size>1) {
|
|
||||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
|
||||||
// try the struct first.
|
|
||||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
|
||||||
val struct = thing?.struct
|
|
||||||
if (struct != null) {
|
|
||||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
|
||||||
// return ref to the mangled name variable
|
|
||||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
|
||||||
return thing.definingScope().getLabelOrVariable(mangled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// special case: the do....until statement can also look INSIDE the anonymous scope
|
|
||||||
if(localContext.parent.parent is UntilLoop) {
|
|
||||||
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.lookup(scopedName, localContext)
|
|
||||||
if(symbolFromInnerScope!=null)
|
|
||||||
return symbolFromInnerScope
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup something from the module.
|
|
||||||
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
|
||||||
is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt
|
|
||||||
null -> null
|
|
||||||
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object BuiltinFunctionScopePlaceholder : INameScope {
|
|
||||||
override val name = "<<builtin-functions-scope-placeholder>>"
|
|
||||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
|
||||||
override var statements = mutableListOf<Statement>()
|
|
||||||
override var parent: Node = ParentSentinel
|
|
||||||
override fun linkParents(parent: Node) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// prefix for struct member variables
|
|
||||||
internal fun mangledStructMemberName(varName: String, memberName: String) = "prog8struct_${varName}_$memberName"
|
|
@ -1,44 +0,0 @@
|
|||||||
package prog8.ast.base
|
|
||||||
|
|
||||||
import prog8.parser.ParsingFailedError
|
|
||||||
|
|
||||||
|
|
||||||
class ErrorReporter {
|
|
||||||
private enum class MessageSeverity {
|
|
||||||
WARNING,
|
|
||||||
ERROR
|
|
||||||
}
|
|
||||||
private class CompilerMessage(val severity: MessageSeverity, val message: String, val position: Position)
|
|
||||||
|
|
||||||
private val messages = mutableListOf<CompilerMessage>()
|
|
||||||
private val alreadyReportedMessages = mutableSetOf<String>()
|
|
||||||
|
|
||||||
fun err(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position))
|
|
||||||
fun warn(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
|
|
||||||
|
|
||||||
fun handle() {
|
|
||||||
var numErrors = 0
|
|
||||||
var numWarnings = 0
|
|
||||||
messages.forEach {
|
|
||||||
when(it.severity) {
|
|
||||||
MessageSeverity.ERROR -> System.err.print("\u001b[91m") // bright red
|
|
||||||
MessageSeverity.WARNING -> System.err.print("\u001b[93m") // bright yellow
|
|
||||||
}
|
|
||||||
val msg = "${it.position.toClickableStr()} ${it.severity} ${it.message}".trim()
|
|
||||||
if(msg !in alreadyReportedMessages) {
|
|
||||||
System.err.println(msg)
|
|
||||||
alreadyReportedMessages.add(msg)
|
|
||||||
when(it.severity) {
|
|
||||||
MessageSeverity.WARNING -> numWarnings++
|
|
||||||
MessageSeverity.ERROR -> numErrors++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.err.print("\u001b[0m") // reset color
|
|
||||||
}
|
|
||||||
messages.clear()
|
|
||||||
if(numErrors>0)
|
|
||||||
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isEmpty() = messages.isEmpty()
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
package prog8.ast.base
|
|
||||||
|
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.processing.*
|
|
||||||
import prog8.ast.statements.Directive
|
|
||||||
import prog8.compiler.CompilationOptions
|
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
|
||||||
val checker = AstChecker(this, compilerOptions, errors)
|
|
||||||
checker.visit(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter) {
|
|
||||||
val fixer = BeforeAsmGenerationAstChanger(this, errors)
|
|
||||||
fixer.visit(this)
|
|
||||||
fixer.applyModifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.reorderStatements(errors: ErrorReporter) {
|
|
||||||
val reorder = StatementReorderer(this, errors)
|
|
||||||
reorder.visit(this)
|
|
||||||
reorder.applyModifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.addTypecasts(errors: ErrorReporter) {
|
|
||||||
val caster = TypecastsAdder(this, errors)
|
|
||||||
caster.visit(this)
|
|
||||||
caster.applyModifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.verifyFunctionArgTypes() {
|
|
||||||
val fixer = VerifyFunctionArgTypes(this)
|
|
||||||
fixer.visit(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Module.checkImportedValid() {
|
|
||||||
val imr = ImportedModuleDirectiveRemover()
|
|
||||||
imr.visit(this, this.parent)
|
|
||||||
imr.applyModifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
|
||||||
|
|
||||||
val checker2 = AstIdentifiersChecker(this, errors)
|
|
||||||
checker2.visit(this)
|
|
||||||
|
|
||||||
if(errors.isEmpty()) {
|
|
||||||
val transforms = AstVariousTransforms(this)
|
|
||||||
transforms.visit(this)
|
|
||||||
transforms.applyModifications()
|
|
||||||
val lit2decl = LiteralsToAutoVars(this)
|
|
||||||
lit2decl.visit(this)
|
|
||||||
lit2decl.applyModifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modules.map { it.name }.toSet().size != modules.size) {
|
|
||||||
throw FatalAstException("modules should all be unique")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.variousCleanups() {
|
|
||||||
val process = VariousCleanups()
|
|
||||||
process.visit(this)
|
|
||||||
process.applyModifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.moveMainAndStartToFirst() {
|
|
||||||
// the module containing the program entrypoint is moved to the first in the sequence.
|
|
||||||
// the "main" block containing the entrypoint is moved to the top in there,
|
|
||||||
// and finally the entrypoint subroutine "start" itself is moved to the top in that block.
|
|
||||||
|
|
||||||
val directives = modules[0].statements.filterIsInstance<Directive>()
|
|
||||||
val start = this.entrypoint()
|
|
||||||
if(start!=null) {
|
|
||||||
val mod = start.definingModule()
|
|
||||||
val block = start.definingBlock()
|
|
||||||
if(!modules.remove(mod))
|
|
||||||
throw FatalAstException("module wrong")
|
|
||||||
modules.add(0, mod)
|
|
||||||
mod.remove(block)
|
|
||||||
var afterDirective = mod.statements.indexOfFirst { it !is Directive }
|
|
||||||
if(afterDirective<0)
|
|
||||||
mod.statements.add(block)
|
|
||||||
else
|
|
||||||
mod.statements.add(afterDirective, block)
|
|
||||||
block.remove(start)
|
|
||||||
afterDirective = block.statements.indexOfFirst { it !is Directive }
|
|
||||||
if(afterDirective<0)
|
|
||||||
block.statements.add(start)
|
|
||||||
else
|
|
||||||
block.statements.add(afterDirective, start)
|
|
||||||
|
|
||||||
// overwrite the directives in the module containing the entrypoint
|
|
||||||
for(directive in directives) {
|
|
||||||
modules[0].statements.removeAll { it is Directive && it.directive == directive.directive }
|
|
||||||
modules[0].statements.add(0, directive)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.compiler.target.CompilationTarget
|
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
|
|
||||||
internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter) : IAstVisitor {
|
|
||||||
private var blocks = mutableMapOf<String, Block>()
|
|
||||||
|
|
||||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
|
||||||
errors.err("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
|
||||||
blocks.clear() // blocks may be redefined within a different module
|
|
||||||
|
|
||||||
super.visit(module)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(block: Block) {
|
|
||||||
if(block.name in CompilationTarget.instance.machine.opcodeNames)
|
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${block.name}'", block.position)
|
|
||||||
|
|
||||||
val existing = blocks[block.name]
|
|
||||||
if(existing!=null)
|
|
||||||
nameError(block.name, block.position, existing)
|
|
||||||
else
|
|
||||||
blocks[block.name] = block
|
|
||||||
|
|
||||||
if(!block.isInLibrary) {
|
|
||||||
val libraries = program.modules.filter { it.isLibraryModule }
|
|
||||||
val libraryBlockNames = libraries.flatMap { it.statements.filterIsInstance<Block>().map { b -> b.name } }
|
|
||||||
if(block.name in libraryBlockNames)
|
|
||||||
errors.err("block is already defined in an included library module", block.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(directive: Directive) {
|
|
||||||
if(directive.directive=="%target") {
|
|
||||||
val compatibleTarget = directive.args.single().name
|
|
||||||
if (compatibleTarget != CompilationTarget.instance.name)
|
|
||||||
errors.err("module's compilation target ($compatibleTarget) differs from active target (${CompilationTarget.instance.name})", directive.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(directive)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
|
||||||
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
|
||||||
|
|
||||||
if(decl.name in BuiltinFunctions)
|
|
||||||
errors.err("builtin function cannot be redefined", decl.position)
|
|
||||||
|
|
||||||
if(decl.name in CompilationTarget.instance.machine.opcodeNames)
|
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
|
||||||
|
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
|
||||||
if (decl.structHasBeenFlattened)
|
|
||||||
return super.visit(decl) // don't do this multiple times
|
|
||||||
|
|
||||||
if (decl.struct == null) {
|
|
||||||
errors.err("undefined struct type", decl.position)
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes })
|
|
||||||
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
|
||||||
|
|
||||||
if (decl.value is NumericLiteralValue) {
|
|
||||||
errors.err("you cannot initialize a struct using a single value", decl.position)
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decl.value != null && decl.value !is ArrayLiteralValue) {
|
|
||||||
errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
|
||||||
if (existing != null && existing !== decl)
|
|
||||||
nameError(decl.name, decl.position, existing)
|
|
||||||
|
|
||||||
if(decl.definingBlock().name==decl.name)
|
|
||||||
nameError(decl.name, decl.position, decl.definingBlock())
|
|
||||||
if(decl.definingSubroutine()?.name==decl.name)
|
|
||||||
nameError(decl.name, decl.position, decl.definingSubroutine()!!)
|
|
||||||
|
|
||||||
super.visit(decl)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
|
||||||
if(subroutine.name in CompilationTarget.instance.machine.opcodeNames) {
|
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
|
|
||||||
} else if(subroutine.name in BuiltinFunctions) {
|
|
||||||
// the builtin functions can't be redefined
|
|
||||||
errors.err("builtin function cannot be redefined", subroutine.position)
|
|
||||||
} else {
|
|
||||||
// already reported elsewhere:
|
|
||||||
// if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
|
||||||
// checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
|
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(subroutine.name), subroutine)
|
|
||||||
if (existing != null && existing !== subroutine)
|
|
||||||
nameError(subroutine.name, subroutine.position, existing)
|
|
||||||
|
|
||||||
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters
|
|
||||||
val symbolsInSub = subroutine.allDefinedSymbols()
|
|
||||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
|
||||||
val paramNames = subroutine.parameters.map { it.name }.toSet()
|
|
||||||
val paramsToCheck = paramNames.intersect(namesInSub)
|
|
||||||
for(name in paramsToCheck) {
|
|
||||||
val labelOrVar = subroutine.getLabelOrVariable(name)
|
|
||||||
if(labelOrVar!=null && labelOrVar.position != subroutine.position)
|
|
||||||
nameError(name, labelOrVar.position, subroutine)
|
|
||||||
val sub = subroutine.statements.firstOrNull { it is Subroutine && it.name==name}
|
|
||||||
if(sub!=null)
|
|
||||||
nameError(name, subroutine.position, sub)
|
|
||||||
val block = program.allBlocks().firstOrNull { it.name==name }
|
|
||||||
if(block!=null)
|
|
||||||
nameError(name, subroutine.position, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
|
||||||
errors.err("asmsub can only contain inline assembly (%asm)", subroutine.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(subroutine)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(label: Label) {
|
|
||||||
if(label.name in CompilationTarget.instance.machine.opcodeNames)
|
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
|
|
||||||
|
|
||||||
if(label.name in BuiltinFunctions) {
|
|
||||||
// the builtin functions can't be redefined
|
|
||||||
errors.err("builtin function cannot be redefined", label.position)
|
|
||||||
} else {
|
|
||||||
val existing = label.definingSubroutine()?.getAllLabels(label.name) ?: emptyList()
|
|
||||||
for(el in existing) {
|
|
||||||
if(el === label || el.name != label.name)
|
|
||||||
continue
|
|
||||||
else {
|
|
||||||
nameError(label.name, label.position, el)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(label)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(string: StringLiteralValue) {
|
|
||||||
if (string.value.length > 255)
|
|
||||||
errors.err("string literal length max is 255", string.position)
|
|
||||||
|
|
||||||
super.visit(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl) {
|
|
||||||
for(member in structDecl.statements){
|
|
||||||
val decl = member as? VarDecl
|
|
||||||
if(decl!=null && decl.datatype !in NumericDatatypes)
|
|
||||||
errors.err("structs can only contain numerical types", decl.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(structDecl)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.statements.Directive
|
|
||||||
|
|
||||||
|
|
||||||
internal class ImportedModuleDirectiveRemover: AstWalker() {
|
|
||||||
/**
|
|
||||||
* Most global directives don't apply for imported modules, so remove them
|
|
||||||
*/
|
|
||||||
|
|
||||||
private val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(directive.directive in moduleLevelDirectives) {
|
|
||||||
return listOf(IAstModification.Remove(directive, parent as INameScope))
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,416 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.*
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
|
|
||||||
|
|
||||||
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
|
||||||
// Reorders the statements in a way the compiler needs.
|
|
||||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
|
||||||
// - library blocks are put last.
|
|
||||||
// - blocks are ordered by address, where blocks without address are placed last.
|
|
||||||
// - in every block and module, most directives and vardecls are moved to the top. (not in subroutines!)
|
|
||||||
// - the 'start' subroutine is moved to the top.
|
|
||||||
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
|
||||||
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
|
||||||
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
|
|
||||||
// - sorts the choices in when statement.
|
|
||||||
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
|
||||||
|
|
||||||
override fun after(module: Module, parent: Node): Iterable<IAstModification> {
|
|
||||||
val (blocks, other) = module.statements.partition { it is Block }
|
|
||||||
module.statements = other.asSequence().plus(blocks.sortedBy { (it as Block).address ?: Int.MAX_VALUE }).toMutableList()
|
|
||||||
|
|
||||||
val mainBlock = module.statements.filterIsInstance<Block>().firstOrNull { it.name=="main" }
|
|
||||||
if(mainBlock!=null && mainBlock.address==null) {
|
|
||||||
module.statements.remove(mainBlock)
|
|
||||||
module.statements.add(0, mainBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(module.statements)
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reorderVardeclsAndDirectives(statements: MutableList<Statement>) {
|
|
||||||
val varDecls = statements.filterIsInstance<VarDecl>()
|
|
||||||
statements.removeAll(varDecls)
|
|
||||||
statements.addAll(0, varDecls)
|
|
||||||
|
|
||||||
val directives = statements.filterIsInstance<Directive>().filter {it.directive in directivesToMove}
|
|
||||||
statements.removeAll(directives)
|
|
||||||
statements.addAll(0, directives)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(block: Block, parent: Node): Iterable<IAstModification> {
|
|
||||||
parent as Module
|
|
||||||
if(block.isInLibrary) {
|
|
||||||
return listOf(
|
|
||||||
IAstModification.Remove(block, parent),
|
|
||||||
IAstModification.InsertLast(block, parent)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(block.statements)
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(subroutine.name=="start" && parent is Block) {
|
|
||||||
if(parent.statements.filterIsInstance<Subroutine>().first().name!="start") {
|
|
||||||
return listOf(
|
|
||||||
IAstModification.Remove(subroutine, parent),
|
|
||||||
IAstModification.InsertFirst(subroutine, parent)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val subs = subroutine.statements.filterIsInstance<Subroutine>()
|
|
||||||
if(subs.isNotEmpty()) {
|
|
||||||
// all subroutines defined within this subroutine are moved to the end
|
|
||||||
return subs.map { IAstModification.Remove(it, subroutine) } +
|
|
||||||
subs.map { IAstModification.InsertLast(it, subroutine) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
when (val expr2 = arrayIndexedExpression.indexer.origExpression) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
arrayIndexedExpression.indexer.indexNum = expr2
|
|
||||||
arrayIndexedExpression.indexer.origExpression = null
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
arrayIndexedExpression.indexer.indexVar = expr2
|
|
||||||
arrayIndexedExpression.indexer.origExpression = null
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
is Expression -> {
|
|
||||||
// replace complex indexing with a temp variable
|
|
||||||
return getAutoIndexerVarFor(arrayIndexedExpression)
|
|
||||||
}
|
|
||||||
else -> return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
// when using a simple bit shift and assigning it to a variable of a different type,
|
|
||||||
// try to make the bit shifting 'wide enough' to fall into the variable's type.
|
|
||||||
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
|
|
||||||
if(expr.operator=="<<" || expr.operator==">>") {
|
|
||||||
val leftDt = expr.left.inferType(program)
|
|
||||||
when (parent) {
|
|
||||||
is Assignment -> {
|
|
||||||
val targetDt = parent.target.inferType(program)
|
|
||||||
if(leftDt != targetDt) {
|
|
||||||
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.STRUCT), true, parent.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is VarDecl -> {
|
|
||||||
if(!leftDt.istype(parent.datatype)) {
|
|
||||||
val cast = TypecastExpression(expr.left, parent.datatype, true, parent.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IFunctionCall -> {
|
|
||||||
val argnum = parent.args.indexOf(expr)
|
|
||||||
when (val callee = parent.target.targetStatement(program.namespace)) {
|
|
||||||
is Subroutine -> {
|
|
||||||
val paramType = callee.parameters[argnum].type
|
|
||||||
if(leftDt isAssignableTo paramType) {
|
|
||||||
val cast = TypecastExpression(expr.left, paramType, true, parent.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
|
||||||
val func = BuiltinFunctions.getValue(callee.name)
|
|
||||||
val paramTypes = func.parameters[argnum].possibleDatatypes
|
|
||||||
for(type in paramTypes) {
|
|
||||||
if(leftDt isAssignableTo type) {
|
|
||||||
val cast = TypecastExpression(expr.left, type, true, parent.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("weird callee")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
val subroutine = expr.definingSubroutine()!!
|
|
||||||
val statement = expr.containingStatement()
|
|
||||||
val indexerVarPrefix = "prog8_autovar_index_"
|
|
||||||
val repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements
|
|
||||||
|
|
||||||
// TODO make this a bit smarter so it can reuse indexer variables. BUT BEWARE of scoping+initialization problems then
|
|
||||||
// add another loop index var to be used for this expression
|
|
||||||
val indexerVarName = "$indexerVarPrefix${expr.indexer.hashCode()}"
|
|
||||||
val indexerVar = AsmGenInfo.ArrayIndexerInfo(indexerVarName, expr.indexer)
|
|
||||||
repo.add(indexerVar)
|
|
||||||
// create the indexer var at block level scope
|
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE,
|
|
||||||
null, indexerVarName, null, null, isArray = false, autogeneratedDontRemove = true, position = expr.position)
|
|
||||||
modifications.add(IAstModification.InsertFirst(vardecl, subroutine))
|
|
||||||
|
|
||||||
// replace the indexer with just the variable
|
|
||||||
// assign the indexing expression to the helper variable, but only if that hasn't been done already
|
|
||||||
val indexerExpression = expr.indexer.origExpression!!
|
|
||||||
val target = AssignTarget(IdentifierReference(listOf(indexerVar.name), indexerExpression.position), null, null, indexerExpression.position)
|
|
||||||
val assign = Assignment(target, indexerExpression, indexerExpression.position)
|
|
||||||
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
|
|
||||||
modifications.add(IAstModification.SetExpression( {
|
|
||||||
expr.indexer.indexVar = it as IdentifierReference
|
|
||||||
expr.indexer.indexNum = null
|
|
||||||
expr.indexer.origExpression = null
|
|
||||||
}, target.identifier!!.copy(), expr.indexer))
|
|
||||||
|
|
||||||
return modifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
|
||||||
val choices = whenStatement.choiceValues(program).sortedBy {
|
|
||||||
it.first?.first() ?: Int.MAX_VALUE
|
|
||||||
}
|
|
||||||
whenStatement.choices.clear()
|
|
||||||
choices.mapTo(whenStatement.choices) { it.second }
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
|
||||||
val declValue = decl.value
|
|
||||||
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
|
||||||
val declConstValue = declValue.constValue(program)
|
|
||||||
if(declConstValue==null) {
|
|
||||||
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
|
||||||
// Unless we're dealing with a floating point variable because that will actually make things less efficient at the moment (because floats are mostly calcualated via the stack)
|
|
||||||
if(decl.datatype!=DataType.FLOAT) {
|
|
||||||
decl.value = null
|
|
||||||
decl.allowInitializeWithZero = false
|
|
||||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
|
||||||
val assign = Assignment(target, declValue, decl.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.ReplaceNode(decl, assign, parent),
|
|
||||||
IAstModification.InsertFirst(decl, decl.definingScope())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
|
||||||
val valueType = assignment.value.inferType(program)
|
|
||||||
val targetType = assignment.target.inferType(program)
|
|
||||||
var assignments = emptyList<Assignment>()
|
|
||||||
|
|
||||||
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
|
||||||
assignments = if (assignment.value is ArrayLiteralValue) {
|
|
||||||
flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] '
|
|
||||||
} else {
|
|
||||||
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
|
||||||
assignments = if (assignment.value is ArrayLiteralValue) {
|
|
||||||
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] '
|
|
||||||
} else {
|
|
||||||
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(assignments.isNotEmpty()) {
|
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
val scope = assignment.definingScope()
|
|
||||||
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, scope) }
|
|
||||||
modifications.add(IAstModification.Remove(assignment, scope))
|
|
||||||
return modifications
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
|
||||||
// rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand
|
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
|
||||||
if(binExpr!=null) {
|
|
||||||
if(binExpr.left isSameAs assignment.target) {
|
|
||||||
// A = A <operator> 5, unchanged
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
if(binExpr.operator in associativeOperators) {
|
|
||||||
if (binExpr.right isSameAs assignment.target) {
|
|
||||||
// A = v <associative-operator> A ==> A = A <associative-operator> v
|
|
||||||
return listOf(IAstModification.SwapOperands(binExpr))
|
|
||||||
}
|
|
||||||
|
|
||||||
val leftBinExpr = binExpr.left as? BinaryExpression
|
|
||||||
if(leftBinExpr?.operator == binExpr.operator) {
|
|
||||||
return if(leftBinExpr.left isSameAs assignment.target) {
|
|
||||||
// A = (A <associative-operator> x) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
|
||||||
val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position)
|
|
||||||
val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
|
||||||
} else {
|
|
||||||
// A = (x <associative-operator> A) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
|
||||||
val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position)
|
|
||||||
val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val rightBinExpr = binExpr.right as? BinaryExpression
|
|
||||||
if(rightBinExpr?.operator == binExpr.operator) {
|
|
||||||
return if(rightBinExpr.left isSameAs assignment.target) {
|
|
||||||
// A = x <associative-operator> (A <same-operator> y) ==> A = A <associative-operator> (x <same-operator> y)
|
|
||||||
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.right, binExpr.position)
|
|
||||||
val newValue = BinaryExpression(rightBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
|
||||||
} else {
|
|
||||||
// A = x <associative-operator> (y <same-operator> A) ==> A = A <associative-operator> (x <same-operator> y)
|
|
||||||
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.left, binExpr.position)
|
|
||||||
val newValue = BinaryExpression(rightBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> {
|
|
||||||
val identifier = assign.target.identifier!!
|
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
|
||||||
val alv = assign.value as? ArrayLiteralValue
|
|
||||||
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> {
|
|
||||||
val identifier = assign.target.identifier!!
|
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
|
||||||
val sourceIdent = assign.value as IdentifierReference
|
|
||||||
val sourceVar = sourceIdent.targetVarDecl(program.namespace)!!
|
|
||||||
if(!sourceVar.isArray) {
|
|
||||||
errors.err("value must be an array", sourceIdent.position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
val alv = sourceVar.value as? ArrayLiteralValue
|
|
||||||
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenArrayAssign(targetVar: VarDecl, alv: ArrayLiteralValue?, identifier: IdentifierReference, position: Position): List<Assignment> {
|
|
||||||
if(targetVar.arraysize==null) {
|
|
||||||
errors.err("array has no defined size", identifier.position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) {
|
|
||||||
errors.err("element count mismatch", position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO use a pointer loop instead of individual assignments
|
|
||||||
return alv.value.mapIndexed { index, value ->
|
|
||||||
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, position), position), position)
|
|
||||||
Assignment(AssignTarget(null, idx, null, position), value, value.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment): List<Assignment> {
|
|
||||||
val identifier = structAssignment.target.identifier!!
|
|
||||||
val identifierName = identifier.nameInSource.single()
|
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
|
||||||
val struct = targetVar.struct!!
|
|
||||||
|
|
||||||
val slv = structAssignment.value as? ArrayLiteralValue
|
|
||||||
if(slv==null || slv.value.size != struct.numberOfElements) {
|
|
||||||
errors.err("element count mismatch", structAssignment.position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
|
|
||||||
targetDecl as VarDecl
|
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
|
||||||
sourceValue, sourceValue.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List<Assignment> {
|
|
||||||
// TODO use memcopy beyond a certain number of elements
|
|
||||||
val identifier = structAssignment.target.identifier!!
|
|
||||||
val identifierName = identifier.nameInSource.single()
|
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
|
||||||
val struct = targetVar.struct!!
|
|
||||||
when (structAssignment.value) {
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
|
||||||
when {
|
|
||||||
sourceVar.struct!=null -> {
|
|
||||||
// struct memberwise copy
|
|
||||||
val sourceStruct = sourceVar.struct!!
|
|
||||||
if(sourceStruct!==targetVar.struct) {
|
|
||||||
// structs are not the same in assignment
|
|
||||||
return listOf() // error will be printed elsewhere
|
|
||||||
}
|
|
||||||
if(struct.statements.size!=sourceStruct.statements.size)
|
|
||||||
return listOf() // error will be printed elsewhere
|
|
||||||
return struct.statements.zip(sourceStruct.statements).map { member ->
|
|
||||||
val targetDecl = member.first as VarDecl
|
|
||||||
val sourceDecl = member.second as VarDecl
|
|
||||||
if(targetDecl.name != sourceDecl.name)
|
|
||||||
throw FatalAstException("struct member mismatch")
|
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
|
||||||
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
|
||||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourceVar.isArray -> {
|
|
||||||
val array = (sourceVar.value as ArrayLiteralValue).value
|
|
||||||
if(struct.statements.size!=array.size)
|
|
||||||
return listOf() // error will be printed elsewhere
|
|
||||||
return struct.statements.zip(array).map {
|
|
||||||
val decl = it.first as VarDecl
|
|
||||||
val mangled = mangledStructMemberName(identifierName, decl.name)
|
|
||||||
val targetName = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val target = AssignTarget(targetName, null, null, structAssignment.position)
|
|
||||||
val assign = Assignment(target, it.second, structAssignment.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw FatalAstException("can only assign arrays or structs to structs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ArrayLiteralValue -> {
|
|
||||||
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("strange struct value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
|
||||||
import prog8.ast.statements.AnonymousScope
|
|
||||||
import prog8.ast.statements.NopStatement
|
|
||||||
|
|
||||||
|
|
||||||
internal class VariousCleanups: AstWalker() {
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
|
||||||
return listOf(IAstModification.Remove(nopStatement, parent as INameScope))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
|
||||||
return if(parent is INameScope)
|
|
||||||
listOf(ScopeFlatten(scope, parent as INameScope))
|
|
||||||
else
|
|
||||||
noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
|
|
||||||
override fun perform() {
|
|
||||||
val idx = into.statements.indexOf(scope)
|
|
||||||
if(idx>=0) {
|
|
||||||
into.statements.addAll(idx+1, scope.statements)
|
|
||||||
into.statements.remove(scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
if(typecast.expression is NumericLiteralValue) {
|
|
||||||
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
|
||||||
if(value.isValid)
|
|
||||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
package prog8.compiler
|
|
||||||
|
|
||||||
internal class AssemblyError(msg: String) : RuntimeException(msg)
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user