mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dedc27e5b | ||
|
|
5ba494c5e5 | ||
|
|
5c16ebe377 | ||
|
|
b5dfc0cd97 | ||
|
|
d276fb9ed5 | ||
|
|
ec9567a669 | ||
|
|
70d81298c1 | ||
|
|
1de1c10b41 | ||
|
|
25f883bb85 | ||
|
|
adc6d6626a | ||
|
|
90ff904b6e | ||
|
|
ba54601eab | ||
|
|
1c71f702f4 | ||
|
|
3d3c567ea6 | ||
|
|
539c75bbf6 | ||
|
|
2683b6388a | ||
|
|
a919ea63e4 | ||
|
|
07910fc543 | ||
|
|
13e032e1a3 | ||
|
|
c3b5ee3dfd | ||
|
|
b0e0058310 | ||
|
|
9cbbf85a85 | ||
|
|
9936a855ab | ||
|
|
bb1d1e2007 | ||
|
|
0aa6c130e8 | ||
|
|
51257e47da | ||
|
|
67ddd97f3d | ||
|
|
abea7bf329 | ||
|
|
bc3f49dc13 | ||
|
|
cf2a5ae4f9 | ||
|
|
e5865a1446 | ||
|
|
5b5f553d37 | ||
|
|
b9fe1f2057 | ||
|
|
cfd1f951f0 | ||
|
|
b0e55eb25b | ||
|
|
b405ffb8bd | ||
|
|
0fa08310e7 | ||
|
|
968d9819db | ||
|
|
f85c59b12c | ||
|
|
34eedd1fe7 | ||
|
|
2c10e2b102 | ||
|
|
05bea61a69 | ||
|
|
2a8b24074b | ||
|
|
1f184a14ae | ||
|
|
8be1cd1090 | ||
|
|
c3ec400ee7 | ||
|
|
5130a74754 | ||
|
|
e61db5ed3f | ||
|
|
cc5ef21fd6 | ||
|
|
f1f925ecee | ||
|
|
ab422c0ebc | ||
|
|
05a13463f4 | ||
|
|
19238cc366 | ||
|
|
e3d4401d99 | ||
|
|
5da4efc2a3 | ||
|
|
c0c44156f1 | ||
|
|
8645833319 | ||
|
|
6a867d98eb | ||
|
|
58363ff4e4 | ||
|
|
2555aa72ca | ||
|
|
3b94fd4c4b | ||
|
|
95f500c32f | ||
|
|
870c2e8827 | ||
|
|
af2360648f | ||
|
|
bb95d40d6d | ||
|
|
55ea599fca | ||
|
|
fd5fdf3d13 | ||
|
|
ca129edce4 | ||
|
|
c1694a4b08 | ||
|
|
a348bbbebe | ||
|
|
e6b0a89d64 | ||
|
|
9a4821d1e3 | ||
|
|
71583a5b93 | ||
|
|
407ba34c72 | ||
|
|
fc4782a35b | ||
|
|
7a83b7db1b | ||
|
|
a827f4c4ce | ||
|
|
01c5794799 | ||
|
|
1c093cca2d | ||
|
|
a85b267872 | ||
|
|
5e0c1c9794 | ||
|
|
887df44359 | ||
|
|
a5fc162c71 | ||
|
|
9d3fc99d71 | ||
|
|
a871713907 | ||
|
|
2a19e5682a | ||
|
|
22d4fa73d6 | ||
|
|
2ed95c52ef | ||
|
|
73fb04aaf5 | ||
|
|
f97a7f426c | ||
|
|
c1189617f3 | ||
|
|
78d43c3cb6 | ||
|
|
86f9b6aa69 | ||
|
|
8df2bb8d2e | ||
|
|
33f30d925e | ||
|
|
c8c51245af | ||
|
|
db227679a8 | ||
|
|
02110de844 | ||
|
|
7d77a0eca6 | ||
|
|
0f3475128a | ||
|
|
e6e4714b02 | ||
|
|
d83ece397d | ||
|
|
a3c768bc6b | ||
|
|
a3d14003cb | ||
|
|
d451953e40 | ||
|
|
d505c42d15 | ||
|
|
8c92663824 | ||
|
|
10d86eb4d8 | ||
|
|
ee5e734f23 | ||
|
|
6b233b7da3 | ||
|
|
a140519374 | ||
|
|
2b118f2e42 | ||
|
|
be9d9fe377 | ||
|
|
d1345aafec | ||
|
|
d5932a7a57 | ||
|
|
5751beb72f | ||
|
|
02194a735a | ||
|
|
b5fc25b2f4 | ||
|
|
5a593b9ef3 | ||
|
|
b411914624 | ||
|
|
0f80b695e7 | ||
|
|
63c425f247 | ||
|
|
276627abcb | ||
|
|
34225d10aa | ||
|
|
69261c51b3 | ||
|
|
63d7ef7105 | ||
|
|
88a3e5b23f | ||
|
|
282c3aa00b | ||
|
|
19f3b03ac3 | ||
|
|
1b5efe1e5d | ||
|
|
2b5a15a178 | ||
|
|
a9f22d9375 | ||
|
|
f9b3514f2a | ||
|
|
be12fb507e | ||
|
|
b2dcd02ee4 | ||
|
|
5c23471254 | ||
|
|
6cb6f37871 | ||
|
|
67e284b680 | ||
|
|
1e5ba48fe0 | ||
|
|
8e6a27d7c1 | ||
|
|
d0edc0b484 | ||
|
|
ccd3d94ec0 | ||
|
|
98355d3850 | ||
|
|
0e53113b85 | ||
|
|
e9a3ed457e | ||
|
|
6001da30d8 | ||
|
|
c55f5e4b50 | ||
|
|
e983c0c2b9 | ||
|
|
532bf13057 | ||
|
|
de706e79de | ||
|
|
a19f337ebe | ||
|
|
041a904e44 | ||
|
|
131134d24b | ||
|
|
97fb5298a8 | ||
|
|
b580a177d4 | ||
|
|
a0b1ca5d9f | ||
|
|
3a291c56b4 | ||
|
|
ed41d5dbc5 | ||
|
|
628bd41740 | ||
|
|
de988f72c6 | ||
|
|
869c5ef544 | ||
|
|
17ee233e8b | ||
|
|
f2d1dffbe0 | ||
|
|
4008280a62 | ||
|
|
2e7235ec02 | ||
|
|
a4330f5048 | ||
|
|
f65aeff231 | ||
|
|
505243aff7 | ||
|
|
d0f2fc13ba | ||
|
|
53d51dcd38 | ||
|
|
c1aa0e49fe | ||
|
|
e1e72d4317 | ||
|
|
feec343cbc | ||
|
|
5cdc4233f3 | ||
|
|
1ae89d18cc | ||
|
|
eb19d00a1e | ||
|
|
e01ee10ef0 | ||
|
|
f4b3cb12e1 | ||
|
|
77ced60ad6 | ||
|
|
6fad90af6e | ||
|
|
3076318d7e | ||
|
|
9c99110738 | ||
|
|
1457d36997 | ||
|
|
43fb9296dd | ||
|
|
8bf744cc4a | ||
|
|
f1f5f070cf | ||
|
|
191a240d0d | ||
|
|
cafb2e355b | ||
|
|
c99c5751f8 | ||
|
|
435b7e56fa | ||
|
|
14f13fac53 | ||
|
|
f8b5037924 | ||
|
|
d2870cb02e | ||
|
|
8e43fc426c | ||
|
|
ba1b6e127a | ||
|
|
4f36a6958c | ||
|
|
df8f08b4ab | ||
|
|
fd5746d3fc | ||
|
|
6ab74fd51d | ||
|
|
245082ca1d | ||
|
|
845e58ea10 | ||
|
|
2a5dbc1ef7 | ||
|
|
02a1deb1e0 | ||
|
|
3ba8e5de10 | ||
|
|
819d1b4af7 | ||
|
|
cdd3d37376 | ||
|
|
d0bc01d618 | ||
|
|
ffab73334a | ||
|
|
83aeb8846c | ||
|
|
3a2087310b | ||
|
|
61b616701e | ||
|
|
bddf872906 | ||
|
|
48214a6fdb | ||
|
|
b7340856df | ||
|
|
2685854113 | ||
|
|
ad92f39469 | ||
|
|
d3fe041e01 | ||
|
|
f32bac5004 | ||
|
|
9a8e4ac105 | ||
|
|
78857e5c22 | ||
|
|
1bb828875d | ||
|
|
82aa67ffda | ||
|
|
7c44835772 | ||
|
|
8b9c63fe90 | ||
|
|
e77fcc8c16 | ||
|
|
08c12131d4 | ||
|
|
5a3ddf705a | ||
|
|
20b29fb9b1 | ||
|
|
fd3d59e159 | ||
|
|
6d1dfd2a09 | ||
|
|
7095b14bcb | ||
|
|
1fe5ff18b1 | ||
|
|
793b2a703b | ||
|
|
53948be68f | ||
|
|
3d0dbb71ce | ||
|
|
3dfa804e52 | ||
|
|
fd93d103f6 | ||
|
|
28977996e2 | ||
|
|
e867cb88a7 | ||
|
|
1ba8dfcc8b | ||
|
|
cd2d30c2c7 | ||
|
|
a47d2fa40d | ||
|
|
6be70af79f | ||
|
|
15a28814d5 | ||
|
|
049acf0e98 | ||
|
|
df0d9e567f | ||
|
|
78920aa115 | ||
|
|
1bb9ad5f46 | ||
|
|
1f30a06429 | ||
|
|
5560c342db | ||
|
|
e073c007ad | ||
|
|
9d1fa856d4 | ||
|
|
7a26bb972f | ||
|
|
e72a4723f9 | ||
|
|
96f0d112d2 | ||
|
|
eac1b64f81 | ||
|
|
0011dd8f4c | ||
|
|
5ce463d133 | ||
|
|
5926473281 | ||
|
|
0a1d06b7f2 | ||
|
|
f98e7d6ad9 | ||
|
|
37896aeed4 | ||
|
|
b175dd0265 | ||
|
|
310d17d8bb | ||
|
|
371bd4b120 | ||
|
|
3965d77813 | ||
|
|
e0ed761661 | ||
|
|
51d5261776 | ||
|
|
b85ac38b3b | ||
|
|
6ff9153d9b | ||
|
|
e0d2e407e4 | ||
|
|
0117ab410b | ||
|
|
b78a0cf076 | ||
|
|
722d52c939 | ||
|
|
162e5ffe3c | ||
|
|
7383ffae46 | ||
|
|
1b4635d27e | ||
|
|
1c376aa9d1 | ||
|
|
91ff17f666 | ||
|
|
5a81021ddc | ||
|
|
9fdd9254f1 | ||
|
|
ae4c4ca160 | ||
|
|
7ab4880c4a | ||
|
|
5c16a40f33 | ||
|
|
6f9f31cd8a | ||
|
|
7cd70acc01 | ||
|
|
2d3c20d347 | ||
|
|
b05bc9ac0e | ||
|
|
2589f953c3 | ||
|
|
a40f9de9e6 | ||
|
|
e7491a56ef | ||
|
|
2bf5d1406d | ||
|
|
c32e7ee57b | ||
|
|
a633b5a4b5 | ||
|
|
a5c078be14 | ||
|
|
547d21a42b | ||
|
|
e2e0992d02 | ||
|
|
10fd321537 | ||
|
|
fb26a7af3b | ||
|
|
8592a3a200 | ||
|
|
930697dad5 | ||
|
|
a60ba27bad | ||
|
|
87480b150a | ||
|
|
0cb9d408ff | ||
|
|
c784fd03fd | ||
|
|
5dcc11d67e | ||
|
|
e36a589e23 | ||
|
|
fe6a8f63a0 | ||
|
|
60e2a38060 | ||
|
|
d808818506 | ||
|
|
0f72bafeae | ||
|
|
c794d2a50b | ||
|
|
db84c375f1 | ||
|
|
03fa6422b3 | ||
|
|
ad34633ca9 | ||
|
|
d5eef79892 | ||
|
|
d5a53c5d21 | ||
|
|
bd51296a33 | ||
|
|
c0103f149e | ||
|
|
86174e49c9 | ||
|
|
0be336c8a8 | ||
|
|
d836f48f38 | ||
|
|
952d7b3162 | ||
|
|
5638eb1c53 | ||
|
|
d3f19bb438 | ||
|
|
be60021edc | ||
|
|
b2ed3b5934 | ||
|
|
59d0f7d49c | ||
|
|
47700918cf | ||
|
|
db7edde2aa | ||
|
|
e2702d3e22 | ||
|
|
74475d023b | ||
|
|
9ae0623ad0 | ||
|
|
33b34b990e | ||
|
|
6dc96bb8f0 | ||
|
|
aeca6350da | ||
|
|
6a340aa59d | ||
|
|
207650fbc2 | ||
|
|
4fb3df977e | ||
|
|
9f742e1806 | ||
|
|
aca6c22098 | ||
|
|
4b45dae68a | ||
|
|
0b68481fda | ||
|
|
bba08f55de | ||
|
|
9e5df3d2d4 | ||
|
|
5c1aa2307d | ||
|
|
3129f5e071 | ||
|
|
04a8181c05 | ||
|
|
27bf9c9a4c | ||
|
|
7658a0ab12 | ||
|
|
42d3340fe4 | ||
|
|
1100ab0d22 | ||
|
|
96eb3f0fb2 | ||
|
|
1ed618c474 | ||
|
|
e6c26587d6 | ||
|
|
4739bd087a | ||
|
|
92751d9285 | ||
|
|
0792571b28 | ||
|
|
511564dd1e | ||
|
|
3da3d63eb4 | ||
|
|
3c859512a5 | ||
|
|
45c05522d5 | ||
|
|
db417ab9ce | ||
|
|
c17a3aff5c | ||
|
|
6184a5004c | ||
|
|
07772bddfd | ||
|
|
15bf4c7692 | ||
|
|
f38c701658 | ||
|
|
10ce992b35 | ||
|
|
331e2b8f2b | ||
|
|
05093e35c8 | ||
|
|
5fd11ca036 | ||
|
|
5330069b53 | ||
|
|
5408a3ca68 | ||
|
|
cecf49b8e3 | ||
|
|
65c17b920a | ||
|
|
7666b72fea | ||
|
|
bcd12a0fc7 | ||
|
|
dd0828a1fc | ||
|
|
770532a039 | ||
|
|
400149d5a3 | ||
|
|
d6ed40af36 | ||
|
|
570ea50a3b | ||
|
|
04b9f8e749 | ||
|
|
2bd7f3e210 | ||
|
|
00bae6e746 | ||
|
|
f6d5e88f31 | ||
|
|
8d44a1b226 | ||
|
|
a47be61812 | ||
|
|
68226a8fcd | ||
|
|
2016883f6c | ||
|
|
d4dd2f02e8 | ||
|
|
bc959435ec | ||
|
|
e5c6e4b854 | ||
|
|
4294658d71 | ||
|
|
0a7352de99 | ||
|
|
bb907aa2af | ||
|
|
e5d74d55b5 | ||
|
|
c291d7d4a8 | ||
|
|
d2633187a6 | ||
|
|
ca56cf26c5 | ||
|
|
f72bec3c4a | ||
|
|
06b4ec600a | ||
|
|
410e330889 | ||
|
|
11f187bda8 | ||
|
|
a53e7c7050 | ||
|
|
e56ccdbc3a | ||
|
|
51264267ad | ||
|
|
8be0cd3c19 | ||
|
|
decfbfc8cc | ||
|
|
652da4c692 | ||
|
|
ce58f0e759 | ||
|
|
8ba8efbc3e | ||
|
|
276121bfb3 | ||
|
|
5c9c6bd023 | ||
|
|
86f331aa39 | ||
|
|
e61328b6d6 | ||
|
|
9502a6f590 | ||
|
|
01748517ae | ||
|
|
3890191981 | ||
|
|
c20e5b788b | ||
|
|
fb3a001a99 | ||
|
|
04910ffbc0 | ||
|
|
caa991e478 | ||
|
|
f9767a5b1b | ||
|
|
3b68741208 | ||
|
|
b8ef64139a | ||
|
|
1ad9e0fc46 | ||
|
|
ce244906ef | ||
|
|
63ab79157f | ||
|
|
9ac0cfd14c | ||
|
|
02c544bb62 | ||
|
|
998d1ad446 | ||
|
|
89e38c6d42 | ||
|
|
a326c6f079 | ||
|
|
acf70d68ca | ||
|
|
17850d3bc1 | ||
|
|
d03e49a92f | ||
|
|
62c5b531c5 | ||
|
|
8911f58bd2 | ||
|
|
da6e47d422 | ||
|
|
10d80d0274 | ||
|
|
23b9a2792d | ||
|
|
0a52cd2cee | ||
|
|
fedf342cee | ||
|
|
2bafc161d6 | ||
|
|
d0c5e35f0a | ||
|
|
859a02fb0a | ||
|
|
2a53dbc1f3 | ||
|
|
63cba4825a | ||
|
|
08e3652924 | ||
|
|
10796c09c7 | ||
|
|
a3cbf4c3db | ||
|
|
bdbdf6e5cd | ||
|
|
1b5dd980df | ||
|
|
f6b59c6832 | ||
|
|
fe8d904792 | ||
|
|
9736ac19d1 | ||
|
|
14abc1f0b7 | ||
|
|
effc7f1b56 | ||
|
|
093be9f2dd | ||
|
|
9aff85f08a | ||
|
|
f2077cb56f | ||
|
|
2a9ac45cad | ||
|
|
e15452b110 | ||
|
|
a64b586a43 | ||
|
|
2613d45cd5 | ||
|
|
c25fbbccfb | ||
|
|
77f0cf8e89 | ||
|
|
74ca190ce0 | ||
|
|
6a0b22bb64 | ||
|
|
07be7f0154 | ||
|
|
d1383813d2 | ||
|
|
3bd2064c3a | ||
|
|
f95f568319 | ||
|
|
5eb873b722 | ||
|
|
3f181b0ede | ||
|
|
d6c8fc0c61 | ||
|
|
709ced1bca | ||
|
|
3b16d08246 | ||
|
|
b9a6deea25 | ||
|
|
099514a2c0 | ||
|
|
ed02b84f7d | ||
|
|
7d6e67e251 | ||
|
|
4aa364925d | ||
|
|
e5ae9ad4e0 | ||
|
|
6329388a4e | ||
|
|
ac851a35bc | ||
|
|
bc0d6453ae | ||
|
|
8c65e0a537 | ||
|
|
d415cf356f | ||
|
|
2bd74b8c44 | ||
|
|
88b48f58c9 | ||
|
|
75accf4059 | ||
|
|
c3f9959891 | ||
|
|
ee492a1e9a | ||
|
|
415f0fb071 | ||
|
|
8e7bf4227b | ||
|
|
d2b743c296 | ||
|
|
cb44e76626 | ||
|
|
b2983e912d | ||
|
|
10ae42ed59 | ||
|
|
5cbd4aafcc | ||
|
|
4aa326d5ca | ||
|
|
39b1bbcf58 | ||
|
|
4661d3bada | ||
|
|
ab98f4a5e2 | ||
|
|
b99b054a09 | ||
|
|
5d5ad8a70e | ||
|
|
9a5edeebbc | ||
|
|
3fb45a611a | ||
|
|
2e0db6f211 | ||
|
|
51ac096a31 | ||
|
|
dfad83650d | ||
|
|
492cd686a0 | ||
|
|
561e05a764 | ||
|
|
f56037142b | ||
|
|
a41cb60328 | ||
|
|
ca3e4b161b | ||
|
|
4971ebd41f | ||
|
|
e2276f000c | ||
|
|
520a12194e | ||
|
|
6c61e1e5e8 | ||
|
|
a385412e70 | ||
|
|
928443469a | ||
|
|
823f1cd6e2 | ||
|
|
3009ddaaab | ||
|
|
bc0aecb1af | ||
|
|
573702111e | ||
|
|
eaab2cfe5f | ||
|
|
666285a002 | ||
|
|
d477e9b2bd | ||
|
|
d774bad2b4 | ||
|
|
e3bb09d2a1 | ||
|
|
112bce0ac0 | ||
|
|
aac7d68455 | ||
|
|
620793011e | ||
|
|
cb4ffaa028 | ||
|
|
8c4944d703 | ||
|
|
f5fbb5f930 | ||
|
|
40ce40db32 | ||
|
|
ee556434bc | ||
|
|
5cd175d54f | ||
|
|
b51e116b63 | ||
|
|
5c7ac0efb7 | ||
|
|
34b228cfca | ||
|
|
2b26952044 | ||
|
|
db38c27224 | ||
|
|
560013e58b | ||
|
|
cdb41f4352 | ||
|
|
13cd68ba96 | ||
|
|
f69b293b7f | ||
|
|
d4bf0bf5b7 | ||
|
|
8c6a95f5e2 | ||
|
|
6301a8b040 | ||
|
|
daa9b40883 | ||
|
|
aa045bb383 | ||
|
|
9d7049a659 | ||
|
|
bbfb2eb367 | ||
|
|
447271f3f2 | ||
|
|
78d6d2d59a | ||
|
|
c6cd39b4ab | ||
|
|
cd34c4eed0 | ||
|
|
652b585ea4 | ||
|
|
2913fa47b2 | ||
|
|
d6e4175e21 | ||
|
|
3a0add60fd | ||
|
|
bc4470aeda | ||
|
|
97dade3655 | ||
|
|
54f23387d0 | ||
|
|
77161e0f39 | ||
|
|
70d99bebb0 | ||
|
|
75156f7dbd | ||
|
|
9827be0047 | ||
|
|
495dd526e1 | ||
|
|
3d20bc13b5 | ||
|
|
6028cc49ab | ||
|
|
23d3fb96b9 | ||
|
|
51503e054a | ||
|
|
3f6177cbb8 | ||
|
|
c1c88e0327 | ||
|
|
18ba25d6a4 | ||
|
|
e74162671a | ||
|
|
2290308e06 | ||
|
|
82693cc3ee | ||
|
|
50b8cae519 | ||
|
|
3a74931a61 | ||
|
|
c87e920c61 | ||
|
|
2c2de8bfb3 | ||
|
|
d386343fe7 | ||
|
|
5b32cfd172 | ||
|
|
46dffcfb96 | ||
|
|
b156c876f0 | ||
|
|
71b66f506d | ||
|
|
28d087b8b4 | ||
|
|
a57815153d | ||
|
|
f910f73f28 | ||
|
|
6635515bf0 | ||
|
|
cc0425a2c4 | ||
|
|
c7052a183e | ||
|
|
d9a0f3cb4a | ||
|
|
80d6eb232d | ||
|
|
d9ffb10eb5 | ||
|
|
463d53345d | ||
|
|
4b1063530e | ||
|
|
9266943e74 | ||
|
|
1e3ae2cb1d | ||
|
|
55bc242e8b | ||
|
|
aa3bbbb867 | ||
|
|
45ad0911df | ||
|
|
946def4613 | ||
|
|
3000c399bd | ||
|
|
1682400988 | ||
|
|
6ad435df1f | ||
|
|
cd433b2111 | ||
|
|
a50eebb369 | ||
|
|
13d4af59c7 | ||
|
|
b6f4f2adb7 | ||
|
|
e57d9f59a2 | ||
|
|
071d5dc0c5 | ||
|
|
4030a1b9e5 | ||
|
|
0217d05c68 | ||
|
|
8553f3c9f1 | ||
|
|
d157d03ea0 | ||
|
|
1799b5f00c | ||
|
|
2ddb5e11b7 | ||
|
|
3aa8b9fd12 | ||
|
|
51300196e8 | ||
|
|
f7b2f19cba | ||
|
|
014a82a1ee | ||
|
|
745cf3d958 | ||
|
|
c4aa681d58 | ||
|
|
feb0774bbd | ||
|
|
2ccfb2ac90 | ||
|
|
2191c4b7d8 | ||
|
|
39c1cf0c3c | ||
|
|
c693be0dd6 | ||
|
|
0f433055a7 | ||
|
|
76c95ba6fa | ||
|
|
bd272a8567 | ||
|
|
b38fff76e9 | ||
|
|
07b7639ff2 | ||
|
|
6871561c09 | ||
|
|
77a71138ad | ||
|
|
165f94bc93 | ||
|
|
b75090f928 | ||
|
|
62b0c82f93 | ||
|
|
b1ef863c7f | ||
|
|
e78345410f | ||
|
|
8b86f97aaa | ||
|
|
93135774e6 | ||
|
|
a64f27c6b0 | ||
|
|
69ef63c96d | ||
|
|
df1a2a1611 | ||
|
|
d19a3af9ed | ||
|
|
352c11ad9f | ||
|
|
9504711fc7 | ||
|
|
590feda903 | ||
|
|
6e7e2922bf | ||
|
|
01df1f0083 | ||
|
|
397299bd1d | ||
|
|
c275aacd38 | ||
|
|
6a6e18773e | ||
|
|
99e037489b | ||
|
|
fb5290e17b | ||
|
|
818774ab84 | ||
|
|
d667312acc | ||
|
|
64d0cd87a8 | ||
|
|
2fffbdde89 | ||
|
|
dc8ea31c49 | ||
|
|
f4ead66e91 | ||
|
|
314e7f5691 | ||
|
|
da31465b7f | ||
|
|
397a907088 | ||
|
|
50c6962e1f | ||
|
|
b7d1fb1342 | ||
|
|
afb458a7da | ||
|
|
da3c7f267f | ||
|
|
b7ba7c50b1 | ||
|
|
bdbfe7048b | ||
|
|
79039b66d4 | ||
|
|
833e463525 | ||
|
|
6611e4e092 | ||
|
|
1f31cb18e4 | ||
|
|
bc8ba252a5 | ||
|
|
c353dd40bf | ||
|
|
928ef6bbaa | ||
|
|
45dde856c6 | ||
|
|
e1ccef4e89 | ||
|
|
4d3f0ec223 | ||
|
|
b73c958c4a | ||
|
|
2eac457e1c | ||
|
|
815ef7e654 | ||
|
|
0cd8c4f87e | ||
|
|
b02a8ed954 | ||
|
|
b1e07f3fdb | ||
|
|
fc8727f81e | ||
|
|
ec728bad52 | ||
|
|
a5e827e40f | ||
|
|
34061c5a63 | ||
|
|
9c2bcab4a5 | ||
|
|
2f07f41664 | ||
|
|
7027304597 | ||
|
|
ee75333891 | ||
|
|
6a70fb0480 | ||
|
|
ebc738b132 | ||
|
|
e5939be0bd | ||
|
|
0c0affd1bf | ||
|
|
c7158fd968 | ||
|
|
de2f3f0d0d | ||
|
|
c0286e3349 | ||
|
|
513ee25432 | ||
|
|
be74290ddc | ||
|
|
a36501a9ed | ||
|
|
ee5d33a230 | ||
|
|
a8fd66cfdb | ||
|
|
f5ce744748 | ||
|
|
68066acdec | ||
|
|
6286035d89 | ||
|
|
9a37eb9146 | ||
|
|
307796f115 | ||
|
|
e962431139 | ||
|
|
b7620abc9a | ||
|
|
d2719d23f5 | ||
|
|
d69eead6d8 | ||
|
|
6db3611d93 | ||
|
|
a84320c7e2 | ||
|
|
dfc720557c | ||
|
|
32149d073a | ||
|
|
fc38be6376 | ||
|
|
13f6efc1d1 | ||
|
|
8296002887 | ||
|
|
424b89f357 | ||
|
|
4f5590fbff | ||
|
|
598e70c49a | ||
|
|
a6d051b496 | ||
|
|
796d3242e1 | ||
|
|
1e8ff6f82a | ||
|
|
07bb5c36bd | ||
|
|
7e26ecb0b6 | ||
|
|
0c59ad70d4 | ||
|
|
53ce688cc5 | ||
|
|
bb8b44d9d0 | ||
|
|
51ae32d23e | ||
|
|
c873fac0dc | ||
|
|
f0a67fff8a | ||
|
|
f85ccd837d | ||
|
|
396fcbc927 | ||
|
|
0f564b301d | ||
|
|
f4f34fc2ed | ||
|
|
f7639cb78f | ||
|
|
f5f5aef722 | ||
|
|
d6e30d8468 | ||
|
|
37afdf5a18 | ||
|
|
5eb7074172 | ||
|
|
9aff280d10 | ||
|
|
44741a8e32 | ||
|
|
37535f2913 | ||
|
|
ec9475c308 | ||
|
|
b961ce97d6 | ||
|
|
4ed92d71a7 | ||
|
|
3e1386a987 | ||
|
|
4195e3968a | ||
|
|
961095bfec | ||
|
|
3ea8ca59b7 | ||
|
|
71ffbe2ba7 | ||
|
|
e63921009c | ||
|
|
db1aa3f257 | ||
|
|
8abdb837b2 | ||
|
|
cdeabd4b66 | ||
|
|
efff74c0f1 | ||
|
|
0e177cb531 | ||
|
|
35b921b062 | ||
|
|
a7d98e43b8 | ||
|
|
845ee2dd83 | ||
|
|
f9b0bfe31b | ||
|
|
f303d15628 | ||
|
|
67f7ffa52d | ||
|
|
88c5d9783a | ||
|
|
e7fc0360ad | ||
|
|
cd1862dd9f | ||
|
|
045c4909a8 | ||
|
|
f1bfe619b2 | ||
|
|
e0107bacbd | ||
|
|
b3bd2a6a09 | ||
|
|
ff1f58e022 | ||
|
|
557b12668d | ||
|
|
b058f1c7c2 | ||
|
|
3e07b6ca70 | ||
|
|
d66dc664de | ||
|
|
a2b9d78cf3 | ||
|
|
44f70da113 | ||
|
|
c87eaf576d | ||
|
|
c09b1395f6 | ||
|
|
d6a8201291 | ||
|
|
4187f97f7a | ||
|
|
9d3b2f12fd | ||
|
|
87c1bbbf40 | ||
|
|
a7ad6abdb9 | ||
|
|
a611406020 | ||
|
|
75da38224d | ||
|
|
8d6f3301c8 | ||
|
|
86b52a1c5e | ||
|
|
2c8b1c2022 | ||
|
|
ab1f065752 | ||
|
|
2c7256a443 | ||
|
|
97420b28e5 | ||
|
|
1467c7039d | ||
|
|
d319badc6c | ||
|
|
65d6c1c438 | ||
|
|
abeefb5655 | ||
|
|
50fecbcebe | ||
|
|
4fe8b72d42 | ||
|
|
f3b060df51 | ||
|
|
09d1cb6925 | ||
|
|
54fa72fa98 | ||
|
|
fd62fe7511 | ||
|
|
bfb34dff62 | ||
|
|
817b623596 | ||
|
|
f6dbeb1f63 | ||
|
|
c4b9bdd33f | ||
|
|
68e0d5f1b5 | ||
|
|
4939e3df55 | ||
|
|
19f19f3880 | ||
|
|
ad0c767ea8 | ||
|
|
ed5f4d5855 | ||
|
|
c2f5d37486 | ||
|
|
231b50dacb | ||
|
|
a71895cbe8 | ||
|
|
a8bede17b2 | ||
|
|
f6c8e693a5 | ||
|
|
9461e4088c | ||
|
|
efd73fd10d | ||
|
|
ddf6e84a1a | ||
|
|
633d6c34e2 | ||
|
|
6e7fbc6683 | ||
|
|
124ea1230b | ||
|
|
8b48a295b6 | ||
|
|
d285d37fdb | ||
|
|
8bb927b483 | ||
|
|
1af4cd0d63 | ||
|
|
db2f28c4cd | ||
|
|
5d9fbd2ccc | ||
|
|
7efc709538 | ||
|
|
79419a98d0 | ||
|
|
1c77d5d5e7 | ||
|
|
c6854e22a3 | ||
|
|
83acc2f285 | ||
|
|
6de95f7a3b | ||
|
|
82839a2d82 | ||
|
|
e5fc9b3132 | ||
|
|
ced4c5944a | ||
|
|
d4c460072b | ||
|
|
0b9384b556 | ||
|
|
6c3277e3e3 | ||
|
|
d9ff1eb38a | ||
|
|
e178097735 | ||
|
|
a1ab8ed208 | ||
|
|
6ababbf8f4 | ||
|
|
79629befc1 | ||
|
|
8022c0772a | ||
|
|
8ad2b4638b | ||
|
|
3a0392df8a | ||
|
|
beb28b061d | ||
|
|
c39acc5031 | ||
|
|
f54a29c415 | ||
|
|
783b111059 | ||
|
|
fb286f8b54 | ||
|
|
5999110e3f | ||
|
|
52a757ea78 | ||
|
|
28df08eea8 | ||
|
|
79505308ba | ||
|
|
a7e9d8e14b | ||
|
|
08f3abe5bf | ||
|
|
3ef09d7d9a | ||
|
|
a9142b9ce5 | ||
|
|
13e6f64d3b | ||
|
|
4a1256c772 | ||
|
|
ff9bec90ec | ||
|
|
a6fee1e510 | ||
|
|
80538f101e | ||
|
|
aee53b14c7 | ||
|
|
5eb2fc8d86 | ||
|
|
98f91bbf88 | ||
|
|
b48b36ef18 | ||
|
|
221a093e5f | ||
|
|
2ca1820d4e | ||
|
|
d58737d5be | ||
|
|
b52cee3154 | ||
|
|
9a76941e10 | ||
|
|
0285a4cce1 | ||
|
|
5a3aa1bd25 | ||
|
|
0f79351de9 | ||
|
|
3cdb25ce8e | ||
|
|
b7193bd0c6 | ||
|
|
10ff6a0095 | ||
|
|
d30f58004e | ||
|
|
17bdb22e4f | ||
|
|
a68209be37 | ||
|
|
7b40eade44 | ||
|
|
8a717c74b9 | ||
|
|
aa72ded21e | ||
|
|
8e53c83844 | ||
|
|
e2a2db1256 | ||
|
|
2afd2d4dae | ||
|
|
fad17cc094 | ||
|
|
f93d957999 | ||
|
|
8db0344cee | ||
|
|
32e531f951 | ||
|
|
a4769702f9 | ||
|
|
cf19fb8df1 | ||
|
|
79b8bb5c9f | ||
|
|
fc5889ec0b | ||
|
|
369303c46d | ||
|
|
d65670cc7b | ||
|
|
f74eeaee0f | ||
|
|
826fb3e9c2 | ||
|
|
a3d7b8a899 | ||
|
|
0cc36ed6e4 | ||
|
|
976bd52972 | ||
|
|
4a8d5def84 | ||
|
|
2f60716082 | ||
|
|
729efb04e1 | ||
|
|
4ea8b4d445 | ||
|
|
e800c165f9 | ||
|
|
fd9bd23449 | ||
|
|
8880ed1393 | ||
|
|
f7fde070ca | ||
|
|
5ada80779d | ||
|
|
8972235a0e | ||
|
|
e56f533e38 | ||
|
|
324fb7dbf7 | ||
|
|
44285b9b5d | ||
|
|
a68f477d61 | ||
|
|
ae9f99448e | ||
|
|
7c0fb10197 | ||
|
|
9e85571a7b | ||
|
|
9e10c15e2e | ||
|
|
6bd7752bac | ||
|
|
83ec437e8a | ||
|
|
4a1d05dd46 | ||
|
|
aa324e355a | ||
|
|
5cb8bcead7 | ||
|
|
bbd06c0c99 | ||
|
|
651830ea82 | ||
|
|
c70146f1dc | ||
|
|
d4e83b28bb | ||
|
|
bc58a25765 | ||
|
|
38645022c9 | ||
|
|
647cd0fbe1 | ||
|
|
ea8935a346 | ||
|
|
7ea80babfc | ||
|
|
dee761a99e | ||
|
|
88ee7a8187 | ||
|
|
eb8b408b82 | ||
|
|
3d10882f57 | ||
|
|
1988496512 | ||
|
|
88b074c208 | ||
|
|
c4c5636a81 | ||
|
|
c39d570b72 | ||
|
|
4ccd7f9f3a | ||
|
|
1c9c5aeef7 | ||
|
|
23ad540aa5 | ||
|
|
08810c2749 | ||
|
|
a52966f327 | ||
|
|
624220e9a3 | ||
|
|
842b11ed9e | ||
|
|
82267b3f56 | ||
|
|
67fb45a55b | ||
|
|
11186f1dbe | ||
|
|
0116fac201 | ||
|
|
817f4f8e7c | ||
|
|
866313209b | ||
|
|
28e351daab | ||
|
|
893e16d814 | ||
|
|
33470c47fc | ||
|
|
63f7b87572 | ||
|
|
e2901cca1b | ||
|
|
ce8006992a | ||
|
|
0b5413ad83 | ||
|
|
dd7adde387 | ||
|
|
23058b51a1 | ||
|
|
2f90c53ad0 | ||
|
|
c3be7ab4b3 | ||
|
|
a9b8fbc6c6 | ||
|
|
720988ae72 | ||
|
|
b0981a5fae | ||
|
|
ea5deeefbd | ||
|
|
054c98da7c | ||
|
|
dc434d034a | ||
|
|
a4a1b563aa | ||
|
|
3f34b83e0d | ||
|
|
9c63ef39c7 | ||
|
|
9f6106452e | ||
|
|
f9fbfe30e3 | ||
|
|
9a9bf170c6 | ||
|
|
7dd64b4f13 | ||
|
|
b6c0bac96f | ||
|
|
8ede098154 | ||
|
|
2a4a3b786e | ||
|
|
b4e0a2019e | ||
|
|
e14c3f8b59 | ||
|
|
c81f76226d | ||
|
|
edc353cc24 | ||
|
|
dcce519c69 | ||
|
|
0a16dcafc0 | ||
|
|
54d41b7f6f | ||
|
|
0541b84d09 | ||
|
|
1b420f7fe7 | ||
|
|
6a9a82ff9d | ||
|
|
aa36e6b19f | ||
|
|
51cb6aad50 | ||
|
|
b5ce409592 | ||
|
|
2119817e4a | ||
|
|
1efdfe8ea1 | ||
|
|
67d4180825 | ||
|
|
be31e190d2 | ||
|
|
a68cf3c812 | ||
|
|
c2bf9024f8 | ||
|
|
bd72eaad4c | ||
|
|
b5d1575823 | ||
|
|
1d306e5cdc | ||
|
|
b137164fe6 | ||
|
|
67d4ad50e1 | ||
|
|
c71066af4c | ||
|
|
6f0a0981bd | ||
|
|
49a4d9ba37 | ||
|
|
fcdfa741b9 | ||
|
|
e3e395836d | ||
|
|
3bab177d50 | ||
|
|
12abafb917 | ||
|
|
8dc2e47507 | ||
|
|
0be90dedf2 | ||
|
|
daf7c3357c | ||
|
|
e6bab3ceeb | ||
|
|
59387b2ae8 | ||
|
|
e8795859c5 | ||
|
|
bebe60b687 | ||
|
|
ddceec364e | ||
|
|
f8f20440d3 | ||
|
|
f8722faa4e | ||
|
|
d067fa4b73 | ||
|
|
26fbbf48a4 | ||
|
|
d5cc414221 | ||
|
|
b5e51ab937 | ||
|
|
552e55c29f | ||
|
|
a228908c1a | ||
|
|
15fc3b6c04 | ||
|
|
0456badd02 | ||
|
|
d28f154f1c | ||
|
|
399cf5118d | ||
|
|
a87f2640d3 | ||
|
|
a90ef274d7 | ||
|
|
1c02179c5c | ||
|
|
77584493fd | ||
|
|
a36709e638 | ||
|
|
341778ba67 | ||
|
|
8d63cce749 | ||
|
|
ec50b5a007 | ||
|
|
8e7bbcdbe0 | ||
|
|
37ecdc47b3 | ||
|
|
112ca3cc53 | ||
|
|
33b3a1664c | ||
|
|
8a0c02e264 | ||
|
|
31d84c8921 | ||
|
|
34bedbeef1 | ||
|
|
3b1b0985c1 | ||
|
|
e40ace9dea | ||
|
|
4c0e6e2640 | ||
|
|
08b314c37d | ||
|
|
86da9d3c7e | ||
|
|
4e61e25c02 | ||
|
|
5097d52d99 | ||
|
|
368387e1a7 | ||
|
|
09d2185bb1 | ||
|
|
5c02e2bd71 | ||
|
|
fb01389b3d | ||
|
|
aaa81210ce | ||
|
|
51269257ea | ||
|
|
23a853db1e | ||
|
|
9da430ffeb | ||
|
|
cc063124cf | ||
|
|
3b37b89951 | ||
|
|
844b537d1e | ||
|
|
caf1d4a22a | ||
|
|
d8e244df99 | ||
|
|
548e421e27 | ||
|
|
322fa7ea69 | ||
|
|
db6c887795 | ||
|
|
cf7bea0985 | ||
|
|
61fe55168a | ||
|
|
25d7f8808f | ||
|
|
1c4999ec87 | ||
|
|
c726d3f937 | ||
|
|
f70341df1b | ||
|
|
f0b791452e | ||
|
|
adf5600a9b | ||
|
|
6d4ccc5feb | ||
|
|
5f3829d5cc | ||
|
|
770ebdcd4a | ||
|
|
96f690e749 | ||
|
|
eabdd3a8f3 | ||
|
|
50650b966b | ||
|
|
65e34d4989 | ||
|
|
05dad5ab5f | ||
|
|
1a69a2f1bc | ||
|
|
435faafaad | ||
|
|
686b32dc29 | ||
|
|
0e64a22910 | ||
|
|
4f0839f27e | ||
|
|
bb1953267d | ||
|
|
acc630972a | ||
|
|
6a33be3fd8 | ||
|
|
cd8aae4681 | ||
|
|
11456496bd | ||
|
|
f5fc4e345c | ||
|
|
86eef7039f | ||
|
|
f4b2264fcf | ||
|
|
9b36ae2277 | ||
|
|
913ab03963 | ||
|
|
38448e471c | ||
|
|
67231af623 | ||
|
|
e31ef6f06f | ||
|
|
09d188106a | ||
|
|
d8e2116481 | ||
|
|
435dfbb932 | ||
|
|
ba93966474 | ||
|
|
ea8d17cdb2 | ||
|
|
082265fb25 | ||
|
|
d138a7a567 | ||
|
|
ea27d732ab | ||
|
|
9e557ce8ac | ||
|
|
924e28e9b3 | ||
|
|
e5d9af75de | ||
|
|
31c1bf8bc5 | ||
|
|
37d4055036 | ||
|
|
78b1076110 | ||
|
|
0a3c748e41 | ||
|
|
ebf79ef9e2 | ||
|
|
60a73248cd | ||
|
|
abbb7d7ba3 | ||
|
|
59c378089e | ||
|
|
0b789b5f0b | ||
|
|
4382b96a9a | ||
|
|
246e4f35a6 | ||
|
|
99b9370178 | ||
|
|
506062c6b6 | ||
|
|
d634061cd9 | ||
|
|
8353c689ca | ||
|
|
d59d8ff1fe | ||
|
|
e98e6f70ac | ||
|
|
53e442d509 | ||
|
|
134352ed7c | ||
|
|
f7cbfdff06 | ||
|
|
b28ee0819f | ||
|
|
5de626aab8 | ||
|
|
7aad5d486e | ||
|
|
701f155951 | ||
|
|
8c324d7514 | ||
|
|
522958e0e9 | ||
|
|
97390db5f5 | ||
|
|
af920d1427 | ||
|
|
779ebc0537 | ||
|
|
38949b82c3 | ||
|
|
d11386ef26 | ||
|
|
0e0377d1f0 | ||
|
|
55e0dbab27 | ||
|
|
4dc82f2c83 | ||
|
|
1ba5587404 | ||
|
|
835c4b6da3 | ||
|
|
dbd955b61e | ||
|
|
d20e2fd88c | ||
|
|
e0dea89477 | ||
|
|
ccc6b56e35 | ||
|
|
6fc2902895 | ||
|
|
c96e4b40d4 | ||
|
|
37da3e2170 | ||
|
|
2661d3c489 | ||
|
|
b89bbb9281 | ||
|
|
696bf636ed | ||
|
|
40952a788a | ||
|
|
0162e7a0c1 | ||
|
|
6ce099f176 | ||
|
|
476a4bac8e | ||
|
|
63a410a6df | ||
|
|
cca27faa3b | ||
|
|
803e6bd81a | ||
|
|
88269628a2 | ||
|
|
b920d553a0 | ||
|
|
5e2d0d0dfc | ||
|
|
2ae3bd68eb | ||
|
|
9c183f27eb | ||
|
|
8046023e82 | ||
|
|
e328520588 | ||
|
|
7eb079050c | ||
|
|
2fdd5543b2 | ||
|
|
d04164c0a6 | ||
|
|
b047731f82 | ||
|
|
4d91f92a2e | ||
|
|
98505d27b1 | ||
|
|
cd63a58ad9 | ||
|
|
170f8dd092 | ||
|
|
619dcb6a84 | ||
|
|
99ae8ea52e | ||
|
|
dc031c30eb | ||
|
|
1e702439b7 | ||
|
|
8debc42381 | ||
|
|
532d719089 | ||
|
|
b40860aca4 | ||
|
|
2cbe6b5f7f | ||
|
|
d2cc7ccdfa | ||
|
|
2cb183c6d8 | ||
|
|
84026b105f | ||
|
|
a4d0589f10 | ||
|
|
e375f6afce | ||
|
|
5a7bc04816 | ||
|
|
bd1894580e | ||
|
|
9e694c0337 | ||
|
|
c82586db28 | ||
|
|
dd2d466350 | ||
|
|
830da8de0a | ||
|
|
4e5ee333c8 | ||
|
|
9df899eb63 | ||
|
|
ca7491a702 | ||
|
|
1a07129865 | ||
|
|
4fbd67ff99 | ||
|
|
5bc6c50f42 | ||
|
|
063de3801d | ||
|
|
ae65266a4a | ||
|
|
8ed2401e0b | ||
|
|
d2e8ee8269 | ||
|
|
1f996e3b8b | ||
|
|
7108b74105 | ||
|
|
801fe1b604 | ||
|
|
fb44c87597 | ||
|
|
6b9cdbd482 | ||
|
|
0ab98033b5 | ||
|
|
14a2b96609 | ||
|
|
f829b689db | ||
|
|
dfda8b7ed5 | ||
|
|
4388466451 | ||
|
|
5c2f509a52 | ||
|
|
59582f5210 | ||
|
|
e2a8bdbdfb | ||
|
|
0916b943da | ||
|
|
9c7ebc883c | ||
|
|
0ee42b9aa0 | ||
|
|
37b3868ca3 | ||
|
|
a6835ce3f0 | ||
|
|
69c96ad99b | ||
|
|
b72877d59d | ||
|
|
05eb15d4f7 | ||
|
|
f1fec37c79 | ||
|
|
73f6880ff8 | ||
|
|
8a53742f31 | ||
|
|
9be40e85ff | ||
|
|
61079c1eb7 | ||
|
|
1075ee8fc3 | ||
|
|
a28b265197 | ||
|
|
20e534c468 | ||
|
|
da7aa5dc49 | ||
|
|
8f2a43ca0a | ||
|
|
d0909d7810 | ||
|
|
1641999d20 | ||
|
|
e16452037c | ||
|
|
344d79684a | ||
|
|
573a1d9b7b | ||
|
|
25ab57580c | ||
|
|
a332e0e3d1 | ||
|
|
376f1cb139 | ||
|
|
90f80558d7 | ||
|
|
e281994898 | ||
|
|
29fac122e1 | ||
|
|
1dc412eb90 | ||
|
|
3770a4fe0c | ||
|
|
79cda544c8 | ||
|
|
f04b97d890 | ||
|
|
3e9b4ccc45 | ||
|
|
2c3d838dd8 | ||
|
|
7668a3c660 | ||
|
|
5dd45b714a | ||
|
|
8b08895d0f | ||
|
|
8f8d99e3ed | ||
|
|
23474360ec | ||
|
|
81c255c450 | ||
|
|
ef23d52ed7 | ||
|
|
220ab773aa | ||
|
|
e3e5bff7bb | ||
|
|
7b9a841b2a | ||
|
|
40423911ef | ||
|
|
582a70b046 | ||
|
|
5b63590ebf | ||
|
|
2b6510dc19 | ||
|
|
9d49589d73 | ||
|
|
125b66c929 | ||
|
|
5255f1c052 | ||
|
|
a6ba05d60c | ||
|
|
41e963b04b | ||
|
|
6ff75bef29 | ||
|
|
72c16d0d32 | ||
|
|
94653e5c8c | ||
|
|
3e2b2a698d | ||
|
|
ae04f5aee8 | ||
|
|
5c56267662 | ||
|
|
e55ce5504e | ||
|
|
fb1e89d9ef | ||
|
|
bc550a4549 | ||
|
|
ebdea9cf76 | ||
|
|
09ec508f82 | ||
|
|
d06e9ea7f6 | ||
|
|
a36bdc54fd |
No files matched your search
@@ -0,0 +1,5 @@
|
||||
---
|
||||
description: Startup workflow to load project context
|
||||
---
|
||||
|
||||
1. Read the file `AGENTS.md` in the project root as initial system prompt to build context and instructions about the project structure, module descriptions, and development commands.
|
||||
@@ -0,0 +1,291 @@
|
||||
---
|
||||
name: asm6502-coder
|
||||
description: Writing or understanding 6502/65C02 assembly code using 64tass syntax, for use as stand alone .asm or inline within Prog8 programs
|
||||
---
|
||||
|
||||
# 65(C)02 Assembly Coder Skill
|
||||
|
||||
You are an expert MOS 6502 assembly development assistant. Keep responses concise and practical — prefer short, correct code examples over lengthy prose.
|
||||
You are working with **6502/65C02 assembly** using **64tass syntax**, in separate `*.asm` files or embedded in a Prog8 program (inside `%asm {{ }}` blocks or `asmsub` routines). Follow all rules below.
|
||||
|
||||
## Assembler: 64tass Syntax
|
||||
### Formatting Rules
|
||||
- Labels start in the first column of the line.
|
||||
- Instructions are indented at least 4 spaces.
|
||||
- Two spaces between the instruction opcode and its operand (e.g., `lda #1`, `sta $d020`).
|
||||
- End-of-line comments are preceded by two spaces before the `;` (e.g., `sta $d020 ; border color`).
|
||||
- If a comment is the only thing on a line, it starts in the first column (no indentation).
|
||||
- Opcodes and operands are written in lowercase (e.g., `lda`, not `LDA`; `$d020`, not `$D020`).
|
||||
|
||||
- **NOT ca65/cc65** or other assemblers. Key differences:
|
||||
- `.proc` / `.pend` for procedures (scoping)
|
||||
- `_label` for local labels (prefixed with underscore, scoped to `.proc`)
|
||||
- **Symbol aliases**: inside a `.proc`, use `_name = P8ZP_SCRATCH_B1` to give a scratch variable a descriptive name.
|
||||
- **Anonymous labels**: defined as `+` (forward) or `-` (backward) at the start of a line.
|
||||
- **CRITICAL DISTINCTION**: Label **definitions** are always just a single `+` or `-`. Label **references** use `+`, `++`, `+++` to refer to the 1st, 2nd, 3rd upcoming forward label, and `-`, `--`, `---` for the 1st, 2nd, 3rd preceding backward label.
|
||||
- `+` refers to the NEXT upcoming `+` label, `++` refers to the ONE AFTER that, etc.
|
||||
- `-` refers to the MOST RECENT `-` label, `--` to the one before that, etc.
|
||||
- When a `+` label is passed, the forward-reference count resets (so `++` then refers to the next one after that new label)
|
||||
- Example:
|
||||
```asm
|
||||
- dex ; backward label '-'
|
||||
bne - ; branch to most recent '-' (the dex above)
|
||||
ldx #5
|
||||
+ dex ; forward label '+'
|
||||
bne + ; branch to this same '+' (forward)
|
||||
sta $400
|
||||
+ lda #0 ; second '+' label definition (just '+')
|
||||
bne ++ ; branch to the '++' below (third forward '+')
|
||||
rts
|
||||
+ inc $d020 ; third forward label definition (just '+', referred to as '++')
|
||||
```
|
||||
- Data directives: `.byte`, `.word`, `.dword`, `.fill` (allocate space)
|
||||
- Equates: `label = value` (not `label .equ value` or `#define`)
|
||||
- **Number Literals**: Hex `$1234`, Binary `%10101010`, Decimal `123`.
|
||||
- Zero-page variables defined with `=`
|
||||
- `.text` for inline string data
|
||||
- **Conditional assembly**: `.if`, `.elsif`, `.else`, `.endif`
|
||||
- **Memory Sections**: `.section <name>`, `.send <name>` (common sections: `CODE`, `DATA`, `BSS`, `BSS_NOCLEAR`)
|
||||
|
||||
## Instructions
|
||||
- **Instructions like `rol`, `ror`, `asl`, `lsr` (and `inc`, `dec` on 65C02) require an explicit operand for accumulator**: write `rol a`, `inc a`, etc., not just `rol` or `inc`.
|
||||
- Standard 6502 addressing modes: implied, immediate (`#`), zero-page (`zp`), zero-page,X (`zp,x`), absolute (`abs`), absolute,X (`abs,x`), absolute,Y (`abs,y`), indirect (`(abs)`), indirect,X (`(zp,x)`), indirect,Y (`(zp),y`), relative (branches), accumulator
|
||||
- Branches: `bne`, `beq`, `bmi`, `bpl`, `bcs`, `bcc`, `bvs`, `bvc` (relative, max +127/-128 bytes)
|
||||
- Jumps: `jmp` (absolute or indirect), `jsr`/`rts` (subroutine call/return)
|
||||
- No `push`/`pop` mnemonics — use `pha`/`pla` (byte) and `txa`/`phx`/`plx`/`tay`/`phy`/`ply` for registers
|
||||
|
||||
### Instruction Side Effects (Flags)
|
||||
- **`Z` (Zero)**: Set if the result of an operation is 0.
|
||||
- **`N` (Negative)**: Set if bit 7 of the result is 1.
|
||||
- **`C` (Carry)**: Used for unsigned overflow and shifts. `cmp` sets `C` if `Register >= Operand`.
|
||||
- **`V` (Overflow)**: Set if a signed arithmetic operation overflowed.
|
||||
- **Commonly affected by**: `lda`, `ldx`, `ldy`, `inx`, `dex`, `tax`, `tay`, `txa`, `tya`, `and`, `ora`, `eor`, `asl`, `lsr`, `rol`, `ror`, `adc`, `sbc`, `cmp`, `cpx`, `cpy`, `bit`.
|
||||
- **Note**: `lda`, `ldx`, `ldy` do NOT affect the Carry flag. Only `adc`, `sbc`, `cmp`, and shift/rotate instructions affect Carry.
|
||||
|
||||
## 6502 vs 65C02
|
||||
- **CX16 target only**: can use WDC 65C02 instructions — `stz`, `phx`, `plx`, `phy`, `ply`, `bra`, `trb`, `tsb`, `stp`, `wai`, `inc a`, `dec a`, `bit #imm`, `bit zp,x`, `bit abs,x`, `jmp (abs,x)`, and `(zp)` indirect addressing mode (e.g., `lda (zp)`).
|
||||
- **C64, C128, PET32 targets**: original 6502 only — no `stz`, no `phx`/`plx`/`phy`/`ply`, no `bra` etc.
|
||||
- **Note**: The Rockwell/bit-manipulation instructions (`rmb`, `smb`, `bbr`, `bbs`) are **NOT** available.
|
||||
- Check the target before using 65C02-specific instructions
|
||||
- **6502 / 65C02 instruction reference table**: https://www.pagetable.com/c64ref/6502/?cpu=65c02&tab=4 (provides exact instruction details for all opcodes: operation, addressing modes, byte length, and cycle count).
|
||||
|
||||
## Commander X16 Memory Map
|
||||
```
|
||||
Bank Offset Content
|
||||
$0000-$9EFF Fixed/System RAM
|
||||
$9F00-$9FFF I/O Area (VIA, VERA, YM2151)
|
||||
$00-$FF:$A000-$BFFF Banked RAM (max 2 MiB, often 512 KiB) (256x8K banks)
|
||||
$00-$1F:$C000-$FFFF System ROM (512 KiB) (32x16K banks)
|
||||
```
|
||||
- Bank registers (in zeropage): `$00` selects the current RAM bank (0-255), `$01` selects the current ROM bank (0-31). For JSRFAR/banked calls from Prog8, use `extsub @bank ...` instead of manipulating these directly.
|
||||
- Full hardware specs at https://ayce.dev/emptyx16.html#emptyx16---x16-hardware-specs
|
||||
|
||||
## Calling Convention / Register Conventions
|
||||
- **Accumulator (A)**: 8-bit, used for most arithmetic, data movement, return values
|
||||
- **X register**: 8-bit, often used for indexing, loop counters
|
||||
- **Y register**: 8-bit, often used for indirect addressing index
|
||||
- **Processor Status (P)**: flags — carry (C), zero (Z), negative/N (sign bit 7), overflow (V), decimal (D), interrupt (I), break (B)
|
||||
- **No caller-saved vs callee-saved convention** — list all modified registers in `clobbers (A, X, Y)` when writing `asmsub`
|
||||
- The CPU stack (SP, $0100-$01FF) is limited (usually ~128 bytes free). Do not overflow it
|
||||
|
||||
## Assembly within Prog8 Programs
|
||||
|
||||
### `%asm {{ }}` blocks
|
||||
- Embed arbitrary 64tass assembly directly in your Prog8 source
|
||||
- Access Prog8 symbols using their prefixed names (see below)
|
||||
- Can be placed inside subroutines or at block level
|
||||
|
||||
### `asmsub` (assembly subroutine)
|
||||
- For kernel (ROM) routines or low-level assembly
|
||||
- Parameters passed via registers: `@A`, `@X`, `@Y`, `@AX` (A low, X high), `@AY` (A low, Y high), `@R0`-`@R15`, `@FAC1`/`@FAC2` (float), `@Pc` (carry), `@Pz` (zero)
|
||||
- Return value: `-> type @register` — also via `@Pz`/`@Pc` for flags
|
||||
- Clobbers: `clobbers (A, X, Y)` — MUST list all modified registers
|
||||
- **CRITICAL**: Parameter names in `asmsub` are **documentation only**. You MUST use the actual registers in your assembly code, NOT the parameter names (unless you create aliases yourself).
|
||||
- Create symbolic aliases at assembly top for clarity: `x1 = cx16.r0`, `y1 = cx16.r0L`
|
||||
- Accessing Prog8 parameters if they were NOT mapped to registers: use `p8v_paramname`. (Mapping to registers is preferred for speed).
|
||||
|
||||
Example:
|
||||
```prog8
|
||||
asmsub line(uword x1 @R0, ubyte y1 @A, uword x2 @R1, ubyte y2 @Y) clobbers (A, X, Y) {
|
||||
%asm {{
|
||||
x1 = cx16.r0
|
||||
x2 = cx16.r1
|
||||
lda x1 ; use alias, not "_x1"
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
### `asmsub` parameter annotation reference
|
||||
| Annotation | Register | Size |
|
||||
|------------|----------|------|
|
||||
| `@A` | Accumulator | 8-bit |
|
||||
| `@X` | X register | 8-bit |
|
||||
| `@Y` | Y register | 8-bit |
|
||||
| `@AX` | A (low) + X (high) | 16-bit |
|
||||
| `@AY` | A (low) + Y (high) | 16-bit |
|
||||
| `@R0`-`@R15` | cx16 virtual registers | 16-bit each |
|
||||
| `@FAC1`/`@FAC2` | Floating-point accumulators | 5-byte float |
|
||||
| `@Pc` | Carry flag | bool |
|
||||
| `@Pz` | Zero flag | bool |
|
||||
|
||||
## Accessing Prog8 Symbols from Assembly
|
||||
All Prog8 symbols are prefixed when accessed from assembly:
|
||||
|
||||
| Prefix | Refers to | Example |
|
||||
|--------|-----------|---------|
|
||||
| `p8v_` | Variables, parameters | `p8v_myvar` |
|
||||
| `p8s_` | Subroutines | `p8s_mysub` |
|
||||
| `p8b_` | Blocks | `p8b_myblock` |
|
||||
| `p8c_` | Constants, enum members | `p8c_myconst`, `p8c_MyEnum_Member` |
|
||||
| `p8l_` | Labels | `p8l_mylabel` |
|
||||
| `p8t_` | Struct types | `p8t_MyStruct` |
|
||||
| `p8_` | Other symbols | |
|
||||
|
||||
- **Fully qualified**: e.g., `p8b_myblock.p8v_myvar`, `p8b_myblock.p8s_mysub.p8v_localvar`
|
||||
- Within a `.proc` (subroutine) in your assembly, short names often work (assembler scoping)
|
||||
- **`%option no_symbol_prefixing`**: disables all prefixes. Stdlib modules (`cbm`, `cx16`, `txt`) use this — you can write `cbm.CHROUT` directly
|
||||
- **Split word arrays**: two separate byte arrays — append `_lsb` and `_msb` to the name: `p8v_myarray_lsb`, `p8v_myarray_msb`
|
||||
|
||||
## Zeropage Usage
|
||||
- Do NOT use arbitrary zeropage locations. Only use these predefined scratch variables:
|
||||
- `P8ZP_SCRATCH_B1` (byte)
|
||||
- `P8ZP_SCRATCH_REG` (byte)
|
||||
- `P8ZP_SCRATCH_W1` (word)
|
||||
- `P8ZP_SCRATCH_W2` (word)
|
||||
- `P8ZP_SCRATCH_PTR` (word)
|
||||
- On CX16: virtual registers `cx16.r0`-`cx16.r15` are in zeropage (and their low/high bytes: `cx16.r0L`, `cx16.r0H`, etc.)
|
||||
- Virtual registers `cx16.r0`-`cx16.r15` are available on ALL targets, but only on CX16 in zeropage
|
||||
- Assume the scratch variables are not consecutive in zeropage. The CX16 virtual registers ARE consecutive in memory though.
|
||||
- For additional temporary storage, allocate regular variables in BSS
|
||||
- **CRITICAL: NEVER use hardcoded zeropage addresses** (like `$1e`, `$22`, `$7c`, etc.) to reference P8ZP_SCRATCH registers or cx16 virtual registers. Their addresses differ per compilation target. **ALWAYS** use the symbolic names (`P8ZP_SCRATCH_W1`, `cx16.r14`, `cx16.r14+1`, etc.) so the assembler resolves the correct address for the target.
|
||||
|
||||
## Common 6502 Patterns
|
||||
|
||||
### Common Branch Logic (Comparisons)
|
||||
| Logic | Unsigned | Signed |
|
||||
|-------|----------|--------|
|
||||
| `A == imm` | `cmp #imm`, `beq label` | (Same) |
|
||||
| `A != imm` | `cmp #imm`, `bne label` | (Same) |
|
||||
| `A < imm` | `cmp #imm`, `bcc label` | `sec`, `sbc #imm`, `bvc *+4`, `eor #$80`, `bmi label` |
|
||||
| `A >= imm` | `cmp #imm`, `bcs label` | `sec`, `sbc #imm`, `bvc *+4`, `eor #$80`, `bpl label` |
|
||||
| `A <= imm` | `beq label`, `bcc label` | (Use complex signed logic or reorder) |
|
||||
| `A > imm` | `beq +`, `bcs label`, `+` | (Use complex signed logic or reorder) |
|
||||
|
||||
### Looping (downto with BNE)
|
||||
```asm
|
||||
ldx #count
|
||||
loop ; do work here
|
||||
dex
|
||||
bne loop ; loop while X != 0 (runs 'count' times)
|
||||
```
|
||||
|
||||
### Self-Modifying Code (SMC) — Avoid When Possible
|
||||
**Do NOT use self-modifying code unless absolutely necessary** — self-modifying code writes to the code segment at runtime, which means it **cannot run from ROM** and is generally fragile, hard to debug, and violates the expected behavior of modern tooling (emulators, debuggers, etc.). Prefer lookup tables, indirect jumps via a vector in RAM, or alternative algorithms that keep code in place.
|
||||
|
||||
### Self-Modifying Code (SMC) Detection
|
||||
Look for `sta`, `stx`, or `sty` pointing into code labels:
|
||||
```asm
|
||||
lda #$42
|
||||
sta _target+1 ; Modifies the immediate operand of the LDA at _target
|
||||
...
|
||||
_target lda #$00 ; This #$00 will be replaced by #$42 at runtime
|
||||
```
|
||||
|
||||
### Indirect indexed read (table of data)
|
||||
```asm
|
||||
ldy #index
|
||||
lda (ptr),y ; read byte at address stored in zp ptr + Y
|
||||
```
|
||||
|
||||
### 16-bit arithmetic (word add)
|
||||
```asm
|
||||
clc
|
||||
lda word1_lo
|
||||
adc word2_lo
|
||||
sta result_lo
|
||||
lda word1_hi
|
||||
adc word2_hi
|
||||
sta result_hi
|
||||
```
|
||||
|
||||
### Calling a Prog8 subroutine from assembly
|
||||
```asm
|
||||
jsr p8s_myblock.p8s_mysub
|
||||
```
|
||||
|
||||
### Reading a word variable
|
||||
```asm
|
||||
lda p8v_myword ; loads LSB
|
||||
ldy p8v_myword+1 ; loads MSB (word variables are stored LSB-first)
|
||||
```
|
||||
|
||||
## CPU Quirks and Pitfalls
|
||||
|
||||
### JMP ($xxFF) Page Wrap Bug (NMOS 6502)
|
||||
- **Problem**: On original 6502 CPUs, `jmp ($caff)` will fetch the LSB from `$caff` but the MSB from `$ca00` (instead of `$cb00`).
|
||||
- **Target**: Affects C64, C128, PET32.
|
||||
- **Solution**: Avoid placing indirect jump vectors on a page boundary, or use the CX16 (65C02) which fixed this bug.
|
||||
|
||||
### BRK Instruction and the "Signature Byte"
|
||||
- **Behavior**: After a `BRK` instruction, the return address on the stack is incremented by **2**. This means the CPU skips the byte immediately following the `BRK` opcode.
|
||||
- **Usage**: This skipped byte is often used as a "signature" or parameter byte for the BRK handler.
|
||||
|
||||
### BIT Instruction Flags
|
||||
- **Absolute/Zero-page**: `bit $1234` copies bit 7 of the memory value to the **N** flag and bit 6 to the **V** flag. The **Z** flag is set based on `A AND memory`.
|
||||
- **Immediate (65C02 only)**: `bit #$01` only affects the **Z** flag; it does **NOT** modify N or V.
|
||||
- **NMOS 6502**: Does **not** support `bit #imm`.
|
||||
|
||||
### The "B" (Break) Flag
|
||||
- **Quirk**: The B flag (bit 4 of the status register) doesn't actually exist in the hardware status register. It only exists on the stack after a `PHP` or `BRK` instruction (set to 1) or a hardware IRQ/NMI (set to 0).
|
||||
- **Detection**: To tell if an interrupt was caused by `BRK` or a hardware IRQ, your handler must `pla`, `and #$10`, and check the result.
|
||||
|
||||
### Decimal Mode Flag (D) Persistence
|
||||
- **Pitfall**: On NMOS 6502, the `D` flag is **not** cleared on interrupt. Always use `cld` in IRQ handlers. On 65C02, it is cleared automatically, but `cld` is still good practice.
|
||||
- **ADC/SBC**: Be extremely careful with arithmetic if you haven't explicitly set or cleared the `D` flag, as its state might be unknown.
|
||||
|
||||
## Optimization Tips
|
||||
- **`stz` (65C02 only)**: Saves cycles and bytes compared to `lda #0`, `sta ...`.
|
||||
- **`bra` (65C02 only)**: Shorter and usually faster than `jmp`.
|
||||
- **Avoid `clc` before `bcc`**: `cmp` already sets the carry flag correctly for `bcc`/`bcs`.
|
||||
- **`inx` / `dex` vs `clc`+`adc #1`**: Incrementing/decrementing is faster and doesn't affect the carry flag.
|
||||
- **Zero-page usage**: Accessing variables in zeropage is 1 cycle faster and 1 byte shorter than absolute addressing.
|
||||
- **Cycle Counting**: Most instructions take 2-4 cycles. `jsr` takes 6, `rts` takes 6. Branches take 2 (no branch), 3 (branch taken), or 4 (branch taken across page boundary).
|
||||
|
||||
## 64tass Macros
|
||||
Define macros for common tasks:
|
||||
```asm
|
||||
pushax .macro
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
.endm
|
||||
|
||||
popax .macro
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
.endm
|
||||
```
|
||||
Call them with `#pushax` and `#popax`.
|
||||
|
||||
## IRQ Handler Best Practices
|
||||
- **Clear the Decimal Flag (`cld`)**: On the original 6502, the decimal flag (`D`) is **not** automatically cleared when an interrupt occurs. If the interrupted code was in decimal mode, your handler will also run in decimal mode, causing arithmetic errors. Always call `cld` at the beginning of your handler. The 65C02 clears it automatically, but `cld` is still recommended for portability.
|
||||
|
||||
## Invoking the Assembler (64tass)
|
||||
If you need to manually invoke `64tass` to assemble a generated `.asm` file, you should be aware of the default arguments that `prog8c` supplies to ensure compatibility with the generated code:
|
||||
|
||||
- `--ascii`: **CRITICAL.** Prog8 generates character and string data in ASCII. Without this flag, `64tass` defaults to PETSCII, which will garble your strings.
|
||||
- `--case-sensitive`: Prog8 is case-sensitive and expects the assembler to be as well.
|
||||
- `--long-branch`: Enables automatic conversion of relative branches (`beq`, `bne`, etc.) to absolute jumps if the target is out of range. Prog8 relies on this.
|
||||
- `-Wno-implied-reg`: Suppresses warnings when the accumulator `a` is omitted from instructions like `rol`, `lsr`, etc. (though the skill recommends always using `rol a` for clarity).
|
||||
- `-Wall`: Enables all warnings.
|
||||
- `--cbm-prg` (or `--atari-xex` / `--nostart`): Sets the output format and adds the appropriate load address header.
|
||||
|
||||
### Optional but Recommended for Debugging:
|
||||
- `--vice-labels --labels=labels.txt`: Generates a label file that can be loaded into the VICE monitor (`load_labels "labels.txt"`) to see your Prog8 symbol names while debugging.
|
||||
- `--list=listing.txt`: Generates a full assembly listing file with addresses and opcodes.
|
||||
|
||||
### Example manual invocation:
|
||||
```bash
|
||||
64tass --ascii --case-sensitive --long-branch -Wall --cbm-prg -o myprogram.prg myprogram.asm
|
||||
```
|
||||
@@ -0,0 +1,248 @@
|
||||
---
|
||||
name: prog8-coder
|
||||
description: Writing or understanding Prog8 programs
|
||||
---
|
||||
|
||||
# Prog8 Coder Skill
|
||||
|
||||
You are an expert retro system development assistant, versed in the Prog8 programming language. Keep responses concise and practical — prefer short, correct code examples over lengthy prose.
|
||||
You are working with **Prog8** source code (`.p8` files) or its Intermediate Representation (`.p8ir` files). Prog8 targets 8-bit systems (C64, CX16, C128, PET32) with the 6502 CPU, plus a `virtual` target for testing.
|
||||
Follow ALL the rules below carefully.
|
||||
|
||||
## General & Setup
|
||||
- A program = a `main` block containing a `start` subroutine entry point, plus optional other subroutines/blocks
|
||||
- Add `%zeropage basicsafe` at the top of your program to allow clean return on exit (instead of resetting the machine/emulator)
|
||||
- Module imports: `%import modulename` — no `as` aliasing. Use the module's defined prefix (e.g., `%import textio` → `txt.xxx`)
|
||||
- `.p8ir` files are the **Intermediate Representation** — target-independent, executable via `prog8c -vm file.p8ir`
|
||||
- **Library search**: `prog8c -libsearch <regex>` to find routines in the embedded stdlib (e.g., `prog8c -libsearch "txt\."`)
|
||||
- **Library dump**: `prog8c -libdump <dir>` to extract all embedded library source files
|
||||
- **`sys` module**: always available, no import needed
|
||||
- **CX16 programs**: add `%encoding iso`, call `txt.iso()` in `start()`, end with `sys.poweroff_system()`
|
||||
|
||||
## Datatypes & Variables
|
||||
- Primitives: `bool`, `byte`, `ubyte`, `word`, `uword`, `long`, `float`, `str`
|
||||
- `ubyte`/`uword` = unsigned; `long` = signed 4-byte; `float` = 5-byte MS format; `str` = 0-terminated ubytes (max 255 chars)
|
||||
- **float requires `%import floats`** at top of file, else compiler errors
|
||||
- Arrays: max 256 bytes (512 for split word arrays). For larger data, use `memory()` + pointers
|
||||
- `memory(name, size)` returns a `uword` address to a statically reserved block of memory
|
||||
- To point a typed pointer at a `memory()` block, assign the uword directly: `^^MyStruct ptr = memory("name", size)`. The `^^Type:expression` syntax (below) only works with array literals, not general uword expressions.
|
||||
- Struct initialization: `^^StructType ptr = ^^StructType:[val1,val2,...]` (the `^^StructType:` can be omitted if inferable). This `^^Type:[...]` syntax does NOT work with variables or `memory()` — only literal arrays.
|
||||
- Struct definitions must be inside a block, not at file level.
|
||||
- Struct fields: simple types, `str`, and fixed-size arrays. Array fields: `ubyte[108] name`. `str` in struct = `^^ubyte`
|
||||
- Word/pointer arrays split into LSB/MSB by default. Override with `@nosplit`
|
||||
- **No call stack**: all variables statically allocated. No recursion without manual stack management
|
||||
- Variables zero-initialized (globals at start, locals on subroutine entry)
|
||||
- `@shared`, `@zp`, `@requirezp`, `@dirty`, `@nosplit` are **tags** that go on variable declarations. **Place them after the datatype (and array specifiers), before the variable name(s):**
|
||||
```
|
||||
private ubyte[8] @shared vera_storage ; single var with tag
|
||||
ubyte @zp @shared varname ; multiple tags allowed
|
||||
ubyte @requirezp var1 ; require zeropage
|
||||
ubyte[] @shared names ; array with tag
|
||||
```
|
||||
The grammar is: `[private] datatype [arraydims] [tags...] identifierlist`
|
||||
- `@shared` marks variables as "used by external code" (assembly), prevents the optimizer from removing them
|
||||
- `@zp`/`@requirezp`: use sparingly — only for pointers (limited zeropage space)
|
||||
- Pointer-like typed pointers (`^^type`) support C-style scaled arithmetic; `uword` pointers always treat element as 1 byte
|
||||
- `&` = untyped address (uword); `&&` = typed pointer
|
||||
- Available zeropage scratch: `P8ZP_SCRATCH_B1`, `P8ZP_SCRATCH_REG`, `P8ZP_SCRATCH_W1`, `P8ZP_SCRATCH_W2`, `P8ZP_SCRATCH_PTR` — and cx16 virtual registers R0-R15 on all targets
|
||||
- Virtual registers (`cx16.r0`–`cx16.r15`): global 16-bit, NOT preserved across calls.
|
||||
- **WARNING: Virtual registers in ISR/IRQ handlers**: The virtual registers R0-R15 are *not preserved* across the IRQ handler call. If your handler uses them, it will corrupt the interrupted program's state. Either avoid using them in the handler, or save/restore with `cx16.save_virtual_registers()` / `cx16.restore_virtual_registers()`. This applies to all targets, not just CX16.
|
||||
- **WARNING: Long operations clobber R12-R15**: Some operations on `long` values use R12-R15 as temporary storage and will silently overwrite them. Do not rely on R12-R15 values when working with longs, and avoid using R12-R15 explicitly if your code uses long arithmetic.
|
||||
- **WARNING: VERA registers in ISR handlers (CX16)**: If your IRQ handler reads or writes VERA control registers (e.g., `cx16.VERA_DATA0`, `cx16.VERA_ADDR_L`, etc.), you must save and restore the VERA context around the handler's work using `cx16.save_vera_context()` / `cx16.restore_vera_context()`. Without this, the handler will corrupt any VERA operations (tilemap updates, sprite positioning, etc.) happening in the interrupted main program.
|
||||
- **IRQ handler best practices**: Keep handlers extremely short and fast — they run with interrupts disabled and steal cycles from the main program. Do NOT do lengthy processing, I/O, or complex subroutine calls inside the handler. Instead, set a boolean flag or semaphore that the main loop checks periodically, and do the actual work there.
|
||||
- Math performance: integer trig (`math.sin8`, `math.cos8`) uses fast LUTs; float trig (`floats.sin`/`cos`) is much slower
|
||||
|
||||
## Recursion & Stack Management
|
||||
No call stack for variable storage — recursion overwrites locals. To handle it:
|
||||
1. **CPU hardware stack**: `push()`/`pushw()`/`pushl()`/`pushf()` and `pop()`/`popw()`/`popl()`/`popf()`. Save/restore locals around recursive calls
|
||||
2. **Software stacks**: `buffers.stack` (uword) / `buffers.smallstack` (ubyte). Both provide `push_b()`/`push_w()` and `pop_b()`/`pop_w()`
|
||||
3. **Iterative rewrite (preferred)**: Many recursive algorithms work as `repeat` loops with explicit bounds — avoids all stack overhead
|
||||
|
||||
## Strings, Arrays & Pointers
|
||||
- `str` / array: max 256 bytes. `long[]` limited to 64 entries (64x4=256). `str[]` for string arrays: `str[5] names = ["a","b","c","d","e"]`
|
||||
- 2D arrays: `type[rows][cols] name`, access `name[r][c]`. Flat init list only (no nested `[[...]]`). Total size still ≤ 256 bytes
|
||||
- str/array passed as pointer to subroutine (receiving subroutine gets `^^ubyte` or `^^element`)
|
||||
- **No const pointers** or pointer-to-pointer currently supported
|
||||
- **Parsing limitation**: `pointer[index].field` as assignment target needs `^^`: `pointer[index]^^.field = value`
|
||||
- **Pre-allocate buffers**: `str buffer = "." * 50` (empty `""` allocates nothing — `strings.append()` will fail)
|
||||
- **No reassignment**: can't `buffer = "new text"` after declaration. Use `strings.copy()`/`strings.ncopy()`
|
||||
- String concat is expensive on 6502. Prefer separate prints over concatenation
|
||||
- **Efficient buffer iteration**: prefer `ptr++` + `@(ptr)` over `@(buffer + offset)`. Exception: if offset is a `ubyte` (≤ 255), `buffer[offset]` works fine
|
||||
- `@(ptr)` = peek/poke byte. For words: `peekw`/`pokew`, longs: `peekl`/`pokel`, floats: `peekf`/`pokef`, bools: `peekbool`/`pokebool`
|
||||
- Assigning a uword address to a `str` field of a struct is done by direct assignment. Compute the address in a uword variable first, then assign: `cx16.r0 = &namebufs + offset; entry.name = cx16.r0`. The `^^ubyte:(expr)` cast syntax does not parse — use a temp uword instead.
|
||||
- Use `len(array)` instead of hardcoded sizes
|
||||
- Array indexing is 0-based: `arr[0]` is first element
|
||||
- Static memory only — real dynamic allocation impossible, but can emulate with a simple arena allocator over a `memory()` slab
|
||||
|
||||
## Logic & Control Flow
|
||||
- Logical operators (short-circuit, bool only): `and`, `or`, `xor`, `not`
|
||||
- In `a and b`: if `a` is false, `b` is NOT evaluated
|
||||
- In `a or b`: if `a` is true, `b` is NOT evaluated
|
||||
- Important when `b` has side effects
|
||||
- Bitwise operators: `&`, `|`, `^`, `~`, `<<`, `>>`
|
||||
- Bit rotation: `rol()`/`ror()` (through carry), `rol2()`/`ror2()` (no carry)
|
||||
- CPU status flag branches: `if_cs`, `if_cc`, `if_z`, `if_nz` (compile to single 6502 branch instructions)
|
||||
- If-expressions for simple value assignments based on a choice
|
||||
- **Optional braces in if/else**: when the `if` or `else` body is a single statement, the `{ }` can be omitted. Place the statement on the next line, indented. Example:
|
||||
```
|
||||
if x < 5
|
||||
txt.print("small")
|
||||
```
|
||||
- `defer` defers statement execution until scope exit. Multiple defers fire in **reverse registration order** (LIFO / stack order — last deferred runs first). A defer is only registered if execution reaches that statement — conditional paths that skip the `defer` line will not register it.
|
||||
- `goto`, labels, jump lists allowed
|
||||
- **Common mistake**: `and`/`or` for bitmasking — use `&`/`|` instead!
|
||||
|
||||
### The when Statement
|
||||
The `when` statement is a control flow construct that enables you to execute a specific action based on the value of an expression. It is generally more readable and often more efficient than a sequence of `if-else if` statements, as the compiler can optimize it into more efficient branching structures, such as a jump table.
|
||||
- **Expression**: Evaluates an expression and compares it against case values.
|
||||
- **Cases**: Defined by a value followed by the `->` operator.
|
||||
- **Blocks**: Use `{ }` to enclose multiple statements for a case.
|
||||
- **Else Clause**: Serves as a default handler; mandatory unless the expression type is fully covered.
|
||||
- **Efficiency**: Recommended for handling sets of fixed choices as it typically results in better assembly code.
|
||||
|
||||
## Loop Constructs
|
||||
Prog8 supports these loop types. All support `break` and `continue` (except `unroll`).
|
||||
|
||||
### `for` loop — iterate over a range or array
|
||||
- Loop variable **must be declared separately** before the `for` statement
|
||||
- Works with `ubyte`, `byte`, `uword`, `word`, `long`, pointer types (NOT `float`)
|
||||
- Iterates over ranges (`start to end`), descending ranges (`start downto end`), or arrays/strings
|
||||
- Optional `step <constant>` for non-unit step sizes
|
||||
- Loop variable value **after the loop is undefined** — don't rely on it
|
||||
- Descending loops with `downto` usually produce more efficient 6502 code
|
||||
```
|
||||
ubyte i
|
||||
for i in 20 to 155 {
|
||||
; body
|
||||
break ; exit loop
|
||||
continue ; next iteration
|
||||
}
|
||||
|
||||
; descending
|
||||
for i in 155 downto 20 {}
|
||||
for i in 155 to 20 step -1 {}
|
||||
|
||||
; iterate over array elements
|
||||
uword[] fib = [0, 1, 1, 2, 3, 5, 8, 13]
|
||||
uword num
|
||||
for num in fib {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### `while` loop — repeat while condition is true
|
||||
```
|
||||
while condition {
|
||||
; body
|
||||
break
|
||||
continue
|
||||
}
|
||||
```
|
||||
|
||||
### `do`-`until` loop — always executes body at least once
|
||||
```
|
||||
do {
|
||||
; body
|
||||
break
|
||||
continue
|
||||
} until condition
|
||||
```
|
||||
|
||||
### `repeat` loop — repeat a fixed number of times (most efficient)
|
||||
- Most efficient code generation — prefer over `for` when loop variable not needed
|
||||
- Omit count for infinite loop (still supports `break`)
|
||||
```
|
||||
repeat 15 {
|
||||
; body
|
||||
break
|
||||
continue
|
||||
}
|
||||
|
||||
; infinite:
|
||||
repeat {
|
||||
; body
|
||||
break if x==5
|
||||
}
|
||||
```
|
||||
|
||||
### `unroll` loop — compile-time code duplication
|
||||
- Not a real loop — duplicates body N times at compile time
|
||||
- No `break`/`continue` allowed
|
||||
- Only simple statements (assignments, calls) in body
|
||||
- Constant iteration count required
|
||||
```
|
||||
unroll 80 {
|
||||
cx16.VERA_DATA0 = 255
|
||||
}
|
||||
```
|
||||
|
||||
## Subroutines & Return Values
|
||||
- **Don't use `private` on subroutines and variables** (including nested ones) unless the user asks for it. Everything is public by default in Prog8 — follow that convention.
|
||||
- `inline` keyword for subroutines to suggest inlining
|
||||
- No **function overloading** (except builtins) and no **polymorphism** in general. This means you must call specific routines for different types (e.g., `txt.print_ub(val)` vs `txt.print_w(val)` instead of a generic `print(val)`). Cannot use builtin names (msw, lsw, msb, lsb, mkword, mklong, peek, peekw, peekl, etc.) as variable/sub names
|
||||
- Can return 0, 1, or multiple values: `a, b, c = routine()`. Use `void` to skip: `void routine()`, `a, void, c = routine()`
|
||||
- Nested subroutines access parent scope variables directly
|
||||
|
||||
## Assembly Subroutines (asmsub)
|
||||
For low-level assembly that gets arguments via registers and returns values in registers.
|
||||
- **Syntax**: `[private] [inline] asmsub subname(params) [clobbers(regs)] [-> returns] { %asm {{ ... }} }`
|
||||
- **Body Restriction**: The body of an `asmsub` **must only contain** a single `%asm {{ ... }}` node. Regular Prog8 statements or nested blocks are NOT allowed.
|
||||
- **Parameters**: `type name @register` (e.g., `ubyte val @A`, `uword addr @AX`, `float f @FAC1`).
|
||||
- **Return Values**: `-> type @register` (e.g., `-> ubyte @A`, `-> bool @Pz` for immediate branch use).
|
||||
- **Registers**:
|
||||
- **8-bit**: `@A`, `@X`, `@Y`
|
||||
- **16-bit**: `@AX`, `@AY`, `@XY` (register pairs)
|
||||
- **Float**: `@FAC1`, `@FAC2` (Floating Point Accumulators)
|
||||
- **Virtual**: `@R0`–`@R15` (16-bit), `@R0R1`–`@R14R15` (32-bit combined)
|
||||
- **Status Flags (for returns)**: `@Pc` (Carry), `@Pz` (Zero), `@Pv` (Overflow), `@Pn` (Negative)
|
||||
- **Clobbers**: `clobbers (A, X, Y)` — list all hardware registers modified by the routine.
|
||||
- **Parameter names** are for documentation and type checking only. Use the registers in your assembly code.
|
||||
- **Inlining**: `inline asmsub` will paste the assembly code directly at the call site, avoiding `jsr`/`rts` overhead.
|
||||
|
||||
## External Subroutines (extsub)
|
||||
Used to call routines at fixed memory addresses (like ROM KERNAL routines or third-party drivers).
|
||||
- **Syntax**: `[private] extsub [@bank <value>] address = subname(params) [clobbers(regs)] [-> returns]`
|
||||
- **Address**: Can be a hex literal (`$C000`) or a constant expression.
|
||||
- **Bank (optional)**: `@bank <integer>` (constant bank) or `@bank <identifier>` (variable bank).
|
||||
- **No Body**: Unlike `asmsub`, `extsub` has no `{ }` body; it just maps a signature to an address.
|
||||
- **Example**:
|
||||
```prog8
|
||||
; CX16 KERNAL CHROUT
|
||||
extsub $FFD2 = chrout(ubyte char @A) clobbers(A, X, Y)
|
||||
|
||||
; Routine in a specific RAM bank
|
||||
extsub @bank 10 $C09F = audio_init() clobbers(A, X, Y) -> bool @Pc
|
||||
```
|
||||
|
||||
## Inline Assembly
|
||||
When writing inline assembly (`%asm {{ }}` blocks or `asmsub` routines), load the **asm6502-coder** skill for formatting rules, symbol references, anonymous labels, and 64tass syntax details.
|
||||
|
||||
|
||||
|
||||
## Standard Library
|
||||
- Find routines, functions, variables, modules and signatures in the symbol dump file for the given compilation target.
|
||||
- Online location: https://prog8.readthedocs.io/en/latest/libraries.html#low-fi-variable-and-subroutine-definitions-in-all-available-library-modules they are linked there 1 for each compilation target
|
||||
- Structure: builtin functions, then module sections with variables/constants (`type name`) and subroutines (`name (params) -> returntype`)
|
||||
- Text output: `textio` module (`txt.print`, `txt.chrout`, `txt.print_b`/`_ub`/`_w`/`_uw`/`_l`/`_bool`, `txt.print_f` for floats, `txt.spc()`, `txt.nl()`). **Note: Prog8 has no function overloading**, so you cannot use `txt.print(number)` — you must call the specific routine for the type (e.g., `txt.print_ub(val)` for an unsigned byte).
|
||||
- Math: `math` module — integer trig (`sin8`, `cos8`) via fast LUTs; `math.rnd()` for random numbers
|
||||
- String conversion: `conv` module (`str_uword`, `str2word`, etc.) — for printing numbers use txt routines instead
|
||||
- Char operations: `strings` module (`isdigit`, `isxdigit`, `isupper`, `islower`, `isletter`, `isspace`, `isprint`) — use these instead of manual ASCII/PETSCII arithmetic
|
||||
- String functions return useful lengths — capture them: `len = strings.copy(dest, src)`, `len = strings.append(buf, text)`, `len = strings.upper(mystr)`
|
||||
|
||||
## Syntax & Formatting
|
||||
- Hex: `$FF` (not `0xFF`); Binary: `%1010` (not `0b1010`). Underscores for readability: `25_000_000`
|
||||
- 4-digit hex `$0000` = uword. No type suffixes (no `0L`). Cast: `expr as type`
|
||||
- Augmented assignment: `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=`
|
||||
- **`;` starts a comment to end of line** — NOT a statement separator. One statement per line
|
||||
- **No `elif`**: use nested `else { if ... }`
|
||||
- Type casting: `expression as type` (e.g., `bytevar as word`). `as` has very low precedence (lower than arithmetic)
|
||||
- **No automatic type widening**: `byte*byte=byte` (overflow possible!). Cast explicitly
|
||||
- **No bare `{ }` blocks** like C/Java
|
||||
- Indentation: 4 spaces for .p8 and .asm files (no tabs)
|
||||
- Character encoding: 6502 targets use PETSCII by default (call `txt.lowercase()` at start for lowercase). Virtual target uses ISO (`%encoding iso` + `txt.iso()`)
|
||||
- **Test programs for CBM targets must only use lowercase text in string literals** (the C64/C128/PET32/CX16 targets all use PETSCII). In PETSCII, uppercase letters A-Z are the *graphics* symbols, so any uppercase text you put in a string literal is rendered as unpredictable graphics characters instead of readable letters. Use only lowercase (a-z) for any printed test output, labels, or expected-match strings on these targets; reserve uppercase for intentional PETSCII graphics. The `virtual` target (ISO) is unaffected and may use either case.
|
||||
- Array size inferred from initializer: `str[] types = ["a", "b", "c"]`
|
||||
- Enums: `Enum::Value` syntax (double colon), declared inside a block. They are syntactic sugar for a list of `const` declarations, not a type. Use enums for related values, `const` for standalone
|
||||
- Avoid `globals.XXXX` — move constants closer to where they're used
|
||||
- Member access through pointers: use `.` for both direct and pointer access. The compiler infers the type. For complex assignment targets, `^^` may be needed: `ptr^^.field = value`
|
||||
- Qualified names: must use full path from top level (e.g., `cx16.r0`, not relative)
|
||||
@@ -0,0 +1,19 @@
|
||||
# An .aiignore file follows the same syntax as a .gitignore file.
|
||||
# .gitignore documentation: https://git-scm.com/docs/gitignore
|
||||
|
||||
# you can ignore files
|
||||
.DS_Store
|
||||
*.log
|
||||
*.tmp
|
||||
*.bin
|
||||
*.prg
|
||||
|
||||
# or folders
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
output/
|
||||
gradle/
|
||||
.gradle/
|
||||
.kotlin/
|
||||
.git/
|
||||
@@ -0,0 +1,45 @@
|
||||
# EditorConfig for Prog8 projects
|
||||
# Ensures consistent formatting across editors and team members
|
||||
# See https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
# All files
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Prog8 source files (.p8)
|
||||
[*.p8]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Assembly files (.asm) - 64tass syntax
|
||||
[*.asm]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Kotlin compiler source files
|
||||
[*.kt]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Documentation files
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.rst]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Gradle build files
|
||||
[*.gradle.kts]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Git ignore files
|
||||
[.gitignore]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -10,32 +10,46 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: build and install recent 64tass
|
||||
- name: install 64tass
|
||||
run: |
|
||||
sudo apt-get install -y make build-essential
|
||||
git clone --depth=1 https://github.com/irmen/64tass
|
||||
cd 64tass
|
||||
make -j4
|
||||
sudo make install
|
||||
sudo apt update
|
||||
sudo apt install 64tass
|
||||
64tass --version
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
- name: Build and test with Gradle
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Compile
|
||||
run: ./gradlew compileKotlin compileTestKotlin --console=plain
|
||||
|
||||
- name: Test
|
||||
run: ./gradlew test --continue --console=plain
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
./gradlew build shadowJar --no-daemon
|
||||
./gradlew shadowJar --console=plain
|
||||
sha256sum -b compiler/build/libs/*-all.jar > compiler/build/libs/hash.txt
|
||||
|
||||
|
||||
- name: Create compiler shadowJar artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: prog8-compiler-jar-zipped
|
||||
path: |
|
||||
compiler/build/libs/*-all.jar
|
||||
compiler/build/libs/hash.txt
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: test-results
|
||||
path: compiler/build/test-results/test/
|
||||
|
||||
+8
-1
@@ -3,14 +3,21 @@
|
||||
.idea/developer-tools.xml
|
||||
.idea/usage.statistics.xml
|
||||
.idea/shelf/
|
||||
.junie/
|
||||
.junie/memory/
|
||||
.serena
|
||||
build/
|
||||
dist/
|
||||
output/
|
||||
out/
|
||||
out-new/
|
||||
out-old/
|
||||
.*cache/
|
||||
*.directory
|
||||
*.prg
|
||||
*.bin
|
||||
*.p8ir
|
||||
*.bankedcalls
|
||||
*.labels.txt
|
||||
*.vm.txt
|
||||
*.vice-mon-list
|
||||
@@ -19,6 +26,7 @@ parser/**/*.interp
|
||||
parser/**/*.tokens
|
||||
parser/**/*.java
|
||||
compiler/src/prog8/buildversion/*
|
||||
languageServer/src/prog8/buildversion/*
|
||||
*.py[cod]
|
||||
*.egg
|
||||
*.egg-info
|
||||
@@ -32,7 +40,6 @@ parsetab.py
|
||||
.pytest_cache/
|
||||
.attach_pid*
|
||||
compiler/lib/
|
||||
|
||||
.gradle
|
||||
sd*.img
|
||||
*.d64
|
||||
|
||||
Generated
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JunieProjectTechnologies">
|
||||
<option name="explicitlyEnabledTechs">
|
||||
<set>
|
||||
<option value="junie.python" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+11
-1
@@ -2,6 +2,16 @@
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="BUILD_PROCESS_HEAP_SIZE" value="3000" />
|
||||
<bytecodeTargetLevel target="11" />
|
||||
<bytecodeTargetLevel target="11">
|
||||
<module name="prog8.codeGenExperimental" target="17" />
|
||||
<module name="prog8.codeGenExperimental.main" target="17" />
|
||||
<module name="prog8.codeGenExperimental.test" target="17" />
|
||||
<module name="prog8.codeGenM68k" target="17" />
|
||||
<module name="prog8.codeGenM68k.main" target="17" />
|
||||
<module name="prog8.codeGenM68k.test" target="17" />
|
||||
<module name="prog8.codeGenNew6502" target="17" />
|
||||
<module name="prog8.codeGenNew6502.main" target="17" />
|
||||
<module name="prog8.codeGenNew6502.test" target="17" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+29
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="LOCAL" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/codeCore" />
|
||||
<option value="$PROJECT_DIR$/codeGenCpu6502" />
|
||||
<option value="$PROJECT_DIR$/codeGenIntermediate" />
|
||||
<option value="$PROJECT_DIR$/codeOptimizers" />
|
||||
<option value="$PROJECT_DIR$/compiler" />
|
||||
<option value="$PROJECT_DIR$/compilerAst" />
|
||||
<option value="$PROJECT_DIR$/intermediate" />
|
||||
<option value="$PROJECT_DIR$/languageServer" />
|
||||
<option value="$PROJECT_DIR$/parser" />
|
||||
<option value="$PROJECT_DIR$/simpleAst" />
|
||||
<option value="$PROJECT_DIR$/virtualmachine" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="myGradleHome" value="/usr/share/java/gradle" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
+1559
-7
File diff suppressed because it is too large.
Load diff
Generated
+15
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="80ae155e-b222-4ca0-92cf-6041a298e78a" />
|
||||
<option name="name" value="80ae155e-b222-4ca0-92cf-6041a298e78a" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+5
-4
@@ -7,13 +7,14 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinCommonCompilerArguments">
|
||||
<option name="apiVersion" value="2.1" />
|
||||
<option name="languageVersion" value="2.1" />
|
||||
<option name="apiVersion" value="2.4" />
|
||||
<option name="languageVersion" value="2.4" />
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-Xwhen-guards" />
|
||||
<option name="additionalArguments" value="-jvm-default=no-compatibility" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.1.10" />
|
||||
<option name="externalSystemId" value="Gradle" />
|
||||
<option name="version" value="2.4.10" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-23
@@ -1,23 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.10" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.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/2.1.10/kotlin-stdlib-jdk7-2.1.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
-18
@@ -1,18 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr.antlr4" type="repository">
|
||||
<properties maven-id="org.antlr:antlr4:4.13.2">
|
||||
<exclude>
|
||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.2/antlr4-4.13.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.2/antlr4-runtime-4.13.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
Generated
-13
@@ -1,13 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="eclipse.lsp4j" type="repository">
|
||||
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.23.1/org.eclipse.lsp4j-0.23.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.23.1/org.eclipse.lsp4j.jsonrpc-0.23.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.27.0/error_prone_annotations-2.27.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.framework.datatest" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-framework-datatest:5.9.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest/5.9.1/kotest-framework-datatest-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest-jvm/5.9.1/kotest-framework-datatest-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.9.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.9.1/kotest-runner-junit5-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.9.1/kotest-framework-engine-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.172/classgraph-4.8.172.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.8.0/kotlinx-coroutines-debug-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.9.1/kotest-framework-discovery-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.9.1/kotest-extensions-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.9.1/kotest-framework-concurrency-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+6
-1
@@ -12,6 +12,7 @@
|
||||
<option name="pkg" value="" />
|
||||
<option name="language" value="Java" />
|
||||
<option name="generateListener" value="false" />
|
||||
<option name="generateVisitor" value="true" />
|
||||
</PerGrammarGenerationSettings>
|
||||
</list>
|
||||
</option>
|
||||
@@ -19,10 +20,14 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11" />
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-13
@@ -2,22 +2,9 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/beanshell/beanshell.iml" filepath="$PROJECT_DIR$/beanshell/beanshell.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/benchmark-program/benchmark-program.iml" filepath="$PROJECT_DIR$/benchmark-program/benchmark-program.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" filepath="$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/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$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
description: Startup workflow to load project context
|
||||
---
|
||||
|
||||
1. Read the file `AGENTS.md` in the project root as initial system prompt to build context and instructions about the project structure, module descriptions, and development commands.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Agent Instructions
|
||||
|
||||
- The file `AGENTS.md` MUST be loaded as the very first step of every session, if it isn't already loaded by the system.
|
||||
- Follow all project-specific guidelines and information provided in `AGENTS.md`.
|
||||
- Ensure all automated tasks and code generations adhere to the language rules and architectural decisions described in `AGENTS.md`.
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"context": {
|
||||
"fileName": "AGENTS.md"
|
||||
},
|
||||
"$version": 3,
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(prog8c *)",
|
||||
"Bash(gradle *)",
|
||||
"Bash(mkdir *)",
|
||||
"Bash(ls *)",
|
||||
"Bash(cat *)",
|
||||
"Bash(find *)",
|
||||
"Bash(sed *)",
|
||||
"Bash(grep *)",
|
||||
"Bash(xargs *)",
|
||||
"Bash(wc *)",
|
||||
"Bash(diff *)",
|
||||
"Bash(paste *)",
|
||||
"Bash(echo *)",
|
||||
"Bash(cp *)",
|
||||
"Bash(time)",
|
||||
"Bash(kotlinc *)",
|
||||
"Bash(python3 *)",
|
||||
"Bash(jar *)",
|
||||
"Bash(od *)",
|
||||
"Bash(xxd *)",
|
||||
"Bash(gcc *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
# CRITICAL RULES - MUST FOLLOW
|
||||
|
||||
## RESPONSES
|
||||
|
||||
- Keep responses concise and to the point - unless the user asks otherwise
|
||||
|
||||
## PLANNING MODE
|
||||
|
||||
- Always ask clarifying questions
|
||||
- Never assume design, tech stack or features
|
||||
- Use deep-dive sub-agents to assist with research
|
||||
- Use deep-dive sub-agents to review the different aspects of your plan before presenting to the user
|
||||
|
||||
## CHANGE / EDIT MODE
|
||||
|
||||
- Never implement features yourself when possible - use sub-agents!
|
||||
- Identify changes from the plan that can be implemented in parallel, and use sub-agents to implement the features efficiently
|
||||
- When using sub-agents to implement features, act as a coordinator only
|
||||
- Use the best model for the task - premium models for complex tasks (like coding) and mid-tier models for simpler tasks, like documentation
|
||||
- After completing features (large or small), always run commands like lint, type check and next build to check code quality
|
||||
|
||||
## Project Overview
|
||||
- This project is a compiler for the Prog8 programming language.
|
||||
- Prog8 is a programming language primarily targeting 8-bit retro systems with the 6502 CPU, such as the Commodore 64, Commodore 128, and Commander X16.
|
||||
- The compiler has a 6502 code generator backend, and an IR code generator.
|
||||
- The IR code is meant to be used in a new machine specific code generator backend (primarily 6502 but maybe 68000 as well later)
|
||||
- The compiler includes a simple 'virtual machine' that can execute the IR code directly via interpretation.
|
||||
- Prog8 source files have .p8 extension – these are *not* LUA or PICO-8 source files in this case!
|
||||
- Prog8 source files are a "module" that can contain one or more "blocks". They can also import other modules, from internal library files or from source files on the filesystem.
|
||||
- The prog8 compiler is written mostly in Kotlin, those files have the .kt extension.
|
||||
- The standard library is mostly written in Prog8 and assembly code, and can be found in the "compiler" module, in the 'res/prog8lib' directory.
|
||||
- **Library Reference**: For a quick overview of all available modules, routines, and their signatures, **consult the skeleton files (symboldumps)** in `docs/source/_static/symboldumps/`. They are named `skeletons-<target>.txt`.
|
||||
- Kotlin version 2.4 is used for the compiler implementation.
|
||||
- Java 17 is used as Java runtime version.
|
||||
- ANTLR4 version 4.13 is used for the parser implementation.
|
||||
- Dependent library versions can be found in 'build.gradle.kts' and in the IntelliJ IDEA configuration files in .idea/libraries
|
||||
- The compiler main entrypoint is in the "compiler" module, in src/prog8/CompilerMain.kt
|
||||
|
||||
## Compilation Flow (High-Level)
|
||||
```
|
||||
Source → parseMainModule() → processAst() → optimizeAst() → postprocessAst()
|
||||
→ Simple AST → Code Generator → Assembly → .prg
|
||||
```
|
||||
|
||||
**Key phases in `compiler/src/prog8/compiler/Compiler.kt`:**
|
||||
- `parseMainModule()` - Import modules, parse to Compiler AST
|
||||
- `processAst()` - Semantic analysis, constant folding, type casting, validation
|
||||
- `optimizeAst()` - Dead code elimination, inlining, statement optimization
|
||||
- `postprocessAst()` - Memory layout, final transformations
|
||||
- Code generation – Simple AST → IR or 6502 assembly
|
||||
|
||||
**Understanding this order is important:** For example, `ConstantIdentifierReplacer` runs during `processAst()`, so by the time `AstChecker` runs at the end of that phase, identifier references have already been replaced with their actual values.
|
||||
|
||||
### Target Differences
|
||||
- **CPU instruction set differences**: Only the CommanderX16 target (cx16) can use 65C02 instructions such as STZ. The other targets (C64, C128, PET32) can only use original 6502 instructions.
|
||||
- When writing or understanding assembly code, load the `asm6502-coder` skill for 64tass syntax and conventions.
|
||||
|
||||
|
||||
## DEBUGGING TIP: Use `-noopt` to isolate problems
|
||||
When investigating a possible code generation problem (both IR and 6502), **FIRST try compiling the program with the `-noopt` switch** to disable most of the compiler optimizations.
|
||||
- **Problem gone with `-noopt`**: Issue is in **optimization phases** (`optimizeAst()`, `UnusedCodeRemover`, `Inliner`, etc.)
|
||||
- **Problem persists with `-noopt`**: Issue is in **parsing, semantic analysis, symboltable, or the regular code generation path**.
|
||||
|
||||
This way you can determine if the problem is caused by a faulty optimization step, or just occurs in the regular code generation path.
|
||||
|
||||
## DEBUGGING TIP: Use `-compareir` to see what changed
|
||||
When investigating optimization-related issues or tracking regressions:
|
||||
```bash
|
||||
# Compile without optimizations (baseline)
|
||||
prog8c -target virtual -noopt -out dir program.p8
|
||||
|
||||
# Compile with optimizations and compare
|
||||
prog8c -target virtual -compareir dir/program_noopt.p8ir program.p8
|
||||
```
|
||||
**Output shows:**
|
||||
- Instruction/chunk/register count changes with percentages
|
||||
- First 10 actual instruction differences
|
||||
- Helps identify which optimization transformed the code
|
||||
|
||||
## DEBUGGING TIP: Use `-vmtrace` to trace execution
|
||||
When debugging VM execution or control flow issues:
|
||||
```bash
|
||||
prog8c -target virtual -vm program.p8ir -vmtrace
|
||||
prog8c -target virtual -emu -vmtrace program.p8
|
||||
```
|
||||
**Output format:** `[chunkName:instructionIndex] instruction`
|
||||
- Shows each executed IR instruction with location
|
||||
- Useful for understanding control flow and finding where execution diverges
|
||||
- **Only works on virtual target**
|
||||
|
||||
## Typical debugging workflow:
|
||||
1. **Quick check:** `-check` for syntax errors
|
||||
2. **Isolate:** `-noopt` to determine if problem is optimizer-related
|
||||
3. **Compare:** `-compareir` to see what instructions changed
|
||||
4. **Trace:** `-vmtrace` to watch actual execution flow
|
||||
5. **Deep dive:** `-printast1` / `-printast2` for compiler internals
|
||||
|
||||
## IMPORTANT: SymbolTable cachedFlat cache
|
||||
The `SymbolTable` class has a **cached `flat` property** for fast symbol lookups.
|
||||
|
||||
**CRITICAL:** If you modify the AST **after** SymbolTable construction, you **MUST** call `symbolTable.resetCachedFlat()` or lookups will fail!
|
||||
|
||||
## IMPORTANT: Virtual Target (IR Codegen) Issues
|
||||
For problems that **ONLY occur with the 'virtual' target**, **ONLY modify these modules**:
|
||||
- `codeGenIntermediate` - IR code generator
|
||||
- `intermediate` - IR representation and file I/O
|
||||
- `virtualmachine` - VM that executes IR code
|
||||
|
||||
**DO NOT modify** `compilerAst`, `simpleAst`, `codeGenCpu6502`, etc. The IR codegen has its own separate handling for symbol tables, AST transformations, and unused code removal.
|
||||
|
||||
## IMPORTANT: Compiler Crashes Must Be Fixed in the Compiler
|
||||
A Kotlin/Java crash in the compiler itself (as opposed to a compilation error in the user's program) indicates a bug in the compiler code. **Such crashes should NEVER be worked around by modifying the Prog8 source file that triggered them.**
|
||||
|
||||
Instead, always fix the bug in the compiler's Kotlin code. The user's program is correct - the compiler needs to handle it properly.
|
||||
|
||||
Steps when encountering a compiler crash:
|
||||
1. Create a minimal reproduction case of the Prog8 source that triggers the crash
|
||||
2. Debug the compiler to find the root cause in the Kotlin code
|
||||
3. Fix the compiler bug, not the user program
|
||||
|
||||
## CRITICAL: NO FORMATTING
|
||||
- DO NOT change indentation and formatting of lines that are not being modified. NEVER run formatters (black, ruff, prettier, etc.) after edits,
|
||||
- .editorconfig handles basic formatting (indentation, line endings, whitespace)
|
||||
- Make ONLY the requested changes, touch nothing else
|
||||
- **NO EMOJI in user documentation**. Do not use emoji or decorative unicode symbols in documentation files. Functional unicode symbols are acceptable when they serve a clear purpose (e.g., → for arrows, ± for plus-minus, × for multiplication, ° for degrees). Avoid decorative emoji like ❌ ✅ ⚠️ 🎉 etc.
|
||||
|
||||
### Code Style Guidelines
|
||||
**Minimal comments when making changes**: When modifying existing code, add only essential comments that explain *why* a change was made or document non-obvious behavior. **Do not add verbose comments** that restate what the code does; let the code speak for itself. Existing extensive comments should be preserved, but new changes should have minimal commentary.
|
||||
|
||||
**Kotlin string `$` handling**: The `$` character is special in Kotlin string literals (string interpolation). If you need a literal `$` character, use Kotlin's `$$"..."` interpolation prefix instead of backslash escaping (`\$`) or `${'$'}`. The `$$"..."` syntax is a fairly recent Kotlin feature that avoids nasty escaping. For example:
|
||||
```kotlin
|
||||
val bill = $$"The value is $3.99"
|
||||
```
|
||||
|
||||
|
||||
## Prog8 language information
|
||||
|
||||
When the task involves writing or understanding `.p8` (Prog8 source) or `.p8ir` (Prog8 intermediate representation) files, **load the `prog8-coder` skill** — it contains the full language reference, syntax rules, and standard library guidance.
|
||||
|
||||
## Project Module Descriptions
|
||||
- `compiler` - Main compiler entrypoint (src/prog8/CompilerMain.kt)
|
||||
- `compilerAst` - Complex AST where most optimizations run
|
||||
- `simpleAst` - Simplified AST used by code generator backends
|
||||
- `parser` - ANTLR4 parser implementation
|
||||
- `codeCore` - Core code generation utilities
|
||||
- `codeGenCpu6502` - 6502 assembly code generator backend
|
||||
- `codeGenIntermediate` - Intermediate representation (IR) code generator
|
||||
- `codeGenVirtual` - Virtual machine code generator backend
|
||||
- `codeOptimizers` - Optimization passes
|
||||
- `intermediate` - IR components
|
||||
- `virtualmachine` - VM that executes IR code
|
||||
- `languageServer` - Language Server Protocol implementation
|
||||
- `docs` - Documentation files
|
||||
- `examples` - Example Prog8 programs
|
||||
|
||||
## Key Information
|
||||
- never read the files and directories that are ignored via the .aiignore and .gitignore files (EXCEPT when it is a folder for agent config such as .junie, .opencode, .qwen or .agent)
|
||||
- never perform any git source control write/update/add/commit/branch operations. Read and status operations are allowed.
|
||||
- **git log/history queries can be useful** for understanding when/why a feature was added or tracking down when a bug was introduced, but for locating code use grep_search or glob instead.
|
||||
- Architecture decisions: separation of frontend/parser, IR intermediate representation, multiple backends
|
||||
|
||||
# Dev environment tips
|
||||
|
||||
## Development Workflows
|
||||
|
||||
**1. Testing your own Prog8 programs** (no compiler changes):
|
||||
- No rebuild needed - just run `prog8c` directly
|
||||
- Edit your `.p8` file → compile/run → check stdout output
|
||||
- Example: `prog8c -target virtual -emu myprogram.p8`
|
||||
|
||||
**2. Testing compiler or standard library changes**:
|
||||
- Rebuild required after every change to `.kt`, `.p8`, or `.asm` files in the compiler project
|
||||
- Workflow: edit source → `gradle installdist installshadowdist` → test with `prog8c`
|
||||
- Example: fix bug in `CodeGen6502.kt` → rebuild → `prog8c -target cx16 test.p8`
|
||||
|
||||
## Building and Installing the Compiler
|
||||
- use the system installed gradle command instead of the gradle wrapper.
|
||||
|
||||
### Quick compile check (NO tests)
|
||||
- **After changing compiler Kotlin source (.kt files)**: Use `gradle :compiler:compileKotlin --console=plain` to quickly check for syntax/compile errors
|
||||
- This compiles the compiler but **skips running tests**, this is much faster than `gradle build`
|
||||
- **Note:** This does NOT install the compiler - use `gradle installdist installshadowdist --console=plain` after to actually use your changes
|
||||
|
||||
### When you MUST rebuild AND reinstall the compiler
|
||||
|
||||
**After ANY change to:**
|
||||
1. **Kotlin compiler source code** (`.kt` files in any module, or the `.g4` grammar file)
|
||||
2. **Standard library Prog8 files** (`compiler/res/prog8lib/**/*.p8`)
|
||||
3. **Standard library assembly files** (`compiler/res/prog8lib/**/*.asm`)
|
||||
|
||||
**Run:** `gradle installdist installshadowdist --console=plain`
|
||||
|
||||
**Why:** The standard library files (.p8 and .asm) are embedded into the compiler JAR during the build. Changes to these files are NOT picked up by simply running `prog8c` - you MUST rebuild and reinstall for your changes to take effect.
|
||||
|
||||
**Without this step, your changes will NOT be reflected when running `prog8c`!**
|
||||
- This compiles AND installs, but still skips running tests (faster than `gradle build`)
|
||||
|
||||
### When to run full build with tests
|
||||
- **Before final verification**: Always run `gradle build` to ensure all tests pass
|
||||
- **After major refactoring**: Run `gradle build` to catch regressions
|
||||
- **When debugging test failures**: Use `gradle test --tests "*TestName*"` for specific tests
|
||||
- **When working on a standalone codegen module** (one that reads `.p8ir` files): Run `gradle :<module>:test --console=plain` instead of full `gradle build`. Compiler unit tests are irrelevant for IR-level changes. Do run full `gradle build` before final verification.
|
||||
|
||||
### Build command summary
|
||||
|
||||
| Command | When to use | Time |
|
||||
|---------|-------------|------|
|
||||
| `gradle :compiler:compileKotlin --console=plain` | Quick syntax check (no tests) | ~10-20s |
|
||||
| `gradle installdist installshadowdist --console=plain` | After compiler changes (to use them) | ~10-20s |
|
||||
| `gradle build --console=plain` | Before commits, after major changes | ~45-60s |
|
||||
| `gradle test --tests "*Name*" --console=plain` | Run specific tests | ~5-30s |
|
||||
|
||||
**Note:** Use system `gradle` command, not wrapper. Run `gradle clean` if you suspect stale artifacts.
|
||||
|
||||
## Using the Compiler (prog8c)
|
||||
- the prog8c compiler executable can be found in the compiler/build/install/prog8c/bin folder (this is already added to the shell's path)
|
||||
- **the `-check` switch performs a quick syntax/semantic check only; it will NOT produce any output files (no .prg, .asm, etc.)**. Use it only for fast error checking during development.
|
||||
- **the `-noopt` switch DISABLES all optimizations** - useful for debugging to determine if a problem is caused by the optimizer. **Optimizations are ENABLED by default** (no flag needed).
|
||||
- **prog8c uses single-dash command line options** (e.g., `-target`, `-noopt`, `-check`), NOT double-dash (`--target` is invalid).
|
||||
- **the `-printast1` switch prints out the internal Compiler AST** after parsing and semantic analysis.
|
||||
- **the `-printast2` switch prints out the optimized Simple AST** just before it goes to the code generator. This is useful for debugging optimizer issues.
|
||||
- **the `-out outdir` switch sets an alternative output directory** for compiled files (.prg, .asm, .list, etc.). **By default, output files are written to the same directory as the source file**.
|
||||
- **Other useful flags**: `-quiet` (suppress messages), `-warnimplicitcasts` (warn on implicit type widening), `-daemon` (keep a background compiler process alive — must be passed on every invocation)
|
||||
|
||||
### Compilation Output Files
|
||||
- `*.prg` - The final compiled program file for the target system (e.g., Commander X16)
|
||||
- `*.asm` - Generated assembly code from the Prog8 source
|
||||
- `*.list` - Generated full assembly listing file from the Prog8 source
|
||||
- `*.p8ir` - Intermediate representation file, can be executed in the Virtual Machine
|
||||
- `*.vice-mon-list` - Vice emulator monitor list file for debugging
|
||||
|
||||
### Execution Examples
|
||||
- `prog8c -target targetname input.p8` - Compile a Prog8 source file "input.p8" for the given target (cx16, c64, pet32, c128, virtual)
|
||||
- `prog8c -target targetname -emu input.p8` - Compile and execute a prog8 file in the emulator for the given target (cx16, c64, pet32, c128, virtual)
|
||||
- `prog8c -vm input.p8ir` - Execute an existing prog8 program, compiled in IR form, in the Virtual Machine
|
||||
|
||||
### Library Tools
|
||||
- **Library search**: `prog8c -libsearch <regex>` — search for a regex pattern in the embedded stdlib files. Extremely useful to quickly find library routines, variables, or signatures (e.g., `prog8c -libsearch "txt\."` lists all textio routines)
|
||||
- **Library dump**: `prog8c -libdump <dir>` — extract all embedded library source files into a directory for direct inspection
|
||||
|
||||
## Testing and Verification
|
||||
|
||||
- Never assume your changes simply work, always test!
|
||||
- If the project does not have any testing tools, scripts, MCP tools, skills, etc. available for testing, ask the user whether testing should be skipped.
|
||||
|
||||
### Automated Tests (gradle)
|
||||
Always use the `--console=plain` flag with these commands to avoid messing up the agent's text interface.
|
||||
- `gradle build --console=plain` - Full build including all tests (about 50s)
|
||||
- `gradle :compiler:test --console=plain` - Run only compiler tests (faster)
|
||||
- `gradle :compiler:test --tests "prog8tests.compiler.TestOptimization" --console=plain` - Run specific test class
|
||||
|
||||
Note: By default, Gradle only shows failed tests. Passed and skipped tests are silent.
|
||||
|
||||
**⚠️ CRITICAL: Test Filtering Patterns - Read This First!**
|
||||
|
||||
Gradle's `--tests` filter has **strict rules** that are easy to get wrong:
|
||||
|
||||
| Pattern | Example | Works? |
|
||||
|---------|---------|---------------|
|
||||
| **Full class name** | `--tests "prog8tests.compiler.TestOptimization"` | **USE THIS** |
|
||||
| Wildcard at END | `--tests "prog8tests.compiler.Test*"` | Yes |
|
||||
| Wildcard on package | `--tests "prog8tests.compiler.*"` | Yes |
|
||||
| **Wildcard at START** | `--tests "*TestOptimization"` | FAILS |
|
||||
| Test description | `--tests "*Optimization*inline*"` | FAILS |
|
||||
|
||||
**Why the restrictions?** Gradle's `--tests` filter matches **fully qualified class names only**. Wildcards work as **suffixes** (e.g., `Test*`) but NOT as prefixes (e.g., `*Test`). KoTest test names like `"inline multi-value void"` are _descriptions_, not method names, so you cannot filter by them.
|
||||
|
||||
**To find failing tests after a run:**
|
||||
```bash
|
||||
# Check XML results (reliable)
|
||||
grep "<failure" compiler/build/test-results/test/*.xml
|
||||
|
||||
# Or check HTML report (most reliable)
|
||||
cat compiler/build/reports/tests/test/index.html
|
||||
|
||||
# For test compilation errors
|
||||
gradle :compiler:compileTestKotlin --info 2>&1 | grep "^e:"
|
||||
```
|
||||
|
||||
**Additional notes:**
|
||||
- Unit tests use KoTest (FunSpec style)
|
||||
- Tests are in the "compiler" module's `test` directory (and some other modules)
|
||||
- Test config is in root `build.gradle.kts`; tests run in parallel
|
||||
- **When writing test programs**, add at the top: `%zeropage basicsafe` and `%option no_sysinit`
|
||||
- When a test fails, the output shows "There were failing tests. See the report at:" - **read that HTML report**
|
||||
|
||||
### Compiler Unit Test Snippets
|
||||
Prog8 code snippets embedded in the compiler's own Kotlin unit tests (e.g., in `TestAstChecks.kt`, `TestOptimization.kt`) follow different rules from standalone programs:
|
||||
- **Keep self-contained** — avoid `%import` directives (test setup may not have library search path configured)
|
||||
- **Use `cx16` target** (not `virtual`, unless specifically testing the VM)
|
||||
- **Set `writeAssembly=false`** if the test only checks generated AST (much faster)
|
||||
- **Set `optimize=false`** by default for these snippets
|
||||
- **Do NOT use** the `%encoding iso` / `txt.iso()` / `sys.poweroff_system()` pattern — that's only for real CX16 emulator runs
|
||||
|
||||
### Testing Assembly Output
|
||||
When testing generated assembly in Kotlin tests:
|
||||
- **NEVER dump raw assembly in string assertions** (e.g., `asm shouldContain "jsr label"`). On failure, this dumps the ENTIRE assembly listing into test logs.
|
||||
- **Instead, parse into lines and check specific properties:**
|
||||
```kotlin
|
||||
val lines = asm.lines().map { it.trim() }
|
||||
lines.any { it == "lda #42" } shouldBe true
|
||||
lines.count { it.startsWith("jsr") } shouldBe 0
|
||||
```
|
||||
- Prefer `lines.any { ... }` for existence checks and `lines.count { ... } shouldBe 0` for absence checks.
|
||||
|
||||
### Manual Verification & Emulators
|
||||
|
||||
**Testing tip**: When writing and testing Prog8 programs, **use the `virtual` target** (e.g., `prog8c -target virtual -emu input.p8` or `prog8c -vm input.p8ir`). This is the preferred way to test because the virtual target can easily write output to stdout, making it simple to verify program behavior and check results.
|
||||
|
||||
**CX16 output verification**: Use `x16emu -echo iso -run -prg input.prg` to echo screen output to stdout **and auto-start the program**. The `-run` flag is **critical**: without it, the program loads but doesn't execute, so you'll see no output. Pipe through `strings` or `grep` to filter: `x16emu -echo iso -run -prg input.prg 2>&1 | grep -E "(PASS|FAIL)"`.
|
||||
**IMPORTANT**: Always add `%encoding iso` at the top of your source file and call `txt.iso()` in `start()`. This prevents PETSCII→ISO charset translation errors that garble uppercase/special characters and make output unreadable:
|
||||
```prog8
|
||||
%encoding iso
|
||||
%import textio
|
||||
main { sub start() { txt.iso(); txt.print("PASS\n") } }
|
||||
```
|
||||
**IMPORTANT: Always use `sys.poweroff_system()` to exit the CX16 emulator cleanly!** Add `sys.poweroff_system()` at the end of your main program block - this exits x16emu automatically in most cases.
|
||||
**Note:** The `sys` module is always available, there is no need to import it ever.
|
||||
|
||||
**Commodore 64 (x64sc)**: `x64sc input.prg` - run an existing compiled program in the Commodore-64 emulator. Ignore any errors and warnings, because the emulator doesn't produce any output on STDOUT.
|
||||
|
||||
### actual 6502 CPU simulation tests
|
||||
For high-fidelity functional verification of generated 6502 code without a full emulator, you can use the `ksim65` simulator in your unit tests. This allows executing the compiled machine code and asserting on memory or register states.
|
||||
- Use the `simulate()` extension function on a `CompilationResult`.
|
||||
- See `prog8tests.codegeneration.TestExecution6502` for examples.
|
||||
- Both 6502 and 65C02 CPUs can be simulated.
|
||||
- The simulator supports capturing serial output and handling hardware reset/poweroff signals.
|
||||
- There are various assertion helper methods to test on the state of CPU registers, memory contents, etc.
|
||||
- Using the simulator is much faster and way more controllable than using a full emulator to run the code.
|
||||
- The simulator DOES NOT represent an actual real world machine and has VERY LIMITED hardware devices so it can generally not be used to run full example prorams on.
|
||||
|
||||
## Git Operations for File Moves/Deletes
|
||||
|
||||
**When renaming or moving git-tracked files, ALWAYS use `git mv`:**
|
||||
```bash
|
||||
# CORRECT - preserves git history
|
||||
git mv old/path/File.kt new/path/File.kt
|
||||
|
||||
# WRONG - git sees this as delete + add (loses history)
|
||||
mv old/path/File.kt new/path/File.kt
|
||||
```
|
||||
|
||||
**When deleting git-tracked files, ALWAYS use `git rm`:**
|
||||
```bash
|
||||
# CORRECT - properly stages the deletion
|
||||
git rm path/to/File.kt
|
||||
|
||||
# WRONG - git sees this as unstaged deletion
|
||||
rm path/to/File.kt
|
||||
```
|
||||
|
||||
**Why this matters:** `git mv` and `git rm` properly stage the changes and preserve file history. Plain `mv`/`rm` requires git to detect renames heuristically, which may not always work correctly.
|
||||
|
||||
## TODO Items
|
||||
The file `docs/source/todo.rst` contains a comprehensive list of things that still have to be fixed, implemented, or optimized. **Use this to understand what features are NOT yet available** in the compiler or Prog8 language: if a user asks for something that's on the TODO list, you'll know it's not implemented yet and can explain the limitation.
|
||||
@@ -1,12 +1,20 @@
|
||||
# super simple Makefile to lauch the main gradle targets to build and/or test the prog8 compiler
|
||||
# super simple Makefile to launch the main gradle targets to build and/or test the prog8 compiler
|
||||
|
||||
.PHONY: all test
|
||||
|
||||
# Use system gradle if available, otherwise fall back to gradlew wrapper
|
||||
# Windows uses 'where' and gradlew.bat, Unix uses 'command -v' and ./gradlew
|
||||
ifeq ($(OS),Windows_NT)
|
||||
GRADLE = $(shell where gradle >nul 2>&1 && echo gradle || echo gradlew.bat)
|
||||
else
|
||||
GRADLE = $(shell command -v gradle >/dev/null 2>&1 && echo gradle || echo ./gradlew)
|
||||
endif
|
||||
|
||||
all:
|
||||
gradle installdist installshadowdist
|
||||
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"
|
||||
$(GRADLE) installdist installshadowdist
|
||||
@echo "compiler launch script can be found here: compiler/build/install/prog8c-shadow/bin/prog8c"
|
||||
|
||||
test:
|
||||
gradle build
|
||||
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"
|
||||
$(GRADLE) build
|
||||
@echo "compiler launch script can be found here: compiler/build/install/prog8c-shadow/bin/prog8c"
|
||||
|
||||
@@ -15,10 +15,12 @@ which aims to provide many conveniences over raw assembly code (even when using
|
||||
|
||||
**Want to buy me a coffee or a pizza perhaps?**
|
||||
|
||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||
This project was created over the last couple of years by dedicating a huge amoung of my free time to it, to make it the best I possibly can.
|
||||
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
|
||||
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
|
||||
|
||||
[](https://jb.gg/OpenSource)
|
||||
I am using JetBrains products to develop the Prog8 compiler.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@@ -34,8 +36,11 @@ How to get it/build it
|
||||
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
|
||||
Note that if you are not using *gradle* to build it, you might have to perform some manual
|
||||
tasks once to make it compile fully. These are explained in the linked instructions.
|
||||
- Alternatively, you can also install the compiler as a package on some linux distros:
|
||||
- Alternatively, you can also install the compiler as a package on some linux distros (which will take care of dependencies automatically):
|
||||
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
|
||||
- Finally there's a Homebrew recipe for Mac OS (but also for Linux, and WSL2 on Windows, this also takes care of dependencies automatically):
|
||||
``brew install prog8``
|
||||
|
||||
|
||||
Community
|
||||
---------
|
||||
@@ -58,17 +63,23 @@ What does Prog8 provide?
|
||||
|
||||
- all advantages of a higher level language over having to write assembly code manually
|
||||
- programs run very fast because it's compiled to native machine code
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- compiled code is very small; much smaller than equivalent C code compiled with CC65, and usually runs faster as well
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- Can make vars and routines private, as a simple way to restrict access and hide implementation details
|
||||
- various data types other than just bytes (16-bit words, long integers, floats, strings)
|
||||
- Structs and typed pointers
|
||||
- 2D arrays (`matrix[row][col]` syntax)
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
- tight control over Zeropage usage
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- programs can be configured to execute in ROM
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- high-level program optimizations
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- conditional branches that map 1:1 to cpu status flags
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``on .. goto`` statement for fast jump tables
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
|
||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
|
||||
@@ -78,7 +89,7 @@ What does Prog8 provide?
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
||||
- encode strings and characters into petscii or screencodes or even other encodings
|
||||
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
|
||||
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
|
||||
- 50 Kb of available program RAM size on the C64 by default (41 Kb on the C128) because Basic ROM is banked out by default
|
||||
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
@@ -93,8 +104,7 @@ What does Prog8 provide?
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "pet32": Commodore PET (limited support)
|
||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
||||
- "neo": Neo6502 (experimental)
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64 OS, Foenix F256, ...
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||
|
||||
|
||||
@@ -103,8 +113,8 @@ Additional required tools
|
||||
-------------------------
|
||||
|
||||
[64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path.
|
||||
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
|
||||
For other platforms it is very easy to compile it yourself (make ; make install).
|
||||
If you're on Windows, download a recent .exe from the official repository's file section <https://sourceforge.net/projects/tass64/files/binaries/>.
|
||||
For other systems (Mac, Linux) it is very easy to get it via your package manager or to compile it yourself.
|
||||
|
||||
A **Java runtime (jre or jdk), version 11 or newer** is required to run a prepackaged version of the compiler.
|
||||
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
|
||||
@@ -169,7 +179,7 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
}
|
||||
}
|
||||
|
||||
when compiled an ran on a C-64 you'll get:
|
||||
when compiled and ran on a C-64 you'll get:
|
||||
|
||||

|
||||
|
||||
@@ -190,3 +200,19 @@ For instance here's a well known space ship animated in 3D with hidden line remo
|
||||
in the CommanderX16 emulator:
|
||||
|
||||

|
||||
|
||||
|
||||
Performance comparison with various C compilers
|
||||
-----------------------------------------------
|
||||
|
||||
Here is a performance comparison with various C compilers for the 6502/C64 so you can get
|
||||
an idea of how Prog8 stacks up.
|
||||
|
||||
[comparison](benchmark-c/benchmark.md)
|
||||
|
||||
|
||||
Agent context instructions
|
||||
--------------------------
|
||||
|
||||
[Agent context instructions](AGENTS.md)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/bsh-3.0.0-SNAPSHOT.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,65 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
val serverMainClassName = "prog8lsp.MainKt"
|
||||
val applicationName = "prog8-beanshell"
|
||||
|
||||
application {
|
||||
mainClass.set(serverMainClassName)
|
||||
description = "Code completions, diagnostics and more for Prog8"
|
||||
// applicationDefaultJvmArgs = listOf("-DkotlinLanguageServer.version=$version")
|
||||
applicationDistribution.into("bin") {
|
||||
filePermissions {
|
||||
user {
|
||||
read=true
|
||||
execute=true
|
||||
write=true
|
||||
}
|
||||
other.execute = true
|
||||
group.execute = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(files("lib/bsh-3.0.0-SNAPSHOT.jar"))
|
||||
}
|
||||
|
||||
configurations.forEach { config ->
|
||||
config.resolutionStrategy {
|
||||
preferProjectModules()
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
java.srcDir("src")
|
||||
resources.srcDir("resources")
|
||||
}
|
||||
|
||||
tasks.startScripts {
|
||||
applicationName = "prog8-beanshell"
|
||||
}
|
||||
|
||||
tasks.register<Exec>("fixFilePermissions") {
|
||||
// When running on macOS or Linux the start script
|
||||
// needs executable permissions to run.
|
||||
|
||||
onlyIf { !System.getProperty("os.name").lowercase().contains("windows") }
|
||||
commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell")
|
||||
}
|
||||
|
||||
tasks.installDist {
|
||||
finalizedBy("fixFilePermissions")
|
||||
}
|
||||
|
||||
tasks.build {
|
||||
finalizedBy("installDist")
|
||||
}
|
||||
Binary file not shown.
@@ -1,48 +0,0 @@
|
||||
package prog8beanshell
|
||||
|
||||
import java.io.FilterReader
|
||||
import java.io.Reader
|
||||
|
||||
|
||||
class CommandLineReader(val input: Reader): FilterReader(input) {
|
||||
private val normal = 0
|
||||
private val lastCharNL = 1
|
||||
private val sentSemi = 2
|
||||
private var state = lastCharNL
|
||||
|
||||
override fun read(): Int {
|
||||
if (state == sentSemi) {
|
||||
this.state = lastCharNL
|
||||
return 10
|
||||
} else {
|
||||
var b = input.read()
|
||||
while(b==13) b = input.read()
|
||||
|
||||
if (b == 10) {
|
||||
if (this.state == lastCharNL) {
|
||||
b = 59
|
||||
this.state = sentSemi
|
||||
} else {
|
||||
this.state = lastCharNL
|
||||
}
|
||||
} else {
|
||||
this.state = normal
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun read(buff: CharArray, off: Int, len: Int): Int {
|
||||
val b = read()
|
||||
if (b == -1) {
|
||||
return -1
|
||||
} else {
|
||||
buff[off] = b.toChar()
|
||||
return 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package prog8beanshell
|
||||
|
||||
import bsh.FileReader
|
||||
import bsh.Interpreter
|
||||
|
||||
|
||||
class BeanshellInterpreter {
|
||||
|
||||
fun run(symbols: Map<String, Any>) {
|
||||
val interpreter = Interpreter(CommandLineReader(FileReader(System.`in`)), System.out, System.err, true)
|
||||
interpreter.setExitOnEOF(false)
|
||||
symbols.forEach { (name, value) -> interpreter.set(name, value) }
|
||||
interpreter.run()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val i = BeanshellInterpreter()
|
||||
i.run(mapOf(
|
||||
"env" to System.getenv(),
|
||||
"args" to args
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# C-Bench-64 comparison
|
||||
|
||||
Several benchmarks of https://thred.github.io/c-bench-64/
|
||||
have been ported to equivalent Prog8 code and the benchmark results have been penciled in in the graphs below.
|
||||
Because they use the CIA timer for measuring the duration of the runs, the code only works on a C64.
|
||||
|
||||
Maybe one day I'll try to integrate the prog8 data properly but their benchmark site is comparing C compilers, which Prog8 clearly is not.
|
||||
|
||||
However conclusions so far (note: these are micro benchmarks so interpret the results as you will!)
|
||||
|
||||
* Prog8 program size is consistently one of the smallest.
|
||||
* Prog8 execution speed places it more or less in the middle of the stack.
|
||||
|
||||
Measured with Prog8 V12.0 .
|
||||
Benchmarks ran on a PAL C64 emulated through VICE.
|
||||
|
||||
textual results:
|
||||
|
||||
| benchmark | size | time |
|
||||
|-----------|------|----------|
|
||||
| crc8 | 1568 | 2.0 sec |
|
||||
| crc16 | 1608 | 2.5 sec |
|
||||
| crc32 | 1812 | 4.2 sec |
|
||||
| pow | 2152 | 11.7 sec |
|
||||
| sieve | 1772 | 22.1 sec |
|
||||
| sieve_bit | 1918 | 40.1 sec |
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
@@ -0,0 +1,98 @@
|
||||
%import textio
|
||||
|
||||
cia {
|
||||
ubyte freq
|
||||
const ubyte CNT = 200
|
||||
|
||||
sub calibrate() {
|
||||
txt.print("calibrating frequency: ")
|
||||
tod_init(0)
|
||||
txt.print_ub(tod_freq())
|
||||
txt.print(" hz\n")
|
||||
tod_reset()
|
||||
}
|
||||
|
||||
sub print_time() {
|
||||
uword t = tod_get10()
|
||||
txt.print("(cia) time: ")
|
||||
txt.print_uw(t / 10)
|
||||
txt.chrout('.')
|
||||
txt.print_uw(t % 10)
|
||||
txt.print(" sec.\n")
|
||||
}
|
||||
|
||||
sub tod_reset() {
|
||||
; set the tod to 0
|
||||
c64.CIA2TODHR = 0
|
||||
c64.CIA2TODMIN = 0
|
||||
c64.CIA2TODSEC = 0
|
||||
c64.CIA2TOD10 = 0
|
||||
}
|
||||
|
||||
sub tod_get10() -> uword {
|
||||
ubyte h, m, s, t
|
||||
uword time
|
||||
h = c64.CIA2TODHR
|
||||
m = c64.CIA2TODMIN
|
||||
s = c64.CIA2TODSEC
|
||||
t = c64.CIA2TOD10
|
||||
time = t
|
||||
time += (s & $0f) * (10 as uword)
|
||||
time += (s >> 4) * (100 as uword)
|
||||
time += (m & $0f) * 600
|
||||
time += (m >> 4) * 6000
|
||||
return time
|
||||
}
|
||||
|
||||
sub tod_init(ubyte f) {
|
||||
if (f == 0)
|
||||
freq = tod_detect_freq()
|
||||
else
|
||||
freq = f
|
||||
|
||||
if (freq == 50)
|
||||
c64.CIA2CRA |= $80
|
||||
else
|
||||
c64.CIA2CRA &= $7f
|
||||
}
|
||||
|
||||
sub tod_freq() -> ubyte {
|
||||
return freq
|
||||
}
|
||||
|
||||
sub tod_detect_freq() -> ubyte {
|
||||
uword cbl
|
||||
|
||||
c64.CIA2CRB = $40 ; stop timer
|
||||
c64.CIA2CRA = $80 ; stop timer
|
||||
|
||||
; set ta to overflow every 10000 count (~= 10ms)
|
||||
c64.CIA2TAL = $10
|
||||
c64.CIA2TAH = $27
|
||||
c64.CIA2TBL = CNT
|
||||
c64.CIA2TBH = 0
|
||||
|
||||
tod_reset()
|
||||
|
||||
c64.CIA2CRB = $41 ; input from tim1 overflow, continuous, start timer
|
||||
c64.CIA2CRA = $81 ; start timer, continuous tod 50HZ
|
||||
|
||||
|
||||
while c64.CIA2TODSEC == 0 {
|
||||
; wait for tod to count 1s
|
||||
}
|
||||
|
||||
; cal=CIA2.ta_lo;
|
||||
; cah=CIA2.ta_hi;
|
||||
cbl = c64.CIA2TBL
|
||||
; cbh=CIA2.tb_hi;
|
||||
|
||||
; printf("count2 = %d %d %d %d\n",cah, cal, cbh, cbl);
|
||||
; printf("elapsed ~= %d0ms\n",CNT-cbl);
|
||||
|
||||
if CNT - cbl > 90
|
||||
return 50
|
||||
else
|
||||
return 60
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
%import textio
|
||||
%import ciatimer
|
||||
|
||||
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
test.benchmark()
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
const uword EXPECTED = $ffd0
|
||||
uword crc_result
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("crc16.p8\n")
|
||||
txt.print("Calculates the CRC16 of the C64 Kernal\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
crc_result = CRC16($e000, $2000)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("CRC=")
|
||||
txt.print_uwhex(crc_result, true)
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uwhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
sub CRC16(^^ubyte data, uword length) -> uword {
|
||||
; CRC-16/XMODEM
|
||||
uword crc
|
||||
|
||||
repeat length
|
||||
{
|
||||
crc ^= mkword(@(data),0) ; currently there's no easy way to affect only the MSB of the variable
|
||||
|
||||
repeat 8
|
||||
{
|
||||
crc <<=1
|
||||
if_cs
|
||||
crc ^= $1021
|
||||
}
|
||||
data++
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
%import textio
|
||||
%import ciatimer
|
||||
|
||||
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
test.benchmark()
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
const long EXPECTED = $e1fa84c6
|
||||
long crc_result
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("crc32.p8\n")
|
||||
txt.print("Calculates the CRC32 of the C64 Kernal\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
crc_result = CRC32($e000, $2000)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("CRC=")
|
||||
txt.print_ulhex(crc_result, true)
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ulhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
sub CRC32(^^ubyte data, uword length) -> long {
|
||||
; CRC-32/CKSUM
|
||||
long crc
|
||||
|
||||
repeat length
|
||||
{
|
||||
crc ^= (@(data) as long)<<24 ; currently there's no easy way to affect only the MSB of the variable
|
||||
|
||||
repeat 8
|
||||
{
|
||||
crc <<=1
|
||||
if_cs
|
||||
crc ^= $04c11db7
|
||||
}
|
||||
data++
|
||||
}
|
||||
crc ^= $ffffffff
|
||||
return crc
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
%import textio
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
test.benchmark()
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("crc8.p8\n")
|
||||
txt.print("Calculates the CRC8 of the C64 Kernal\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
crc_result = CRC8($e000, $2000)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("CRC=")
|
||||
txt.print_ubhex(crc_result, true)
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ubhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
const ubyte EXPECTED = $a2
|
||||
ubyte crc_result
|
||||
|
||||
sub CRC8(^^ubyte data, uword length) -> ubyte
|
||||
{
|
||||
; CRC-8/GSM-A
|
||||
ubyte crc
|
||||
|
||||
repeat length
|
||||
{
|
||||
crc ^= @(data)
|
||||
|
||||
repeat 8
|
||||
{
|
||||
if (crc & $80) != 0
|
||||
crc = (crc << 1) ^ $1d
|
||||
else
|
||||
crc <<= 1
|
||||
}
|
||||
data++
|
||||
}
|
||||
return crc
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
test.benchmark()
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
const ubyte N_ITER = 10
|
||||
const ubyte SIZE = 32
|
||||
float[SIZE] array
|
||||
|
||||
const float expected = 3614007361536.000000
|
||||
const float epsilon = 10000000
|
||||
float res
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("pow.p8\n")
|
||||
txt.print("Calculates floating point exponential (")
|
||||
txt.print_uw(N_ITER)
|
||||
txt.print(" iterations)\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
ubyte i,j
|
||||
res = 0
|
||||
|
||||
for j in 0 to SIZE-1 {
|
||||
array[j]=0
|
||||
}
|
||||
|
||||
for i in 0 to N_ITER-1 {
|
||||
for j in 0 to SIZE-1 {
|
||||
array[j] += testpow(2.5 / ((i + 1) as float), j)
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0 to SIZE-1 {
|
||||
res += array[j]
|
||||
}
|
||||
}
|
||||
|
||||
sub testpow(float x, ubyte y) -> float
|
||||
{
|
||||
float tmp = x
|
||||
|
||||
if y == 0
|
||||
return 1
|
||||
|
||||
repeat y-1
|
||||
tmp *= x
|
||||
|
||||
return tmp
|
||||
}
|
||||
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("res = ")
|
||||
txt.print_f(res)
|
||||
float diff = expected - res;
|
||||
txt.print("\nexpected = ")
|
||||
txt.print_f(expected)
|
||||
txt.print("\nepsilon = ")
|
||||
txt.print_f(epsilon)
|
||||
txt.print("\ndiff = ")
|
||||
txt.print_f(diff)
|
||||
|
||||
if (diff < epsilon and diff > -epsilon)
|
||||
{
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print("[FAIL]\n")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
@@ -0,0 +1,85 @@
|
||||
%import textio
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
test.benchmark()
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
const ubyte N_ITER = 10
|
||||
const uword SIZE = 8191
|
||||
const uword EXPECTED = 1900
|
||||
uword prime_count
|
||||
^^bool @zp flags = memory("flags", SIZE, 0)
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("sieve.c\n")
|
||||
txt.print("Calculates the primes from 1 to ")
|
||||
txt.print_uw(SIZE * 2 + 2)
|
||||
txt.print(" (")
|
||||
txt.print_ub(N_ITER)
|
||||
txt.print(" iterations)\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
repeat N_ITER
|
||||
prime_count = sieve(SIZE)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("count=")
|
||||
txt.print_uw(prime_count)
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
sub sieve(uword size) -> uword
|
||||
{
|
||||
uword i, prime, k
|
||||
uword count = 1
|
||||
|
||||
for i in 0 to size-1
|
||||
flags[i] = true
|
||||
|
||||
for i in 0 to size-1
|
||||
{
|
||||
if flags[i]
|
||||
{
|
||||
prime = i + i + 3
|
||||
k = i + prime
|
||||
while k < size
|
||||
{
|
||||
flags[k] = false
|
||||
k += prime
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
%import textio
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
test.benchmark()
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
const ubyte N_ITER = 4
|
||||
const uword SIZE = 16000
|
||||
const uword EXPECTED = 3432
|
||||
uword prime_count
|
||||
^^ubyte @zp flags = memory("flags", SIZE/8+1, 0)
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("sieve_bit.p8\n")
|
||||
txt.print("Calculates the primes from 1 to ")
|
||||
txt.print_uw(SIZE * 2 + 2)
|
||||
txt.print(" (")
|
||||
txt.print_ub(N_ITER)
|
||||
txt.print(" iterations)\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
repeat N_ITER
|
||||
prime_count = sieve(SIZE)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("count=")
|
||||
txt.print_uw(prime_count)
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
ubyte[] bitv = [
|
||||
$01,
|
||||
$02,
|
||||
$04,
|
||||
$08,
|
||||
$10,
|
||||
$20,
|
||||
$40,
|
||||
$80
|
||||
]
|
||||
|
||||
sub check_flag(uword idx) -> bool
|
||||
{
|
||||
return flags[idx / 8] & bitv[lsb(idx) & 7] != 0
|
||||
}
|
||||
|
||||
sub clear_flag(uword idx)
|
||||
{
|
||||
flags[idx / 8] &= ~(bitv[lsb(idx) & 7])
|
||||
}
|
||||
|
||||
|
||||
sub sieve(uword size) -> uword
|
||||
{
|
||||
uword i, prime, k
|
||||
uword count=1
|
||||
|
||||
for i in 0 to (size / 8)
|
||||
flags[i] = $ff
|
||||
|
||||
for i in 0 to size-1
|
||||
{
|
||||
if check_flag(i)
|
||||
{
|
||||
prime = i + i + 3
|
||||
k = i + prime;
|
||||
while k < size
|
||||
{
|
||||
clear_flag(k)
|
||||
k += prime
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ adpcm {
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
adpcm.init(0,0)
|
||||
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
|
||||
uword @requirezp nibbles_ptr = $0800 ; for benchmark purposes, the exact nibbles don't really matter, so we just take this benchmark program itself as input
|
||||
repeat 252/2 {
|
||||
unroll 2 {
|
||||
ubyte @zp nibble = @(nibbles_ptr)
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
; Binary Search Tree.
|
||||
; It's a simple implementation for test/demonstration purposes of the pointer support;
|
||||
; no balancing is done and memory is not freed when elements are removed.
|
||||
|
||||
%import textio
|
||||
|
||||
btree {
|
||||
|
||||
sub benchmark(uword max_time) -> uword {
|
||||
txt.nl()
|
||||
cbm.SETTIM(0,0,0)
|
||||
uword score
|
||||
while cbm.RDTIM16() < max_time {
|
||||
bench_operations()
|
||||
txt.chrout('.')
|
||||
score++
|
||||
}
|
||||
txt.nl()
|
||||
return score
|
||||
}
|
||||
|
||||
sub bench_operations() {
|
||||
arena.freeall()
|
||||
btree.root = 0
|
||||
|
||||
for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716]
|
||||
btree.add(cx16.r0)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
|
||||
void btree.contains(203)
|
||||
void btree.contains(204)
|
||||
void btree.contains(605)
|
||||
void btree.contains(606)
|
||||
|
||||
btree.remove(9999)
|
||||
btree.remove(97)
|
||||
btree.remove(187)
|
||||
btree.remove(203)
|
||||
btree.remove(275)
|
||||
btree.remove(321)
|
||||
btree.remove(520)
|
||||
btree.remove(562)
|
||||
btree.remove(606)
|
||||
btree.remove(719)
|
||||
btree.remove(794)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
}
|
||||
|
||||
struct Node {
|
||||
^^Node left
|
||||
^^Node right
|
||||
uword value
|
||||
}
|
||||
|
||||
^^Node root = 0
|
||||
|
||||
sub add(uword value) {
|
||||
^^Node node = arena.alloc(sizeof(Node))
|
||||
node.value = value
|
||||
node.left = node.right = 0
|
||||
|
||||
if root==0
|
||||
root=node
|
||||
else {
|
||||
^^Node parent = root
|
||||
repeat {
|
||||
if parent.value >= value {
|
||||
if parent.left!=0
|
||||
parent = parent.left
|
||||
else {
|
||||
parent.left = node
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if parent.right!=0
|
||||
parent = parent.right
|
||||
else {
|
||||
parent.right = node
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub contains(uword value) -> bool {
|
||||
^^Node r = root
|
||||
while r!=0 {
|
||||
if r.value==value
|
||||
return true
|
||||
if r.value>value
|
||||
r = r.left
|
||||
else
|
||||
r = r.right
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
ubyte count
|
||||
|
||||
if root!=0
|
||||
count_node(root)
|
||||
|
||||
return count
|
||||
|
||||
sub count_node(^^Node r) {
|
||||
count++
|
||||
if r.left!=0 {
|
||||
pushw(r)
|
||||
count_node(r.left)
|
||||
r = popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
pushw(r)
|
||||
count_node(r.right)
|
||||
r = popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub remove(uword value) {
|
||||
; note: we don't deallocate the memory from the node, for simplicity sake
|
||||
^^Node n = root
|
||||
^^Node parent = 0
|
||||
while n!=0 {
|
||||
if n.value==value {
|
||||
if n.left==0
|
||||
replacechild(parent, n, n.right)
|
||||
else if n.right==0
|
||||
replacechild(parent, n, n.left)
|
||||
else {
|
||||
; Both left & right subtrees are present.
|
||||
; N = node to delete.
|
||||
; Find N's successor S. (N's right subtree's minimum element)
|
||||
; Attach N's left subtree to S.left (S doesn't have a left child)
|
||||
; Attach N's right subtree to Parent in place of N.
|
||||
^^Node successor = find_successor(n)
|
||||
successor.left = n.left
|
||||
replacechild(parent, n, n.right)
|
||||
}
|
||||
return
|
||||
}
|
||||
parent = n
|
||||
if n.value>value
|
||||
n = n.left
|
||||
else
|
||||
n = n.right
|
||||
}
|
||||
|
||||
sub find_successor(^^Node p) -> ^^Node {
|
||||
^^Node succ = p
|
||||
p = p.right
|
||||
while p!=0 {
|
||||
succ = p
|
||||
p = p.left
|
||||
}
|
||||
return succ
|
||||
}
|
||||
|
||||
sub replacechild(^^Node p, ^^Node child, ^^Node newchild) {
|
||||
if p.left==child
|
||||
p.left = newchild
|
||||
else
|
||||
p.right = newchild
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_inorder() {
|
||||
if root!=0
|
||||
process_tree(root)
|
||||
|
||||
sub process_tree(^^Node r) {
|
||||
if r.left!=0 {
|
||||
pushw(r)
|
||||
process_tree(r.left)
|
||||
r = popw()
|
||||
}
|
||||
cx16.r0 = r.value
|
||||
if r.right!=0 {
|
||||
pushw(r)
|
||||
process_tree(r.right)
|
||||
r = popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_preorder() {
|
||||
if root!=0
|
||||
process_tree(root,0)
|
||||
|
||||
sub process_tree(^^Node r, ubyte depth) {
|
||||
cx16.r0 = r.value
|
||||
if r.left!=0 {
|
||||
pushw(r)
|
||||
push(depth)
|
||||
process_tree(r.left, depth+1)
|
||||
depth = pop()
|
||||
r = popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
pushw(r)
|
||||
push(depth)
|
||||
process_tree(r.right, depth+1)
|
||||
depth = pop()
|
||||
r = popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
arena {
|
||||
; extremely trivial arena allocator (that never frees)
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
uword next = buffer
|
||||
|
||||
sub alloc(ubyte size) -> uword {
|
||||
defer next += size
|
||||
return next
|
||||
}
|
||||
|
||||
sub freeall() {
|
||||
next = buffer
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ circles {
|
||||
|
||||
sub draw(bool use_kernal, uword max_time) -> uword {
|
||||
if use_kernal
|
||||
void cx16.set_screen_mode(128)
|
||||
cx16.set_screen_mode(128)
|
||||
else
|
||||
gfx_lores.graphics_mode()
|
||||
|
||||
@@ -33,7 +33,7 @@ circles {
|
||||
}
|
||||
|
||||
if use_kernal
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
else {
|
||||
gfx_lores.text_mode()
|
||||
}
|
||||
|
||||
@@ -61,8 +61,7 @@ life {
|
||||
math.rndseed(12345,9999)
|
||||
uword offset = STRIDE+1
|
||||
ubyte x
|
||||
ubyte y
|
||||
for y in 0 to HEIGHT-1 {
|
||||
repeat HEIGHT {
|
||||
for x in 0 to WIDTH-1 {
|
||||
active_world[offset+x] = math.rnd() & 1
|
||||
}
|
||||
|
||||
@@ -35,12 +35,12 @@ queens {
|
||||
if could_place(row, col) {
|
||||
board[row] = col
|
||||
; we need to save the local variables row and col.
|
||||
sys.push(row)
|
||||
sys.push(col)
|
||||
push(row)
|
||||
push(col)
|
||||
continue_running = place_queen(row + 1)
|
||||
; restore the local variables after the recursive call.
|
||||
col = sys.pop()
|
||||
row = sys.pop()
|
||||
col = pop()
|
||||
row = pop()
|
||||
board[row] = 0
|
||||
|
||||
if not continue_running
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
%import sprites
|
||||
%import coroutines
|
||||
%import math
|
||||
|
||||
|
||||
animsprites {
|
||||
uword num_iterations
|
||||
ubyte[64] sx
|
||||
ubyte[64] sy
|
||||
ubyte[64] sc
|
||||
ubyte[64] dx
|
||||
ubyte[64] dy
|
||||
uword maximum_duration
|
||||
|
||||
sub benchmark(uword max_duration) -> uword {
|
||||
maximum_duration = max_duration
|
||||
math.rndseed(1122,9876)
|
||||
cx16.set_screen_mode(3)
|
||||
cx16.mouse_config2(1)
|
||||
sprites.set_mousepointer_hand()
|
||||
repeat 64
|
||||
void coroutines.add(animsprite, 0)
|
||||
cx16.mouse_config2(0)
|
||||
|
||||
cbm.SETTIM(0,0,0)
|
||||
coroutines.run(supervisor)
|
||||
|
||||
sprites.reset(0, 64)
|
||||
return num_iterations
|
||||
}
|
||||
|
||||
sub supervisor() -> bool {
|
||||
if cbm.RDTIM16() >= maximum_duration {
|
||||
coroutines.killall()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub animsprite() {
|
||||
num_iterations++
|
||||
; set up the sprite
|
||||
ubyte sprnum = coroutines.current()
|
||||
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
|
||||
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
|
||||
sx[sprnum] = math.rnd()
|
||||
sy[sprnum] = math.rnd()
|
||||
sc[sprnum] = math.rnd()
|
||||
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
|
||||
; move the sprite around
|
||||
while sc[sprnum]!=0 {
|
||||
animate(sprnum)
|
||||
void coroutines.yield()
|
||||
sprnum = coroutines.current()
|
||||
}
|
||||
|
||||
sub animate(ubyte spr) {
|
||||
defer sc[spr]--
|
||||
sprites.pos(spr, sx[spr], sy[spr])
|
||||
sx[spr] += dx[spr]
|
||||
sy[spr] += dy[spr]
|
||||
}
|
||||
|
||||
; end the task but replace it with a fresh animated sprite task
|
||||
void coroutines.add(animsprite, 0)
|
||||
}
|
||||
}
|
||||
@@ -472,7 +472,7 @@ elite_galaxy {
|
||||
generate_next_planet()
|
||||
textelite.num_commands++
|
||||
}
|
||||
elite_planet.name = make_current_planet_name()
|
||||
void strings.copy(make_current_planet_name(), elite_planet.name)
|
||||
init_market_for_planet()
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ elite_galaxy {
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet()
|
||||
elite_planet.name = make_current_planet_name()
|
||||
void strings.copy(make_current_planet_name(), elite_planet.name)
|
||||
if elite_util.prefix_matches(nameptr, elite_planet.name) {
|
||||
ubyte distance = elite_planet.distance(x, y)
|
||||
if distance < current_distance {
|
||||
@@ -532,7 +532,7 @@ elite_galaxy {
|
||||
; else
|
||||
; txt.chrout('-')
|
||||
; txt.spc()
|
||||
elite_planet.name = make_current_planet_name()
|
||||
void strings.copy(make_current_planet_name(), elite_planet.name)
|
||||
elite_planet.display(true, distance)
|
||||
}
|
||||
pn++
|
||||
@@ -548,7 +548,7 @@ elite_galaxy {
|
||||
str current_name = " " ; 8 max
|
||||
ubyte pn = 0
|
||||
|
||||
current_name = elite_planet.name
|
||||
void strings.copy(elite_planet.name, current_name)
|
||||
init(number)
|
||||
; txt.clear_screen()
|
||||
; txt.print("Galaxy #")
|
||||
@@ -569,7 +569,7 @@ elite_galaxy {
|
||||
generate_next_planet()
|
||||
ubyte distance = elite_planet.distance(px, py)
|
||||
if distance <= max_distance {
|
||||
elite_planet.name = make_current_planet_name()
|
||||
void strings.copy(make_current_planet_name(), elite_planet.name)
|
||||
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
|
||||
uword tx = elite_planet.x
|
||||
uword ty = elite_planet.y
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
%import b_queens
|
||||
%import b_textelite
|
||||
%import b_maze
|
||||
%import b_sprites
|
||||
%import b_btree
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
@@ -24,17 +26,20 @@ main {
|
||||
|
||||
str[20] benchmark_names
|
||||
uword[20] benchmark_score
|
||||
str version = "12.1"
|
||||
|
||||
|
||||
sub start() {
|
||||
ubyte benchmark_number
|
||||
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
|
||||
txt.print("\n\n\n prog8 compiler benchmark tests.\n")
|
||||
sys.wait(60)
|
||||
txt.print("\n\n\n prog8 compiler benchmark tests.\n\n benchmark version ")
|
||||
txt.print(version)
|
||||
txt.print("\n\n")
|
||||
sys.wait(120)
|
||||
|
||||
benchmark_number = 0
|
||||
|
||||
@@ -63,43 +68,50 @@ main {
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("circles with gfx_lores")
|
||||
benchmark_score[benchmark_number] = circles.draw(false, 300)
|
||||
benchmark_score[benchmark_number] = circles.draw(false, 400)
|
||||
benchmark_number++
|
||||
|
||||
; announce_benchmark("circles with kernal")
|
||||
; benchmark_score[benchmark_number] = circles.draw(true, 300)
|
||||
; benchmark_number++
|
||||
|
||||
announce_benchmark("text-elite")
|
||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("sprites-coroutines-defer")
|
||||
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("btree-struct-pointers")
|
||||
benchmark_score[benchmark_number] = btree.benchmark(200)
|
||||
benchmark_number++
|
||||
|
||||
benchmark_names[benchmark_number] = 0
|
||||
benchmark_score[benchmark_number] = 0
|
||||
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
uword final_score
|
||||
uword total_score
|
||||
benchmark_number = 0
|
||||
txt.print("\nscore benchmark\n\n")
|
||||
txt.print("\nscore benchmark (")
|
||||
txt.print(version)
|
||||
txt.print(")\n\n")
|
||||
|
||||
do {
|
||||
txt.spc()
|
||||
txt.print_uw(benchmark_score[benchmark_number])
|
||||
txt.column(6)
|
||||
txt.print(benchmark_names[benchmark_number])
|
||||
final_score += benchmark_score[benchmark_number]
|
||||
total_score += benchmark_score[benchmark_number]
|
||||
txt.nl()
|
||||
benchmark_number++
|
||||
} until benchmark_names[benchmark_number]==0
|
||||
|
||||
txt.print("\n\nfinal score : ")
|
||||
txt.print_uw(final_score)
|
||||
txt.nl()
|
||||
txt.print("\n\ntotal score : ")
|
||||
txt.print_uw(total_score)
|
||||
txt.print(" (higher=better)\n")
|
||||
|
||||
sub announce_benchmark(str name) {
|
||||
benchmark_names[benchmark_number] = name
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
|
||||
+77
-6
@@ -1,9 +1,9 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.10"
|
||||
kotlin("jvm") version "2.4.10"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -12,12 +12,15 @@ allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
freeCompilerArgs = listOf()
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
|
||||
// languageVersion.set(KotlinVersion.KOTLIN_2_3)
|
||||
}
|
||||
sourceSets.all {
|
||||
languageSettings {
|
||||
@@ -28,7 +31,75 @@ allprojects {
|
||||
}
|
||||
|
||||
java {
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Common Configuration for All Subprojects
|
||||
// ============================================================================
|
||||
// This avoids duplication in each module's build.gradle.kts
|
||||
// ============================================================================
|
||||
|
||||
subprojects {
|
||||
// Common dependency exclusions for all subprojects
|
||||
// Note: antlr4 exclusion is done per-module since parser module needs it
|
||||
configurations.all {
|
||||
exclude(group = "com.ibm.icu", module = "icu4j")
|
||||
}
|
||||
|
||||
// Common test dependencies via Kotest BOM (Bill of Materials)
|
||||
// This manages all Kotest module versions centrally
|
||||
dependencies {
|
||||
testImplementation(platform("io.kotest:kotest-bom:6.2.3"))
|
||||
// implementation("com.github.irmen:ksim65:v2.1")
|
||||
}
|
||||
|
||||
// Common sourceSets configuration
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDir("${project.projectDir}/src")
|
||||
resources.srcDir("${project.projectDir}/res")
|
||||
}
|
||||
test {
|
||||
java.srcDir("${project.projectDir}/test")
|
||||
}
|
||||
}
|
||||
|
||||
// Common test configuration
|
||||
tasks.withType<Test>().configureEach {
|
||||
// Enable JUnit 5 (required for Kotest)
|
||||
useJUnitPlatform()
|
||||
|
||||
// Enable concurrent test execution for Kotest
|
||||
// Set parallelism to number of CPU cores
|
||||
jvmArgs("-Dkotest.framework.parallelism=${Runtime.getRuntime().availableProcessors()}")
|
||||
|
||||
// Disable Kotest autoscan warning
|
||||
jvmArgs("-Dkotest.framework.classpath.scanning.autoscan.disable=true")
|
||||
|
||||
// Enable Gradle's parallel test execution (runs multiple test classes concurrently)
|
||||
// Use 50% of available processors to avoid over-subscription
|
||||
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1)
|
||||
|
||||
// Allow filtering tests via -PtestFilter="*TestName*" from command line
|
||||
// Example: gradle test -PtestFilter="*TestLookup*"
|
||||
val testFilter = project.findProperty("testFilter")?.toString()
|
||||
if(testFilter != null) {
|
||||
filter {
|
||||
includeTestsMatching(testFilter)
|
||||
}
|
||||
}
|
||||
|
||||
// Show test results - only show failures to reduce noise
|
||||
testLogging {
|
||||
events("failed")
|
||||
// Show full exception details for failures
|
||||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
showExceptions = true
|
||||
showCauses = true
|
||||
showStackTraces = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,11 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
`java-test-fixtures`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
}
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.3.1")
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir("${project.projectDir}/src")
|
||||
}
|
||||
resources {
|
||||
srcDir("${project.projectDir}/res")
|
||||
}
|
||||
}
|
||||
testImplementation("io.kotest:kotest-runner-junit5")
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,51 @@
|
||||
package prog8.code
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||
|
||||
val PROG8_CONTAINER_MODULES = arrayOf(INTERNED_STRINGS_MODULENAME) // option to add more if needed one day
|
||||
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
|
||||
|
||||
/**
|
||||
* Returns the absolute path of the given path,
|
||||
* where links are replaced by the actual directories,
|
||||
* and containing no redundant path elements.
|
||||
* If the path doesn't refer to an existing directory or file on the file system,
|
||||
* it is returned unchanged.
|
||||
*/
|
||||
fun Path.sanitize(): Path {
|
||||
return try {
|
||||
this.toRealPath().normalize()
|
||||
} catch (_: java.nio.file.NoSuchFileException) {
|
||||
this.absolute().normalize()
|
||||
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
||||
} catch (iox: IOException) {
|
||||
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object SymbolNames {
|
||||
/**
|
||||
* Strips Prog8 symbol prefixes (p8b_, p8t_, p8s_, p8v_, p8l_, p8c_) from a scoped name.
|
||||
* Example: "p8b_plane.p8t_Point" -> "plane.Point"
|
||||
* Used to compare names across different contexts where prefixes may or may not be applied.
|
||||
* See https://github.com/irmen/prog8/issues/198
|
||||
*/
|
||||
fun stripPrefixes(scopedName: String): String {
|
||||
return scopedName.split('.')
|
||||
.map { part ->
|
||||
if(part.length > 4 && part.startsWith("p8") && part[2].isLetter() && part[3] == '_')
|
||||
part.drop(4)
|
||||
else part
|
||||
}
|
||||
.joinToString(".")
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
package prog8.code
|
||||
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.ast.PtNode
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
|
||||
|
||||
/**
|
||||
* Tree structure containing all symbol definitions in the program
|
||||
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
|
||||
*/
|
||||
class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) {
|
||||
/**
|
||||
* The table as a flat mapping of scoped names to the StNode.
|
||||
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||
*/
|
||||
|
||||
private var cachedFlat: Map<String, StNode>? = null
|
||||
|
||||
val flat: Map<String, StNode> get() {
|
||||
if(cachedFlat!=null)
|
||||
return cachedFlat!!
|
||||
|
||||
val result = mutableMapOf<String, StNode>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
result[child.value.scopedName] = child.value
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
cachedFlat = result
|
||||
return result
|
||||
}
|
||||
|
||||
fun resetCachedFlat() {
|
||||
cachedFlat = null
|
||||
}
|
||||
|
||||
val allVariables: Collection<StStaticVariable> by lazy {
|
||||
// can't be done with a generic function because those don't support local recursive functions yet
|
||||
val vars = mutableListOf<StStaticVariable>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
if(child.value.type== StNodeType.STATICVAR)
|
||||
vars.add(child.value as StStaticVariable)
|
||||
else
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
vars
|
||||
}
|
||||
|
||||
val allMemMappedVariables: Collection<StMemVar> by lazy {
|
||||
// can't be done with a generic function because those don't support local recursive functions yet
|
||||
val vars = mutableListOf<StMemVar>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
if(child.value.type== StNodeType.MEMVAR)
|
||||
vars.add(child.value as StMemVar)
|
||||
else
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
vars
|
||||
}
|
||||
|
||||
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
||||
// can't be done with a generic function because those don't support local recursive functions yet
|
||||
val vars = mutableListOf<StMemorySlab>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
if(child.value.type== StNodeType.MEMORYSLAB)
|
||||
vars.add(child.value as StMemorySlab)
|
||||
else
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
vars
|
||||
}
|
||||
|
||||
override fun lookup(scopedName: String) = flat[scopedName]
|
||||
|
||||
fun getLength(name: String): Int? {
|
||||
return when(val node = flat[name]) {
|
||||
is StMemVar -> node.length
|
||||
is StMemorySlab -> node.size.toInt()
|
||||
is StStaticVariable -> node.length
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum class StNodeType {
|
||||
GLOBAL,
|
||||
// MODULE, // not used with current scoping rules
|
||||
BLOCK,
|
||||
SUBROUTINE,
|
||||
EXTSUB,
|
||||
LABEL,
|
||||
STATICVAR,
|
||||
MEMVAR,
|
||||
CONSTANT,
|
||||
BUILTINFUNC,
|
||||
MEMORYSLAB
|
||||
}
|
||||
|
||||
|
||||
open class StNode(val name: String,
|
||||
val type: StNodeType,
|
||||
val astNode: PtNode?,
|
||||
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||
) {
|
||||
|
||||
lateinit var parent: StNode
|
||||
|
||||
val scopedName: String by lazy { scopedNameList.joinToString(".") }
|
||||
|
||||
open fun lookup(scopedName: String) =
|
||||
lookup(scopedName.split('.'))
|
||||
|
||||
fun lookupUnscopedOrElse(name: String, default: () -> StNode) =
|
||||
lookupUnscoped(name) ?: default()
|
||||
|
||||
fun lookupOrElse(scopedName: String, default: () -> StNode): StNode =
|
||||
lookup(scopedName.split('.')) ?: default()
|
||||
|
||||
fun lookupUnscoped(name: String): StNode? {
|
||||
// first consider the builtin functions
|
||||
var globalscope = this
|
||||
while(globalscope.type!= StNodeType.GLOBAL)
|
||||
globalscope = globalscope.parent
|
||||
val globalNode = globalscope.children[name]
|
||||
if(globalNode!=null && globalNode.type== StNodeType.BUILTINFUNC)
|
||||
return globalNode
|
||||
|
||||
// search for the unqualified name in the current scope or its parent scopes
|
||||
var scope=this
|
||||
while(true) {
|
||||
val node = scope.children[name]
|
||||
if(node!=null)
|
||||
return node
|
||||
if(scope.type== StNodeType.GLOBAL)
|
||||
return null
|
||||
else
|
||||
scope = scope.parent
|
||||
}
|
||||
}
|
||||
|
||||
fun add(child: StNode) {
|
||||
children[child.name] = child
|
||||
child.parent = this
|
||||
}
|
||||
|
||||
private val scopedNameList: List<String> by lazy {
|
||||
if(type==StNodeType.GLOBAL)
|
||||
emptyList()
|
||||
else
|
||||
parent.scopedNameList + name
|
||||
}
|
||||
|
||||
private fun lookup(scopedName: List<String>): StNode? {
|
||||
// a scoped name refers to a name in another namespace, and always stars from the root.
|
||||
var node = this
|
||||
while(node.type!=StNodeType.GLOBAL)
|
||||
node = node.parent
|
||||
|
||||
for(name in scopedName) {
|
||||
if(name in node.children)
|
||||
node = node.children.getValue(name)
|
||||
else
|
||||
return null
|
||||
}
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
class StStaticVariable(name: String,
|
||||
val dt: DataType,
|
||||
val initializationStringValue: StString?,
|
||||
val initializationArrayValue: StArray?,
|
||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||
val zpwish: ZeropageWish, // used in the variable allocator
|
||||
val align: Int,
|
||||
astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) {
|
||||
|
||||
var initializationNumericValue: Double? = null
|
||||
private set
|
||||
|
||||
fun setOnetimeInitNumeric(number: Double) {
|
||||
// In certain cases the init value of an existing var should be updated,
|
||||
// so we can't ask this as a constructor parameter.
|
||||
// This has to do with the way Prog8 does the (re)initialization of such variables: via code assignment statements.
|
||||
// Certain codegens might want to put them back into the variable directly.
|
||||
// For strings and arrays this doesn't occur - these are always already specced at creation time.
|
||||
initializationNumericValue = number
|
||||
}
|
||||
|
||||
val uninitialized: Boolean
|
||||
get() = initializationArrayValue==null && initializationStringValue==null && initializationNumericValue==null
|
||||
|
||||
init {
|
||||
if(length!=null) {
|
||||
require(initializationNumericValue == null)
|
||||
if(initializationArrayValue!=null)
|
||||
require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length)
|
||||
}
|
||||
if(initializationNumericValue!=null) {
|
||||
require(dt.isNumericOrBool)
|
||||
}
|
||||
if(initializationArrayValue!=null) {
|
||||
require(dt.isArray)
|
||||
require(length == initializationArrayValue.size)
|
||||
}
|
||||
if(initializationStringValue!=null) {
|
||||
require(dt.isString)
|
||||
require(length == initializationStringValue.first.length + 1)
|
||||
}
|
||||
if(align > 0) {
|
||||
require(dt.isString || dt.isArray)
|
||||
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode?) :
|
||||
StNode(name, StNodeType.CONSTANT, astNode)
|
||||
|
||||
|
||||
class StMemVar(name: String,
|
||||
val dt: DataType,
|
||||
val address: UInt,
|
||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||
astNode: PtNode?) :
|
||||
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||
|
||||
init{
|
||||
require(!dt.isString)
|
||||
if(dt.isStringly && !dt.isWord)
|
||||
requireNotNull(length)
|
||||
}
|
||||
}
|
||||
|
||||
class StMemorySlab(
|
||||
name: String,
|
||||
val size: UInt,
|
||||
val align: UInt,
|
||||
astNode: PtNode?
|
||||
):
|
||||
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
||||
|
||||
|
||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returns: List<DataType>, astNode: PtNode) :
|
||||
StNode(name, StNodeType.SUBROUTINE, astNode)
|
||||
|
||||
|
||||
class StExtSub(name: String,
|
||||
val address: PtAsmSub.Address?, // null in case of asmsub, specified in case of extsub.
|
||||
val parameters: List<StExtSubParameter>,
|
||||
val returns: List<StExtSubParameter>,
|
||||
astNode: PtNode) :
|
||||
StNode(name, StNodeType.EXTSUB, astNode)
|
||||
|
||||
|
||||
|
||||
class StSubroutineParameter(val name: String, val type: DataType, val register: RegisterOrPair?)
|
||||
class StExtSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?) {
|
||||
init {
|
||||
if(number!=null) require(addressOfSymbol==null && boolean==null)
|
||||
if(addressOfSymbol!=null) require(number==null && boolean==null)
|
||||
if(boolean!=null) require(addressOfSymbol==null && number==null)
|
||||
}
|
||||
}
|
||||
|
||||
typealias StString = Pair<String, Encoding>
|
||||
typealias StArray = List<StArrayElement>
|
||||
@@ -1,214 +0,0 @@
|
||||
package prog8.code
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
|
||||
fun make(): SymbolTable {
|
||||
val st = SymbolTable(program)
|
||||
|
||||
BuiltinFunctions.forEach {
|
||||
val dt = DataType.forDt(it.value.returnType ?: BaseDataType.UNDEFINED)
|
||||
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, dt, Position.DUMMY)))
|
||||
}
|
||||
|
||||
val scopestack = ArrayDeque<StNode>()
|
||||
scopestack.add(st)
|
||||
program.children.forEach {
|
||||
addToSt(it, scopestack)
|
||||
}
|
||||
require(scopestack.size==1)
|
||||
|
||||
if(options.compTarget.name != VMTarget.NAME) {
|
||||
listOf(
|
||||
PtMemMapped("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), options.compTarget.zeropage.SCRATCH_B1, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE), options.compTarget.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD), options.compTarget.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD), options.compTarget.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||
).forEach {
|
||||
it.parent = program
|
||||
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||
}
|
||||
}
|
||||
|
||||
return st
|
||||
}
|
||||
|
||||
private fun addToSt(node: PtNode, scope: ArrayDeque<StNode>) {
|
||||
val stNode = when(node) {
|
||||
is PtAsmSub -> {
|
||||
val parameters = node.parameters.map { StExtSubParameter(it.first, it.second.type) }
|
||||
val returns = node.returns.map { StExtSubParameter(it.first, it.second) }
|
||||
StExtSub(node.name, node.address, parameters, returns, node)
|
||||
}
|
||||
is PtBlock -> {
|
||||
StNode(node.name, StNodeType.BLOCK, node)
|
||||
}
|
||||
is PtConstant -> {
|
||||
require(node.type.isNumericOrBool)
|
||||
StConstant(node.name, node.type.base, node.value, node)
|
||||
}
|
||||
is PtLabel -> {
|
||||
StNode(node.name, StNodeType.LABEL, node)
|
||||
}
|
||||
is PtMemMapped -> {
|
||||
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
|
||||
}
|
||||
is PtSub -> {
|
||||
val params = node.parameters.map {StSubroutineParameter(it.name, it.type, it.register) }
|
||||
StSub(node.name, params, node.returns, node)
|
||||
}
|
||||
is PtVariable -> {
|
||||
val initialNumeric: Double?
|
||||
val initialString: StString?
|
||||
val initialArray: StArray?
|
||||
val numElements: Int?
|
||||
val value = node.value
|
||||
if(value!=null) {
|
||||
when (value) {
|
||||
is PtString -> {
|
||||
initialString = StString(value.value, value.encoding)
|
||||
initialArray = null
|
||||
initialNumeric = null
|
||||
numElements = value.value.length + 1 // include the terminating 0-byte
|
||||
}
|
||||
is PtArray -> {
|
||||
initialArray = makeInitialArray(value)
|
||||
initialString = null
|
||||
initialNumeric = null
|
||||
numElements = initialArray.size
|
||||
require(node.arraySize?.toInt()==numElements)
|
||||
}
|
||||
else -> {
|
||||
require(value is PtNumber)
|
||||
initialString = null
|
||||
initialArray = null
|
||||
val number = value.number
|
||||
initialNumeric = number
|
||||
numElements = node.arraySize?.toInt()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
initialNumeric = null
|
||||
initialArray = null
|
||||
initialString = null
|
||||
numElements = node.arraySize?.toInt()
|
||||
}
|
||||
// if(node.type in SplitWordArrayTypes) {
|
||||
// ... split array also add _lsb and _msb to symboltable?
|
||||
// }
|
||||
val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align.toInt(), node)
|
||||
if(initialNumeric!=null)
|
||||
stVar.setOnetimeInitNumeric(initialNumeric)
|
||||
stVar
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
if(node.name=="memory") {
|
||||
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
|
||||
require(node.name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||
val slabname = (node.args[0] as PtString).value
|
||||
val size = (node.args[1] as PtNumber).number.toUInt()
|
||||
val align = (node.args[2] as PtNumber).number.toUInt()
|
||||
// don't add memory slabs in nested scope, just put them in the top level of the ST
|
||||
scope.first().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||
}
|
||||
null
|
||||
}
|
||||
else -> null // node is not present in the ST
|
||||
}
|
||||
|
||||
if(stNode!=null) {
|
||||
scope.last().add(stNode)
|
||||
scope.add(stNode)
|
||||
}
|
||||
node.children.forEach {
|
||||
addToSt(it, scope)
|
||||
}
|
||||
if(stNode!=null)
|
||||
scope.removeLast()
|
||||
}
|
||||
|
||||
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
|
||||
return value.children.map {
|
||||
when(it) {
|
||||
is PtAddressOf -> {
|
||||
if(it.isFromArrayElement)
|
||||
TODO("address-of array element $it in initial array value")
|
||||
StArrayElement(null, it.identifier.name, null)
|
||||
}
|
||||
is PtNumber -> StArrayElement(it.number, null, null)
|
||||
is PtBool -> StArrayElement(null, null, it.value)
|
||||
else -> throw AssemblyError("invalid array element $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// override fun visit(decl: VarDecl) {
|
||||
// val node =
|
||||
// when(decl.type) {
|
||||
// VarDeclType.VAR -> {
|
||||
// var initialNumeric = (decl.value as? NumericLiteral)?.number
|
||||
// if(initialNumeric==0.0)
|
||||
// initialNumeric=null // variable will go into BSS and this will be set to 0
|
||||
// val initialStringLit = decl.value as? StringLiteral
|
||||
// val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
|
||||
// val initialArrayLit = decl.value as? ArrayLiteral
|
||||
// val initialArray = makeInitialArray(initialArrayLit)
|
||||
// if(decl.isArray && decl.datatype !in ArrayDatatypes)
|
||||
// throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
|
||||
// val numElements =
|
||||
// if(decl.isArray)
|
||||
// decl.arraysize!!.constIndex()
|
||||
// else if(initialStringLit!=null)
|
||||
// initialStringLit.value.length+1 // include the terminating 0-byte
|
||||
// else
|
||||
// null
|
||||
// val bss = if(decl.datatype==DataType.STR)
|
||||
// false
|
||||
// else if(decl.isArray)
|
||||
// initialArray.isNullOrEmpty()
|
||||
// else
|
||||
// initialNumeric == null
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
// StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, astNode, decl.position)
|
||||
// }
|
||||
// VarDeclType.CONST -> {
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
// StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, astNode, decl.position)
|
||||
// }
|
||||
// VarDeclType.MEMORY -> {
|
||||
// val numElements =
|
||||
// if(decl.isArray)
|
||||
// decl.arraysize!!.constIndex()
|
||||
// else null
|
||||
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||
// StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, astNode, decl.position)
|
||||
// }
|
||||
// }
|
||||
// scopestack.peek().add(node)
|
||||
// // st.origAstLinks[decl] = node
|
||||
// }
|
||||
//
|
||||
// private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
|
||||
// if(arrayLit==null)
|
||||
// return null
|
||||
// return arrayLit.value.map {
|
||||
// when(it){
|
||||
// is AddressOf -> {
|
||||
// val scopedName = it.identifier.targetNameAndType(program).first
|
||||
// StArrayElement(null, scopedName)
|
||||
// }
|
||||
// is IdentifierReference -> {
|
||||
// val scopedName = it.targetNameAndType(program).first
|
||||
// StArrayElement(null, scopedName)
|
||||
// }
|
||||
// is NumericLiteral -> StArrayElement(it.number, null)
|
||||
// else -> throw FatalAstException("weird element dt in array literal")
|
||||
// }
|
||||
// }.toList()
|
||||
// }
|
||||
//
|
||||
@@ -1,129 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.source.SourceCode
|
||||
import java.nio.file.Path
|
||||
|
||||
// New simplified AST for the code generator.
|
||||
|
||||
|
||||
sealed class PtNode(val position: Position) {
|
||||
|
||||
val children = mutableListOf<PtNode>()
|
||||
lateinit var parent: PtNode
|
||||
|
||||
fun add(child: PtNode) {
|
||||
children.add(child)
|
||||
child.parent = this
|
||||
}
|
||||
|
||||
fun add(index: Int, child: PtNode) {
|
||||
children.add(index, child)
|
||||
child.parent = this
|
||||
}
|
||||
|
||||
fun definingBlock() = findParentNode<PtBlock>(this)
|
||||
fun definingSub() = findParentNode<PtSub>(this)
|
||||
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
|
||||
fun definingISub() = findParentNode<IPtSubroutine>(this)
|
||||
}
|
||||
|
||||
|
||||
sealed interface IPtStatementContainer
|
||||
|
||||
|
||||
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
|
||||
|
||||
|
||||
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||
// Note that as an exception, the 'name' is not read-only
|
||||
// but a var. This is to allow for cheap node renames.
|
||||
val scopedName: String
|
||||
get() {
|
||||
var namedParent: PtNode = this.parent
|
||||
return if(namedParent is PtProgram)
|
||||
name
|
||||
else {
|
||||
while (namedParent !is PtNamedNode)
|
||||
namedParent = namedParent.parent
|
||||
namedParent.scopedName + "." + name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtProgram(
|
||||
val name: String,
|
||||
val memsizer: IMemSizer,
|
||||
val encoding: IStringEncoding
|
||||
) : PtNode(Position.DUMMY) {
|
||||
|
||||
// fun allModuleDirectives(): Sequence<PtDirective> =
|
||||
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
|
||||
|
||||
fun allBlocks(): Sequence<PtBlock> =
|
||||
children.asSequence().filterIsInstance<PtBlock>()
|
||||
|
||||
fun entrypoint(): PtSub? =
|
||||
// returns the main.start subroutine if it exists
|
||||
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
|
||||
?.children
|
||||
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?
|
||||
}
|
||||
|
||||
|
||||
class PtBlock(name: String,
|
||||
val library: Boolean,
|
||||
val source: SourceCode, // taken from the module the block is defined in.
|
||||
val options: Options,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtStatementContainer {
|
||||
class Options(val address: UInt? = null,
|
||||
val forceOutput: Boolean = false,
|
||||
val noSymbolPrefixing: Boolean = false,
|
||||
val veraFxMuls: Boolean = false,
|
||||
val ignoreUnused: Boolean = false)
|
||||
}
|
||||
|
||||
|
||||
class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
|
||||
init {
|
||||
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
|
||||
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
|
||||
companion object {
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtBreakpoint(position: Position): PtNode(position)
|
||||
|
||||
|
||||
class PtAlign(val align: UInt, position: Position): PtNode(position)
|
||||
|
||||
|
||||
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position)
|
||||
|
||||
|
||||
class PtNop(position: Position): PtNode(position)
|
||||
|
||||
|
||||
// find the parent node of a specific type or interface
|
||||
// (useful to figure out in what namespace/block something is defined, etc.)
|
||||
inline fun <reified T> findParentNode(node: PtNode): T? {
|
||||
var candidate = node.parent
|
||||
while(candidate !is T && candidate !is PtProgram)
|
||||
candidate = candidate.parent
|
||||
return if(candidate is PtProgram)
|
||||
null
|
||||
else
|
||||
candidate as T
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
sealed interface IPtSubroutine {
|
||||
val name: String
|
||||
val scopedName: String
|
||||
}
|
||||
|
||||
class PtAsmSub(
|
||||
name: String,
|
||||
val address: Address?,
|
||||
val clobbers: Set<CpuRegister>,
|
||||
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
|
||||
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
|
||||
val inline: Boolean,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine {
|
||||
|
||||
class Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt)
|
||||
}
|
||||
|
||||
|
||||
class PtSub(
|
||||
name: String,
|
||||
val parameters: List<PtSubroutineParameter>,
|
||||
val returns: List<DataType>,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||
init {
|
||||
// params and return values should not be str
|
||||
if(parameters.any{ !it.type.isNumericOrBool })
|
||||
throw AssemblyError("non-numeric/non-bool parameter")
|
||||
if(returns.any { !it.isNumericOrBool })
|
||||
throw AssemblyError("non-numeric/non-bool returntype")
|
||||
parameters.forEach { it.parent=this }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtSubroutineParameter(name: String, val type: DataType, val register: RegisterOrPair?, position: Position): PtNamedNode(name, position)
|
||||
|
||||
|
||||
sealed interface IPtAssignment {
|
||||
val children: MutableList<PtNode>
|
||||
val target: PtAssignTarget
|
||||
get() {
|
||||
if(children.size==2)
|
||||
return children[0] as PtAssignTarget
|
||||
else if(children.size<2)
|
||||
throw AssemblyError("incomplete node")
|
||||
else
|
||||
throw AssemblyError("no singular target")
|
||||
}
|
||||
val value: PtExpression
|
||||
get() = children.last() as PtExpression
|
||||
val multiTarget: Boolean
|
||||
get() = children.size>2
|
||||
}
|
||||
|
||||
class PtAssignment(position: Position, val isVarInitializer: Boolean=false) : PtNode(position), IPtAssignment
|
||||
|
||||
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
|
||||
|
||||
|
||||
class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) {
|
||||
val identifier: PtIdentifier?
|
||||
get() = children.single() as? PtIdentifier
|
||||
val array: PtArrayIndexer?
|
||||
get() = children.single() as? PtArrayIndexer
|
||||
val memory: PtMemoryByte?
|
||||
get() = children.single() as? PtMemoryByte
|
||||
|
||||
val type: DataType
|
||||
get() {
|
||||
return when(val tgt = children.single()) {
|
||||
is PtIdentifier -> tgt.type
|
||||
is PtArrayIndexer -> tgt.type
|
||||
is PtMemoryByte -> tgt.type
|
||||
else -> throw AssemblyError("weird target $tgt")
|
||||
}
|
||||
}
|
||||
|
||||
infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this)
|
||||
}
|
||||
|
||||
|
||||
class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) {
|
||||
val trueScope: PtNodeGroup
|
||||
get() = children[0] as PtNodeGroup
|
||||
val falseScope: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtForLoop(position: Position) : PtNode(position) {
|
||||
val variable: PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
val iterable: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
val statements: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtIfElse(position: Position) : PtNode(position) {
|
||||
val condition: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val ifScope: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
val elseScope: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
|
||||
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
|
||||
}
|
||||
|
||||
|
||||
class PtJump(position: Position) : PtNode(position) {
|
||||
val target: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
}
|
||||
|
||||
|
||||
class PtRepeatLoop(position: Position) : PtNode(position) {
|
||||
val count: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val statements: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtReturn(position: Position) : PtNode(position) // children are all expressions
|
||||
|
||||
|
||||
sealed interface IPtVariable {
|
||||
val name: String
|
||||
val type: DataType
|
||||
}
|
||||
|
||||
|
||||
class PtVariable(
|
||||
name: String,
|
||||
override val type: DataType,
|
||||
val zeropage: ZeropageWish,
|
||||
val align: UInt,
|
||||
val value: PtExpression?,
|
||||
val arraySize: UInt?,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
|
||||
if(value!=null) {
|
||||
require(value is PtArray || value is PtString) { "variable initializer value must only be array or string" }
|
||||
// NOTE: the 6502 code generator expects numerical variables to not have an initialization value,
|
||||
// because that is done via assignment statements. There are no "inline" variables with a given value.
|
||||
// All variables are put into zeropage or into the BSS section and initialized afterwards during program
|
||||
// startup or at the start of the subroutine.
|
||||
// The IR codegen however is different it has a special section <VARIABLESWITHINIT> for all variables
|
||||
// that have a non-zero initialization value, regardless of the datatype. It removes the initialization
|
||||
// assignment and puts the value back into the variable (but only in the symboltable).
|
||||
}
|
||||
|
||||
value?.let {it.parent=this}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||
|
||||
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
require(!type.isString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
val value: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val choices: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
|
||||
val values: PtNodeGroup
|
||||
get() = children[0] as PtNodeGroup
|
||||
val statements: PtNodeGroup
|
||||
get() = children[1] as PtNodeGroup
|
||||
}
|
||||
|
||||
|
||||
class PtDefer(position: Position): PtNode(position), IPtStatementContainer
|
||||
|
||||
|
||||
class PtJmpTable(position: Position) : PtNode(position) // contains only PtIdentifier nodes
|
||||
@@ -1,16 +0,0 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.*
|
||||
|
||||
fun verifyFinalAstBeforeAsmGen(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
|
||||
/*
|
||||
walkAst(program) { node, _ ->
|
||||
if(node is PtVariable) {
|
||||
if(node.value!=null) {
|
||||
// require(node.type in ArrayDatatypes || node.type==DataType.STR) { "final check before asmgen: only string and array variables can still have an init value ${node.name} ${node.type} ${node.position}"}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -30,37 +30,48 @@ private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, Ba
|
||||
|
||||
|
||||
class FSignature(val pure: Boolean, // does it have side effects?
|
||||
val returnType: BaseDataType?,
|
||||
val returnTypes: Array<BaseDataType>,
|
||||
vararg val parameters: FParam) {
|
||||
|
||||
fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention {
|
||||
val returns: ReturnConvention = when (returnType) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
|
||||
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
|
||||
null -> ReturnConvention(null, null)
|
||||
else -> {
|
||||
// return type depends on arg type
|
||||
when (val paramType = actualParamTypes.first()) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||
else -> ReturnConvention(paramType, null)
|
||||
val returns: ReturnConvention
|
||||
if(returnTypes.isEmpty())
|
||||
returns = ReturnConvention(null, null)
|
||||
else if(returnTypes.size==1) {
|
||||
val returnType = returnTypes[0]
|
||||
returns = when (returnType) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
|
||||
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15)
|
||||
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(returnType, RegisterOrPair.AY)
|
||||
else -> {
|
||||
// return type depends on arg type
|
||||
when (val paramType = actualParamTypes.first()) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15)
|
||||
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||
else -> ReturnConvention(paramType, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// note: this path is currently never hit because the builtin functions with multiple return values have been handled in specialized optimized codegens already earlier.
|
||||
TODO("multiple return types from builtin function")
|
||||
}
|
||||
|
||||
return when {
|
||||
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||
actualParamTypes.size==1 -> {
|
||||
// One parameter goes via register/registerpair.
|
||||
// One parameter goes via register/registerpair (except longs)
|
||||
// this avoids repeated code for every caller to store the value in the subroutine's argument variable.
|
||||
// (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site).
|
||||
val paramConv = when(val paramType = actualParamTypes[0]) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
BaseDataType.LONG -> ParamConvention(paramType, null, true)
|
||||
BaseDataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
|
||||
in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ParamConvention(paramType, null, false)
|
||||
@@ -86,67 +97,102 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
}
|
||||
|
||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
|
||||
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
|
||||
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
|
||||
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
|
||||
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
||||
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
|
||||
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
|
||||
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
|
||||
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
|
||||
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
|
||||
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
||||
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"rsave" to FSignature(false, null),
|
||||
"rrestore" to FSignature(false, null),
|
||||
"memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
|
||||
"callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
|
||||
"callfar2" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
|
||||
"call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||
"setlsb" to FSignature(false, emptyArray(), FParam("variable", BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"setmsb" to FSignature(false, emptyArray(), FParam("variable", BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"rol" to FSignature(false, emptyArray(), FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"ror" to FSignature(false, emptyArray(), FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"rol2" to FSignature(false, emptyArray(), FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"ror2" to FSignature(false, emptyArray(), FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"cmp" to FSignature(false, emptyArray(), FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns result in the cpu status flags, but not asa proper return value
|
||||
"prog8_lib_stringcompare" to FSignature(true, arrayOf(BaseDataType.BYTE), FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||
"prog8_lib_square_byte" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"prog8_lib_square_word" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||
"prog8_lib_square_long" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("value", BaseDataType.LONG)),
|
||||
"prog8_lib_structalloc" to FSignature(true, arrayOf(BaseDataType.UWORD)),
|
||||
"prog8_lib_copylong" to FSignature(false, emptyArray(), FParam("pointer1", BaseDataType.UWORD), FParam("pointer2", BaseDataType.UWORD)),
|
||||
"prog8_lib_copyfloat" to FSignature(false, emptyArray(), FParam("pointer1", BaseDataType.UWORD), FParam("pointer2", BaseDataType.UWORD)),
|
||||
"abs" to FSignature(true, arrayOf(BaseDataType.UNDEFINED), FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("value", BaseDataType.WORD)),
|
||||
"abs__long" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("value", BaseDataType.LONG)),
|
||||
"abs__float" to FSignature(true, arrayOf(BaseDataType.FLOAT), FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
|
||||
"offsetof" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("field", BaseDataType.UBYTE)),
|
||||
"sgn" to FSignature(true, arrayOf(BaseDataType.BYTE), FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, arrayOf(BaseDataType.UNDEFINED), FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.UBYTE)),
|
||||
"sqrt__uword" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.UWORD)),
|
||||
"sqrt__long" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
|
||||
"sqrt__float" to FSignature(true, arrayOf(BaseDataType.FLOAT), FParam("value", BaseDataType.FLOAT)),
|
||||
"divmod" to FSignature(false, arrayOf(BaseDataType.UNDEFINED, BaseDataType.UNDEFINED), FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"divmod__ubyte" to FSignature(false, arrayOf(BaseDataType.UBYTE, BaseDataType.UBYTE), FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE)),
|
||||
"divmod__uword" to FSignature(false, arrayOf(BaseDataType.UWORD, BaseDataType.UWORD), FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD)),
|
||||
"divmod__byte" to FSignature(false, arrayOf(BaseDataType.BYTE, BaseDataType.BYTE), FParam("dividend", BaseDataType.BYTE), FParam("divisor", BaseDataType.BYTE)),
|
||||
"divmod__word" to FSignature(false, arrayOf(BaseDataType.WORD, BaseDataType.WORD), FParam("dividend", BaseDataType.WORD), FParam("divisor", BaseDataType.WORD)),
|
||||
"lmh" to FSignature(true, arrayOf(BaseDataType.UBYTE, BaseDataType.UBYTE, BaseDataType.UBYTE), FParam("value", BaseDataType.LONG)),
|
||||
"lsb" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"lsb__long" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.LONG)),
|
||||
"msb" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"msb__long" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.LONG)),
|
||||
"lsw" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"msw" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"mkword" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
||||
"mklong" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("msb", BaseDataType.UBYTE), FParam("b2", BaseDataType.UBYTE), FParam("b1", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
||||
"mklong2" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("msw", BaseDataType.UWORD), FParam("lsw", BaseDataType.UWORD)),
|
||||
"clamp" to FSignature(true, arrayOf(BaseDataType.UNDEFINED), FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__byte" to FSignature(true, arrayOf(BaseDataType.BYTE), FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__ubyte" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
|
||||
"clamp__word" to FSignature(true, arrayOf(BaseDataType.WORD), FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
|
||||
"clamp__uword" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
|
||||
"clamp__long" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("value", BaseDataType.LONG), FParam("minimum", BaseDataType.LONG), FParam("maximum", BaseDataType.LONG)),
|
||||
"min" to FSignature(true, arrayOf(BaseDataType.UNDEFINED), FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__byte" to FSignature(true, arrayOf(BaseDataType.BYTE), FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__ubyte" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"min__word" to FSignature(true, arrayOf(BaseDataType.WORD), FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"min__uword" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"min__long" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)),
|
||||
"max" to FSignature(true, arrayOf(BaseDataType.UNDEFINED), FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__byte" to FSignature(true, arrayOf(BaseDataType.BYTE), FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__ubyte" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"max__word" to FSignature(true, arrayOf(BaseDataType.WORD), FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"max__uword" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"max__long" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)),
|
||||
"peek" to FSignature(true, arrayOf(BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD)),
|
||||
"peekbool" to FSignature(true, arrayOf(BaseDataType.BOOL), FParam("address", BaseDataType.UWORD)),
|
||||
"peekw" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("address", BaseDataType.UWORD)),
|
||||
"peekl" to FSignature(true, arrayOf(BaseDataType.LONG), FParam("address", BaseDataType.UWORD)),
|
||||
"peekf" to FSignature(true, arrayOf(BaseDataType.FLOAT), FParam("address", BaseDataType.UWORD)),
|
||||
"poke" to FSignature(false, emptyArray(), FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE, BaseDataType.BYTE)),
|
||||
"pokebool" to FSignature(false, emptyArray(), FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokebowl" to FSignature(false, emptyArray(), FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
|
||||
"pokew" to FSignature(false, emptyArray(), FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD, BaseDataType.WORD)),
|
||||
"pokel" to FSignature(false, emptyArray(), FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
|
||||
"pokef" to FSignature(false, emptyArray(), FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
||||
"pokemon" to FSignature(false, arrayOf(BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
||||
"rsave" to FSignature(false, emptyArray()),
|
||||
"rrestore" to FSignature(false, emptyArray()),
|
||||
"memory" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
|
||||
"memory__ref" to FSignature(true, arrayOf(BaseDataType.UWORD), FParam("name", BaseDataType.STR)),
|
||||
"callfar" to FSignature(false, arrayOf(BaseDataType.UWORD), FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
|
||||
"callfar2" to FSignature(false, arrayOf(BaseDataType.UWORD), FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
|
||||
"call" to FSignature(false, arrayOf(BaseDataType.UWORD), FParam("address", BaseDataType.UWORD)),
|
||||
"push" to FSignature(false, emptyArray(), FParam("value", BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)),
|
||||
"pushw" to FSignature(false, emptyArray(), FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.POINTER)),
|
||||
"pushl" to FSignature(false, emptyArray(), FParam("value", BaseDataType.LONG)),
|
||||
"pushf" to FSignature(false, emptyArray(), FParam("value", BaseDataType.FLOAT)),
|
||||
"pop" to FSignature(false, arrayOf(BaseDataType.UBYTE)),
|
||||
"popw" to FSignature(false, arrayOf(BaseDataType.UWORD)),
|
||||
"popl" to FSignature(false, arrayOf(BaseDataType.LONG)),
|
||||
"popf" to FSignature(false, arrayOf(BaseDataType.FLOAT)),
|
||||
)
|
||||
|
||||
val InplaceModifyingBuiltinFunctions = setOf(
|
||||
"setlsb", "setmsb",
|
||||
"rol", "ror", "rol2", "ror2",
|
||||
"divmod", "divmod__ubyte", "divmod__uword"
|
||||
"rol", "ror", "rol2", "ror2"
|
||||
)
|
||||
|
||||
val SimpleBuiltinFunctions = setOf(
|
||||
"msb", "lsb", "msw", "lsw",
|
||||
"mkword", "mklong", "mklong2",
|
||||
"set_carry", "set_irqd", "clear_carry", "clear_irqd")
|
||||
@@ -11,26 +11,29 @@ class CompilationOptions(val output: OutputType,
|
||||
val zpAllowed: List<UIntRange>,
|
||||
val floats: Boolean,
|
||||
val noSysInit: Boolean,
|
||||
val romable: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
val compilerVersion: String,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var memtopAddress: UInt,
|
||||
var warnSymbolShadowing: Boolean = false,
|
||||
var warnImplicitTypeCast: Boolean = false,
|
||||
var optimize: Boolean = false,
|
||||
var asmQuiet: Boolean = false,
|
||||
var asmListfile: Boolean = false,
|
||||
var includeSourcelines: Boolean = false,
|
||||
var dumpVariables: Boolean = false,
|
||||
var dumpSymbols: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
var varsGolden: Boolean = false,
|
||||
var slabsHighBank: Int? = null,
|
||||
var slabsGolden: Boolean = false,
|
||||
var dontSplitWordArrays: Boolean = false,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var ignoreFootguns: Boolean = false,
|
||||
var outputDir: Path = Path(""),
|
||||
var quiet: Boolean = false,
|
||||
var profilingInstrumentation: Boolean = false,
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
init {
|
||||
@@ -39,5 +42,79 @@ class CompilationOptions(val output: OutputType,
|
||||
|
||||
companion object {
|
||||
val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u)
|
||||
|
||||
fun builder(compTarget: ICompilationTarget) = Builder(compTarget)
|
||||
|
||||
class Builder(private val compTarget: ICompilationTarget) {
|
||||
private var output: OutputType = compTarget.defaultOutputType
|
||||
private var launcher: CbmPrgLauncherType = CbmPrgLauncherType.NONE
|
||||
private var zeropage: ZeropageType = ZeropageType.DONTUSE
|
||||
private var zpReserved: List<UIntRange> = emptyList()
|
||||
private var zpAllowed: List<UIntRange> = AllZeropageAllowed
|
||||
private var floats: Boolean = false
|
||||
private var noSysInit: Boolean = false
|
||||
private var romable: Boolean = false
|
||||
private var compilerVersion: String = "unknown"
|
||||
private var loadAddress: UInt = compTarget.PROGRAM_LOAD_ADDRESS
|
||||
private var memtopAddress: UInt = compTarget.PROGRAM_MEMTOP_ADDRESS
|
||||
private var warnSymbolShadowing: Boolean = false
|
||||
private var warnImplicitTypeCast: Boolean = false
|
||||
private var optimize: Boolean = false
|
||||
private var asmQuiet: Boolean = false
|
||||
private var asmListfile: Boolean = false
|
||||
private var includeSourcelines: Boolean = false
|
||||
private var dumpVariables: Boolean = false
|
||||
private var dumpSymbols: Boolean = false
|
||||
private var varsHighBank: Int? = null
|
||||
private var varsGolden: Boolean = false
|
||||
private var slabsHighBank: Int? = null
|
||||
private var slabsGolden: Boolean = false
|
||||
private var breakpointCpuInstruction: String? = null
|
||||
private var ignoreFootguns: Boolean = false
|
||||
private var outputDir: Path = Path("")
|
||||
private var quiet: Boolean = false
|
||||
private var profilingInstrumentation: Boolean = false
|
||||
private var symbolDefs: Map<String, String> = emptyMap()
|
||||
|
||||
fun output(output: OutputType) = apply { this.output = output }
|
||||
fun launcher(launcher: CbmPrgLauncherType) = apply { this.launcher = launcher }
|
||||
fun zeropage(zeropage: ZeropageType) = apply { this.zeropage = zeropage }
|
||||
fun zpReserved(zpReserved: List<UIntRange>) = apply { this.zpReserved = zpReserved }
|
||||
fun zpAllowed(zpAllowed: List<UIntRange>) = apply { this.zpAllowed = zpAllowed }
|
||||
fun floats(floats: Boolean) = apply { this.floats = floats }
|
||||
fun noSysInit(noSysInit: Boolean) = apply { this.noSysInit = noSysInit }
|
||||
fun romable(romable: Boolean) = apply { this.romable = romable }
|
||||
fun compilerVersion(compilerVersion: String) = apply { this.compilerVersion = compilerVersion }
|
||||
fun loadAddress(loadAddress: UInt) = apply { this.loadAddress = loadAddress }
|
||||
fun memtopAddress(memtopAddress: UInt) = apply { this.memtopAddress = memtopAddress }
|
||||
fun warnSymbolShadowing(warnSymbolShadowing: Boolean) = apply { this.warnSymbolShadowing = warnSymbolShadowing }
|
||||
fun warnImplicitTypeCast(warnImplicitTypeCast: Boolean) = apply { this.warnImplicitTypeCast = warnImplicitTypeCast }
|
||||
fun optimize(optimize: Boolean) = apply { this.optimize = optimize }
|
||||
fun asmQuiet(asmQuiet: Boolean) = apply { this.asmQuiet = asmQuiet }
|
||||
fun asmListfile(asmListfile: Boolean) = apply { this.asmListfile = asmListfile }
|
||||
fun includeSourcelines(includeSourcelines: Boolean) = apply { this.includeSourcelines = includeSourcelines }
|
||||
fun dumpVariables(dumpVariables: Boolean) = apply { this.dumpVariables = dumpVariables }
|
||||
fun dumpSymbols(dumpSymbols: Boolean) = apply { this.dumpSymbols = dumpSymbols }
|
||||
fun varsHighBank(varsHighBank: Int?) = apply { this.varsHighBank = varsHighBank }
|
||||
fun varsGolden(varsGolden: Boolean) = apply { this.varsGolden = varsGolden }
|
||||
fun slabsHighBank(slabsHighBank: Int?) = apply { this.slabsHighBank = slabsHighBank }
|
||||
fun slabsGolden(slabsGolden: Boolean) = apply { this.slabsGolden = slabsGolden }
|
||||
fun breakpointCpuInstruction(breakpointCpuInstruction: String?) = apply { this.breakpointCpuInstruction = breakpointCpuInstruction }
|
||||
fun ignoreFootguns(ignoreFootguns: Boolean) = apply { this.ignoreFootguns = ignoreFootguns }
|
||||
fun outputDir(outputDir: Path) = apply { this.outputDir = outputDir }
|
||||
fun quiet(quiet: Boolean) = apply { this.quiet = quiet }
|
||||
fun profilingInstrumentation(profilingInstrumentation: Boolean) = apply { this.profilingInstrumentation = profilingInstrumentation }
|
||||
fun symbolDefs(symbolDefs: Map<String, String>) = apply { this.symbolDefs = symbolDefs }
|
||||
|
||||
fun build(): CompilationOptions {
|
||||
return CompilationOptions(
|
||||
output, launcher, zeropage, zpReserved, zpAllowed, floats, noSysInit, romable, compTarget, compilerVersion,
|
||||
loadAddress, memtopAddress, warnSymbolShadowing, warnImplicitTypeCast, optimize, asmQuiet, asmListfile,
|
||||
includeSourcelines, dumpVariables, dumpSymbols, varsHighBank, varsGolden,
|
||||
slabsHighBank, slabsGolden, breakpointCpuInstruction, ignoreFootguns, outputDir, quiet,
|
||||
profilingInstrumentation, symbolDefs
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,12 @@ fun Number.toHex(): String {
|
||||
// 256..65536 -> "$0100".."$ffff"
|
||||
// larger -> "$12345678"
|
||||
// negative values are prefixed with '-'.
|
||||
val integer = this.toInt()
|
||||
if(integer<0)
|
||||
val integer = this.toLong()
|
||||
if(integer<0) {
|
||||
if(integer==-2147483648L)
|
||||
return "$80000000" // the exception to the rule, because -$80000000 is not a valid hex number
|
||||
return '-' + abs(integer).toHex()
|
||||
}
|
||||
return when (integer) {
|
||||
in 0 until 16 -> integer.toString()
|
||||
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
||||
@@ -95,3 +98,24 @@ fun String.unescape(): String {
|
||||
}
|
||||
return result.joinToString("")
|
||||
}
|
||||
|
||||
// === Symbol name prefixing ===
|
||||
// These are used by code generators to add p8b_/p8s_/p8v_/etc. prefixes to symbol names.
|
||||
// The prefix character depends on the symbol type (block='b', sub='s', variable='v', etc.)
|
||||
// Scoped names (containing dots) have each part prefixed according to its role.
|
||||
|
||||
fun prefixScopedName(name: String, type: Char): String {
|
||||
val generatedPrefix = "p8_label_gen_"
|
||||
if('.' !in name) {
|
||||
if(name.startsWith(generatedPrefix))
|
||||
return name
|
||||
return "p8${type}_$name"
|
||||
}
|
||||
val parts = name.split('.')
|
||||
val firstPrefixed = "p8b_${parts[0]}"
|
||||
val lastPart = parts.last()
|
||||
val lastPrefixed = if(lastPart.startsWith(generatedPrefix)) lastPart else "p8${type}_$lastPart"
|
||||
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
|
||||
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
|
||||
return prefixed.joinToString(".")
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* Base data types supported by the Prog8 compiler.
|
||||
* These represent the fundamental types that can be used in Prog8 programs.
|
||||
*/
|
||||
enum class BaseDataType {
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
BYTE, // pass by value 8 bits signed
|
||||
UWORD, // pass by value 16 bits unsigned
|
||||
WORD, // pass by value 16 bits signed
|
||||
LONG, // pass by value 32 bits signed
|
||||
FLOAT, // pass by value machine dependent
|
||||
BOOL, // pass by value bit 0 of an 8-bit byte
|
||||
STR, // pass by reference
|
||||
ARRAY, // pass by reference, subtype is the element type
|
||||
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||
POINTER, // typed pointer, subtype is whatever type is pointed to
|
||||
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
|
||||
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
|
||||
UNDEFINED;
|
||||
|
||||
|
||||
fun largerSizeThan(other: BaseDataType) =
|
||||
when {
|
||||
this == other -> false
|
||||
this.isByteOrBool -> false
|
||||
this.isWord -> other.isByteOrBool
|
||||
this == LONG -> other.isByteOrBool || other.isWord
|
||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||
this.isArray && other.isArray -> false
|
||||
this.isArray -> other.isByteOrBool
|
||||
this == STR -> other.isByteOrBool
|
||||
this.isPointer -> other.isByteOrBool
|
||||
else -> true
|
||||
}
|
||||
|
||||
fun equalsSize(other: BaseDataType) =
|
||||
when {
|
||||
this == other -> true
|
||||
this.isArray && other.isArray -> true
|
||||
this.isByteOrBool -> other.isByteOrBool
|
||||
this.isWord -> other.isWord || other.isPointer
|
||||
this.isPointer -> other.isWord
|
||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
this == STR && other.isArray -> true
|
||||
this.isArray && other == STR -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// BaseDataType Extension Properties
|
||||
// ============================================================================
|
||||
|
||||
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
||||
val BaseDataType.isUnsignedByte get() = this == BaseDataType.UBYTE
|
||||
val BaseDataType.isSignedByte get() = this == BaseDataType.BYTE
|
||||
val BaseDataType.isBool get() = this == BaseDataType.BOOL
|
||||
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
|
||||
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
|
||||
val BaseDataType.isUnsignedWord get() = this == BaseDataType.UWORD
|
||||
val BaseDataType.isSignedWord get() = this == BaseDataType.WORD
|
||||
val BaseDataType.isLong get() = this == BaseDataType.LONG
|
||||
val BaseDataType.isFloat get() = this == BaseDataType.FLOAT
|
||||
val BaseDataType.isInteger get() = this in setOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
||||
val BaseDataType.isIntegerOrBool get() = this in setOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
|
||||
val BaseDataType.isWordOrByteOrBool get() = this in setOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.BOOL)
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in setOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
val BaseDataType.isUnsigned get() = !this.isSigned
|
||||
val BaseDataType.isSignedInteger get() = this.isSigned && this.isInteger
|
||||
val BaseDataType.isUnsignedInteger get() = this.isUnsigned && this.isInteger
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
|
||||
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
|
||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER // pointer arrays are also always stored as split uwords
|
||||
val BaseDataType.isIterable get() = this in setOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
|
||||
|
||||
|
||||
/**
|
||||
* Interface for types that can be used as subtypes in DataType.
|
||||
* Primarily used for struct types.
|
||||
*/
|
||||
interface ISubType {
|
||||
val scopedNameString: String
|
||||
fun memsize(sizer: IMemSizer): Int
|
||||
fun sameas(other: ISubType): Boolean
|
||||
fun getFieldType(name: String): DataType?
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a complete data type in Prog8, including base type and optional subtype.
|
||||
*
|
||||
* DataType is immutable after construction. Use the companion object factory methods
|
||||
* to create instances.
|
||||
*
|
||||
* @property base The base data type (e.g., UBYTE, WORD, ARRAY, POINTER)
|
||||
* @property sub The subtype for arrays and strings (e.g., UBYTE for byte arrays)
|
||||
* @property subType The structured subtype for pointers and struct instances
|
||||
* @property subTypeFromAntlr Deferred subtype resolution from parser
|
||||
*/
|
||||
class DataType private constructor(
|
||||
val base: BaseDataType,
|
||||
val sub: BaseDataType?,
|
||||
var subType: ISubType?,
|
||||
var subTypeFromAntlr: List<String>? = null
|
||||
) {
|
||||
|
||||
init {
|
||||
when {
|
||||
base.isPointerArray -> {
|
||||
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
|
||||
}
|
||||
base.isArray -> {
|
||||
require(sub != null && subType==null && subTypeFromAntlr==null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"}
|
||||
else -> {
|
||||
require(sub == null || (subType == null && subTypeFromAntlr == null)) {
|
||||
"sub and subtype can't both be set"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DataType) return false
|
||||
return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(base, sub, subType)
|
||||
|
||||
fun setActualSubType(actualSubType: ISubType) {
|
||||
subType = actualSubType
|
||||
subTypeFromAntlr = null
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Companion Object - Factory Methods
|
||||
// ============================================================================
|
||||
|
||||
companion object {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType): DataType {
|
||||
if(dt.isStructInstance)
|
||||
TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
|
||||
return simpletypes.getValue(dt)
|
||||
}
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid basic element dt $elementDt")
|
||||
}
|
||||
}
|
||||
|
||||
fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
|
||||
fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
|
||||
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
|
||||
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
|
||||
|
||||
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
|
||||
fun pointer(dt: DataType): DataType = if(dt.isBasic)
|
||||
DataType(BaseDataType.POINTER, dt.base, null)
|
||||
else
|
||||
DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
|
||||
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType)
|
||||
fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
|
||||
fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
|
||||
fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// DataType Methods
|
||||
// ============================================================================
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||
else arrayFor(base, false)
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
when {
|
||||
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
|
||||
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
|
||||
else -> throw IllegalArgumentException("not an array")
|
||||
}
|
||||
|
||||
fun typeForAddressOf(msb: Boolean): DataType {
|
||||
if (isUndefined)
|
||||
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
|
||||
else {
|
||||
if (isBasic)
|
||||
return pointer(base)
|
||||
if (isString)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
if (isPointer)
|
||||
return UWORD
|
||||
if (isArray) {
|
||||
if (msb || isSplitWordArray)
|
||||
return pointer(BaseDataType.UBYTE)
|
||||
val elementDt = elementType()
|
||||
require(elementDt.isBasic)
|
||||
return pointer(elementDt)
|
||||
}
|
||||
if (subType != null)
|
||||
return pointer(this)
|
||||
return UWORD
|
||||
}
|
||||
}
|
||||
|
||||
fun dereference(): DataType {
|
||||
require(isPointer || isUnsignedWord) { "cannot dereference non-pointer type ${this}"}
|
||||
return when {
|
||||
isUnsignedWord -> forDt(BaseDataType.UBYTE)
|
||||
sub!=null -> forDt(sub)
|
||||
subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
|
||||
subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
|
||||
else -> throw IllegalArgumentException("cannot dereference this pointer type")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.BOOL -> "bool[]"
|
||||
BaseDataType.FLOAT -> "float[]"
|
||||
BaseDataType.BYTE -> "byte[]"
|
||||
BaseDataType.WORD -> "word[]"
|
||||
BaseDataType.UBYTE -> "ubyte[]"
|
||||
BaseDataType.UWORD -> "uword[]"
|
||||
BaseDataType.LONG -> "long[]"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
BaseDataType.WORD -> "word[] (split)"
|
||||
BaseDataType.UWORD -> "uword[] (split)"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}"
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
fun sourceString(): String = when (base) {
|
||||
BaseDataType.BOOL -> "bool"
|
||||
BaseDataType.UBYTE -> "ubyte"
|
||||
BaseDataType.BYTE -> "byte"
|
||||
BaseDataType.UWORD -> "uword"
|
||||
BaseDataType.WORD -> "word"
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}"
|
||||
subType!=null -> "^^${subType!!.scopedNameString}"
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}"
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> {
|
||||
when {
|
||||
sub!=null -> sub.name.lowercase()
|
||||
subType!=null -> subType!!.scopedNameString
|
||||
subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".")
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
when {
|
||||
sub!=null -> "^^${sub.name.lowercase()}["
|
||||
subType!=null -> "^^${subType!!.scopedNameString}["
|
||||
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}["
|
||||
else -> "????? ["
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
BaseDataType.UWORD -> "@nosplit uword["
|
||||
BaseDataType.BOOL -> "bool["
|
||||
BaseDataType.BYTE -> "byte["
|
||||
BaseDataType.WORD -> "@nosplit word["
|
||||
BaseDataType.LONG -> "long["
|
||||
BaseDataType.FLOAT -> "float["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
BaseDataType.UWORD -> "uword["
|
||||
BaseDataType.WORD -> "word["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this type is assignable to the given target type (perhaps via a typecast)
|
||||
* without loss of precision.
|
||||
*/
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
when(base) {
|
||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||
BaseDataType.UBYTE -> targetType.base in setOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.BYTE -> targetType.base in setOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in setOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
|
||||
BaseDataType.WORD -> targetType.base in setOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.LONG -> targetType.base in setOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in setOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in setOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.POINTER -> {
|
||||
when {
|
||||
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
|
||||
targetType.isPointer -> this.isUnsignedWord || this == targetType
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
|
||||
BaseDataType.ARRAY_POINTER -> false
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
/**
|
||||
* Returns the memory size in bytes.
|
||||
* Note: for pointer types, size() doesn't return the size of the pointer itself
|
||||
* but the size of the thing it points to.
|
||||
*/
|
||||
fun size(memsizer: IMemSizer): Int = if(sub!=null) {
|
||||
memsizer.memorySize(sub)
|
||||
} else if(subType!=null) {
|
||||
subType!!.memsize(memsizer)
|
||||
} else {
|
||||
memsizer.memorySize(base)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DataType Properties
|
||||
// ============================================================================
|
||||
|
||||
val isBasic = sub==null && subType==null && subTypeFromAntlr==null
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
val isSignedByte = base == BaseDataType.BYTE
|
||||
val isByteOrBool = base.isByteOrBool
|
||||
val isWord = base.isWord
|
||||
val isUnsignedWord = base == BaseDataType.UWORD
|
||||
val isSignedWord = base == BaseDataType.WORD
|
||||
val isInteger = base.isInteger
|
||||
val isWordOrByteOrBool = base.isWordOrByteOrBool
|
||||
val isIntegerOrBool = base.isIntegerOrBool
|
||||
val isNumeric = base.isNumeric
|
||||
val isNumericOrBool = base.isNumericOrBool
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isSignedInteger = isSigned && isInteger
|
||||
val isUnsignedInteger = isUnsigned && isInteger
|
||||
val isArray = base.isArray
|
||||
val isPointer = base.isPointer
|
||||
val isPointerToByte = base.isPointer && sub?.isByteOrBool==true
|
||||
val isPointerToWord = base.isPointer && sub?.isWord==true
|
||||
val isStructInstance = base.isStructInstance
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isLongArray = base.isArray && sub == BaseDataType.LONG
|
||||
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
val isFloat = base == BaseDataType.FLOAT
|
||||
val isLong = base == BaseDataType.LONG
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||
val isSplitWordArray = base.isSplitWordArray
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
}
|
||||
@@ -1,229 +1,5 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.util.Objects
|
||||
|
||||
enum class BaseDataType {
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
BYTE, // pass by value 8 bits signed
|
||||
UWORD, // pass by value 16 bits unsigned
|
||||
WORD, // pass by value 16 bits signed
|
||||
LONG, // pass by value 32 bits signed
|
||||
FLOAT, // pass by value machine dependent
|
||||
BOOL, // pass by value bit 0 of an 8-bit byte
|
||||
STR, // pass by reference
|
||||
ARRAY, // pass by reference, subtype is the element type
|
||||
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||
UNDEFINED;
|
||||
|
||||
|
||||
fun largerSizeThan(other: BaseDataType) =
|
||||
when {
|
||||
this == other -> false
|
||||
this.isByteOrBool -> false
|
||||
this.isWord -> other.isByteOrBool
|
||||
this == LONG -> other.isByteOrBool || other.isWord
|
||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||
this.isArray && other.isArray -> false
|
||||
this.isArray -> other != FLOAT
|
||||
this == STR -> other != FLOAT
|
||||
else -> true
|
||||
}
|
||||
|
||||
fun equalsSize(other: BaseDataType) =
|
||||
when {
|
||||
this == other -> true
|
||||
this.isArray && other.isArray -> true
|
||||
this.isByteOrBool -> other.isByteOrBool
|
||||
this.isWord -> other.isWord
|
||||
this == STR && other== UWORD || this== UWORD && other== STR -> true
|
||||
this == STR && other.isArray -> true
|
||||
this.isArray && other == STR -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
||||
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
|
||||
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
|
||||
val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
||||
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||
|
||||
init {
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
else if(base==BaseDataType.STR)
|
||||
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
else
|
||||
require(sub == null) { "only string and array base types can have a subtype"}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DataType) return false
|
||||
return base == other.base && sub == other.sub
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||
|
||||
companion object {
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
else
|
||||
throw NoSuchElementException("invalid element dt $elementDt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
||||
else arrayFor(base, false)
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
|
||||
override fun toString(): String = when(base) {
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.BOOL -> "bool[]"
|
||||
BaseDataType.FLOAT -> "float[]"
|
||||
BaseDataType.BYTE -> "byte[]"
|
||||
BaseDataType.WORD -> "word[]"
|
||||
BaseDataType.UBYTE -> "ubyte[]"
|
||||
BaseDataType.UWORD -> "uword[]"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
BaseDataType.WORD -> "word[] (split)"
|
||||
BaseDataType.UWORD -> "uword[] (split)"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
fun sourceString(): String = when (base) {
|
||||
BaseDataType.BOOL -> "bool"
|
||||
BaseDataType.UBYTE -> "ubyte"
|
||||
BaseDataType.BYTE -> "byte"
|
||||
BaseDataType.UWORD -> "uword"
|
||||
BaseDataType.WORD -> "word"
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
BaseDataType.UWORD -> "@nosplit uword["
|
||||
BaseDataType.BOOL -> "bool["
|
||||
BaseDataType.BYTE -> "byte["
|
||||
BaseDataType.WORD -> "@nosplit word["
|
||||
BaseDataType.FLOAT -> "float["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_SPLITW -> {
|
||||
when(sub) {
|
||||
BaseDataType.UWORD -> "uword["
|
||||
BaseDataType.WORD -> "word["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
|
||||
}
|
||||
|
||||
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
when(base) {
|
||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
||||
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
val isSignedByte = base == BaseDataType.BYTE
|
||||
val isByteOrBool = base.isByteOrBool
|
||||
val isWord = base.isWord
|
||||
val isUnsignedWord = base == BaseDataType.UWORD
|
||||
val isSignedWord = base == BaseDataType.WORD
|
||||
val isInteger = base.isInteger
|
||||
val isIntegerOrBool = base.isIntegerOrBool
|
||||
val isNumeric = base.isNumeric
|
||||
val isNumericOrBool = base.isNumericOrBool
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
|
||||
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
|
||||
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
val isFloat = base == BaseDataType.FLOAT
|
||||
val isLong = base == BaseDataType.LONG
|
||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
||||
val isSplitWordArray = base.isSplitWordArray
|
||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
|
||||
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
||||
val isIterable = base.isIterable
|
||||
val isPassByRef = base.isPassByRef
|
||||
val isPassByValue = base.isPassByValue
|
||||
}
|
||||
|
||||
|
||||
enum class CpuRegister {
|
||||
A,
|
||||
@@ -242,10 +18,12 @@ enum class RegisterOrPair {
|
||||
FAC2,
|
||||
// cx16 virtual registers:
|
||||
R0, R1, R2, R3, R4, R5, R6, R7,
|
||||
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
R8, R9, R10, R11, R12, R13, R14, R15,
|
||||
// combined virtual registers to store 32 bits longs:
|
||||
R0R1, R2R3, R4R5, R6R7, R8R9, R10R11, R12R13, R14R15;
|
||||
|
||||
companion object {
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
val names: Set<String> = entries.map { it.toString() }.toSet()
|
||||
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
|
||||
return when(cpu) {
|
||||
CpuRegister.A -> A
|
||||
@@ -255,6 +33,22 @@ enum class RegisterOrPair {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the starting virtual register name for the current 32-bit combined virtual register
|
||||
* @return The starting register name as a string, WITHOUT THE cx16 block scope prefix!
|
||||
*/
|
||||
fun startregname() = when(this) {
|
||||
R0R1 -> "r0"
|
||||
R2R3 -> "r2"
|
||||
R4R5 -> "r4"
|
||||
R6R7 -> "r6"
|
||||
R8R9 -> "r8"
|
||||
R10R11 -> "r10"
|
||||
R12R13 -> "r12"
|
||||
R14R15 -> "r14"
|
||||
else -> throw IllegalArgumentException("must be a combined virtual register $this")
|
||||
}
|
||||
|
||||
fun asCpuRegister(): CpuRegister = when(this) {
|
||||
A -> CpuRegister.A
|
||||
X -> CpuRegister.X
|
||||
@@ -263,16 +57,21 @@ enum class RegisterOrPair {
|
||||
}
|
||||
|
||||
fun asScopedNameVirtualReg(type: DataType?): List<String> {
|
||||
require(this in Cx16VirtualRegisters)
|
||||
require(this in Cx16VirtualRegisters || this in CombinedLongRegisters)
|
||||
val suffix = when(type?.base) {
|
||||
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
||||
BaseDataType.UWORD, BaseDataType.POINTER, null -> ""
|
||||
BaseDataType.LONG -> "sl"
|
||||
else -> throw IllegalArgumentException("invalid register param type for cx16 virtual reg")
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
||||
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
|
||||
fun isLong() = this in CombinedLongRegisters
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
enum class Statusflag {
|
||||
@@ -282,7 +81,7 @@ enum class Statusflag {
|
||||
Pn; // don't use
|
||||
|
||||
companion object {
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
val names: Set<String> = entries.map { it.toString() }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,14 +100,25 @@ enum class BranchCondition {
|
||||
VC
|
||||
}
|
||||
|
||||
val Cx16VirtualRegisters = arrayOf(
|
||||
val Cx16VirtualRegisters = setOf(
|
||||
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
|
||||
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
|
||||
RegisterOrPair.R8, RegisterOrPair.R9, RegisterOrPair.R10, RegisterOrPair.R11,
|
||||
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
||||
)
|
||||
|
||||
val CpuRegisters = arrayOf(
|
||||
val CombinedLongRegisters = setOf(
|
||||
RegisterOrPair.R0R1,
|
||||
RegisterOrPair.R2R3,
|
||||
RegisterOrPair.R4R5,
|
||||
RegisterOrPair.R6R7,
|
||||
RegisterOrPair.R8R9,
|
||||
RegisterOrPair.R10R11,
|
||||
RegisterOrPair.R12R13,
|
||||
RegisterOrPair.R14R15
|
||||
)
|
||||
|
||||
val CpuRegisters = setOf(
|
||||
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
|
||||
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
|
||||
)
|
||||
@@ -343,6 +153,5 @@ enum class ZeropageWish {
|
||||
|
||||
enum class SplitWish {
|
||||
DONTCARE,
|
||||
SPLIT,
|
||||
NOSPLIT
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,48 @@ package prog8.code.core
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65C02,
|
||||
VIRTUAL
|
||||
/**
|
||||
* Identifies the target CPU type and its key characteristics that affect IR semantics.
|
||||
*
|
||||
* The most important property is [statusBitsOnMultiByteOps], which controls the IR
|
||||
* status-bit contract (see IRInstructions.kt for the full spec):
|
||||
*
|
||||
* - When `true`, the IR generator is allowed to skip emitting explicit CMP/CMPI before
|
||||
* branches that depend on the result of a previous "status-setting" instruction
|
||||
* (LOAD, INC, DEC, AND, OR, XOR, etc.). The contract is: "this instruction sets
|
||||
* Z and N based on the full multi-byte value." Backends for these targets must
|
||||
* honor this contract (e.g., M68000's `move.w #imm,d0` naturally sets Z based on
|
||||
* the 16-bit value, so the IR can rely on it).
|
||||
*
|
||||
* - When `false`, multi-byte operations on this target do NOT set status bits
|
||||
* correctly for the full value (e.g., 6502's `dec lo` only sets Z based on the
|
||||
* low byte of a 16-bit decrement). The IR generator must therefore always emit
|
||||
* an explicit CMP/CMPI before any branch that depends on the result. Backends
|
||||
* for these targets should NOT add `ora` postambles to set Z; instead they
|
||||
* rely on the explicit CMP/CMPI emitted by the IR generator.
|
||||
*
|
||||
* For 8-bit CPUs (6502, 65C02) the natural status flags from a multi-byte
|
||||
* cascade are misleading: Z is set based on the LAST byte operated on, not
|
||||
* the full value. So we treat the contract as "no instruction sets status
|
||||
* bits except CMP/CMPI/SEC/CLC/SGN/BITTST" and have the IR generator emit
|
||||
* explicit compares. This avoids the trap of "this works for BYTE but
|
||||
* silently breaks for WORD/LONG".
|
||||
*/
|
||||
enum class CpuType(val statusBitsOnMultiByteOps: Boolean) {
|
||||
/** 8-bit NMOS 6502. Multi-byte ops don't set Z based on full value. */
|
||||
CPU6502(false),
|
||||
|
||||
/** 8-bit CMOS 65C02. Same status-bit issue as plain 6502. */
|
||||
CPU65C02(false),
|
||||
|
||||
/**
|
||||
* Virtual machine target. We use `false` here so the IR generator always
|
||||
* emits explicit CMP/CMPI; the VM's setResultReg then does NOT set Z/N
|
||||
* flags (only CMP/CMPI/SEC/CLC/SGN/BITTST do). This matches the VM's
|
||||
* existing documentation (VirtualMachine.kt top comment) and keeps the
|
||||
* behavior consistent across all current targets.
|
||||
*/
|
||||
VIRTUAL(false)
|
||||
}
|
||||
|
||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
@@ -13,8 +51,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
|
||||
val FLOAT_MEM_SIZE: UInt
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val PROGRAM_MEMTOP_ADDRESS: UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
@@ -24,10 +61,12 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
|
||||
val cpu: CpuType
|
||||
var zeropage: Zeropage
|
||||
var golden: GoldenRam
|
||||
val libraryPath: Path?
|
||||
val customLauncher: List<String>
|
||||
val additionalAssemblerOptions: String?
|
||||
val additionalAssemblerOptions: List<String>
|
||||
val defaultOutputType: OutputType
|
||||
val defaultLauncherType: CbmPrgLauncherType
|
||||
val supportsBankedCalls: Boolean
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
@@ -35,7 +74,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
fun convertFloatToBytes(num: Double): List<UByte>
|
||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
|
||||
@@ -4,7 +4,7 @@ interface IErrorReporter {
|
||||
fun err(msg: String, position: Position)
|
||||
fun warn(msg: String, position: Position)
|
||||
fun info(msg: String, position: Position)
|
||||
fun undefined(symbol: List<String>, position: Position)
|
||||
fun undefined(symbol: List<String>, suggestImport: Boolean=false, position: Position)
|
||||
fun noErrors(): Boolean
|
||||
fun report()
|
||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
|
||||
@@ -13,4 +13,6 @@ interface IErrorReporter {
|
||||
}
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun printSingleError(errormessage: String)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ interface IMemSizer {
|
||||
|
||||
fun memorySize(dt: BaseDataType): Int {
|
||||
if(dt.isPassByRef)
|
||||
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size
|
||||
return memorySize(DataType.UWORD, null) // a pointer size
|
||||
try {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
} catch (x: NoSuchElementException) {
|
||||
|
||||
@@ -23,30 +23,26 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
|
||||
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
|
||||
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
|
||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word
|
||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word
|
||||
abstract val SCRATCH_PTR : UInt // temp storage for a pointer
|
||||
|
||||
|
||||
// the variables allocated into Zeropage.
|
||||
// Collections for zeropage allocation (single-threaded usage)
|
||||
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
||||
val allocatedVariables = mutableMapOf<String, VarAllocation>()
|
||||
|
||||
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
|
||||
val free = mutableSetOf<UInt>() // subclasses must set this to the appropriate free locations.
|
||||
|
||||
fun removeReservedFromFreePool() {
|
||||
synchronized(this) {
|
||||
for (reserved in options.zpReserved)
|
||||
reserve(reserved)
|
||||
for (reserved in options.zpReserved)
|
||||
reserve(reserved)
|
||||
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
|
||||
}
|
||||
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u))
|
||||
}
|
||||
|
||||
fun retainAllowed() {
|
||||
synchronized(this) {
|
||||
for(allowed in options.zpAllowed)
|
||||
free.retainAll { it in allowed }
|
||||
}
|
||||
for(allowed in options.zpAllowed)
|
||||
free.retainAll { it in allowed }
|
||||
}
|
||||
|
||||
fun availableBytes() = if(options.zeropage== ZeropageType.DONTUSE) 0 else free.size
|
||||
@@ -64,7 +60,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
position: Position?,
|
||||
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
||||
|
||||
require(name.isEmpty() || name !in allocatedVariables) {"name can't be allocated twice"}
|
||||
require(name.isEmpty() || !allocatedVariables.containsKey(name)) {"name can't be allocated twice"}
|
||||
|
||||
if(options.zeropage== ZeropageType.DONTUSE)
|
||||
return Err(MemAllocationError("zero page usage has been disabled"))
|
||||
@@ -72,6 +68,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
val size: Int =
|
||||
when {
|
||||
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isPointer -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isString || datatype.isArray -> {
|
||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||
if(position!=null)
|
||||
@@ -82,7 +79,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
}
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
|
||||
if(position!=null)
|
||||
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||
else
|
||||
@@ -93,19 +90,17 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
else -> throw MemAllocationError("weird dt")
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
if(free.isNotEmpty()) {
|
||||
if(size==1) {
|
||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||
if(oneSeparateByteFree(candidate))
|
||||
return Ok(VarAllocation(makeAllocation(candidate, 1, datatype, name), datatype,1))
|
||||
}
|
||||
return Ok(VarAllocation(makeAllocation(free[0], 1, datatype, name), datatype,1))
|
||||
}
|
||||
if(free.isNotEmpty()) {
|
||||
if(size==1) {
|
||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||
if (sequentialFree(candidate, size))
|
||||
return Ok(VarAllocation(makeAllocation(candidate, size, datatype, name), datatype, size))
|
||||
if(oneSeparateByteFree(candidate))
|
||||
return Ok(VarAllocation(makeAllocation(candidate, 1, datatype, name), datatype,1))
|
||||
}
|
||||
return Ok(VarAllocation(makeAllocation(free.first(), 1, datatype, name), datatype,1))
|
||||
}
|
||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
|
||||
if (sequentialFree(candidate, size))
|
||||
return Ok(VarAllocation(makeAllocation(candidate, size, datatype, name), datatype, size))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,49 +117,19 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
datatype.isString -> VarAllocation(address, datatype, size)
|
||||
datatype.isArray -> VarAllocation(address, datatype, size)
|
||||
datatype.isPointer -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
return address
|
||||
}
|
||||
|
||||
private fun oneSeparateByteFree(address: UInt) = address in free && address-1u !in free && address+1u !in free
|
||||
private fun oneSeparateByteFree(address: UInt) =
|
||||
address in free &&
|
||||
(address == 0u || address-1u !in free) &&
|
||||
address+1u !in free
|
||||
private fun sequentialFree(address: UInt, size: Int): Boolean {
|
||||
require(size>0)
|
||||
return free.containsAll((address until address+size.toUInt()).toList())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: this class is not yet used
|
||||
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
||||
private var nextLocation: UInt = region.first
|
||||
|
||||
override fun allocate(
|
||||
name: String,
|
||||
datatype: DataType,
|
||||
numElements: Int?,
|
||||
position: Position?,
|
||||
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
|
||||
|
||||
val size: Int =
|
||||
when {
|
||||
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isString -> numElements!!
|
||||
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||
}
|
||||
else -> throw MemAllocationError("weird dt")
|
||||
}
|
||||
|
||||
return if(nextLocation<=region.last && (region.last + 1u - nextLocation) >= size.toUInt()) {
|
||||
val result = Ok(VarAllocation(nextLocation, datatype, size))
|
||||
nextLocation += size.toUInt()
|
||||
result
|
||||
} else
|
||||
Err(MemAllocationError("no more free space in Golden RAM to allocate $size sequential bytes"))
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.code.core
|
||||
|
||||
val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation
|
||||
val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = arrayOf("and", "or", "xor", "not", "in")
|
||||
val BitwiseOperators = arrayOf("&", "|", "^", "~")
|
||||
val PrefixOperators = arrayOf("+", "-", "~", "not")
|
||||
val CommutativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not commutative because of Shortcircuit/McCarthy evaluation
|
||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not", "in")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||
|
||||
fun invertedComparisonOperator(operator: String) =
|
||||
when (operator) {
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.sanitize
|
||||
import prog8.code.source.SourceCode
|
||||
import java.nio.file.InvalidPathException
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||
/**
|
||||
* Source code position.
|
||||
*/
|
||||
data class Position(
|
||||
val file: String,
|
||||
val line: Int, // line number (1-based)
|
||||
val startCol: Int, // start column (1-based, tab-expanded)
|
||||
val endCol: Int // end column (1-based, tab-expanded)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "[$file: line $line col ${startCol}-${endCol}]"
|
||||
}
|
||||
|
||||
fun toClickableStr(): String {
|
||||
if(this===DUMMY)
|
||||
return ""
|
||||
if(SourceCode.isLibraryResource(file))
|
||||
return "$file:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
"file://$path:$line:$startCol:"
|
||||
val path = Path(file).sanitize().toUri().toString()
|
||||
"$path:$line:$startCol:"
|
||||
} catch(_: InvalidPathException) {
|
||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||
"file://$file:$line:$startCol:"
|
||||
@@ -24,4 +35,4 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
|
||||
companion object {
|
||||
val DUMMY = Position("~dummy~", 0, 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package prog8.code.optimize
|
||||
|
||||
import prog8.code.StExtSub
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
fun optimizeSimplifiedAst(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
|
||||
if (!options.optimize)
|
||||
return
|
||||
while (errors.noErrors() &&
|
||||
optimizeAssignTargets(program, st) + optimizeWordPlusTimesTwo(program, options) > 0) {
|
||||
// keep rolling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
|
||||
fun recurse(node: PtNode, depth: Int) {
|
||||
if(act(node, depth))
|
||||
node.children.forEach { recurse(it, depth+1) }
|
||||
}
|
||||
recurse(root, 0)
|
||||
}
|
||||
|
||||
|
||||
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable): Int {
|
||||
var changes = 0
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if(node is PtAssignment) {
|
||||
val value = node.value
|
||||
val functionName = when(value) {
|
||||
is PtBuiltinFunctionCall -> value.name
|
||||
is PtFunctionCall -> value.name
|
||||
else -> null
|
||||
}
|
||||
if(functionName!=null) {
|
||||
val stNode = st.lookup(functionName)
|
||||
if (stNode is StExtSub) {
|
||||
require(node.children.size==stNode.returns.size+1) {
|
||||
"number of targets must match return values"
|
||||
}
|
||||
node.children.zip(stNode.returns).withIndex().forEach { (index, xx) ->
|
||||
val target = xx.first as PtAssignTarget
|
||||
val returnedRegister = xx.second.register.registerOrPair
|
||||
if(returnedRegister!=null && !target.void && target.identifier!=null) {
|
||||
if(isSame(target.identifier!!, xx.second.type, returnedRegister)) {
|
||||
// output register is already identical to target register, so it can become void
|
||||
val voidTarget = PtAssignTarget(true, target.position)
|
||||
node.children[index] = voidTarget
|
||||
voidTarget.parent = node
|
||||
changes++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(node.children.dropLast(1).all { (it as PtAssignTarget).void }) {
|
||||
// all targets are now void, the whole assignment can be discarded and replaced by just a (void) call to the subroutine
|
||||
val index = node.parent.children.indexOf(node)
|
||||
val voidCall = PtFunctionCall(functionName, true, DataType.forDt(BaseDataType.UNDEFINED), value.position)
|
||||
value.children.forEach { voidCall.add(it) }
|
||||
node.parent.children[index] = voidCall
|
||||
voidCall.parent = node.parent
|
||||
changes++
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
|
||||
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
|
||||
if(returnedRegister in Cx16VirtualRegisters) {
|
||||
val regname = returnedRegister.name.lowercase()
|
||||
val identifierRegName = identifier.name.substringAfterLast('.')
|
||||
/*
|
||||
cx16.r? UWORD
|
||||
cx16.r?s WORD
|
||||
cx16.r?L UBYTE
|
||||
cx16.r?H UBYTE
|
||||
cx16.r?sL BYTE
|
||||
cx16.r?sH BYTE
|
||||
*/
|
||||
if(identifier.type.isByte && type.isByte) {
|
||||
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
|
||||
return identifierRegName.substring(2) in arrayOf("", "L", "sL") // note: not the -H (msb) variants!
|
||||
}
|
||||
}
|
||||
else if(identifier.type.isWord && type.isWord) {
|
||||
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
|
||||
return identifierRegName.substring(2) in arrayOf("", "s")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false // there are no identifiers directly corresponding to cpu registers
|
||||
}
|
||||
|
||||
|
||||
private fun optimizeWordPlusTimesTwo(program: PtProgram, options: CompilationOptions): Int {
|
||||
if(options.compTarget.name== VMTarget.NAME)
|
||||
return 0
|
||||
var changes = 0
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if (node is PtBinaryExpression) {
|
||||
if(node.operator=="*" && node.right.type.isWord && node.right.asConstValue()==2.0) {
|
||||
TODO("optimize word + byte*2 (usually already replaced by w+b<<2)")
|
||||
}
|
||||
else if(node.operator=="<<" && node.right.asConstValue()==1.0) {
|
||||
val typecast=node.left as? PtTypeCast
|
||||
if(typecast!=null && typecast.type.isWord && typecast.value is PtIdentifier) {
|
||||
val addition = node.parent as? PtBinaryExpression
|
||||
if(addition!=null && (addition.operator=="+" || addition.operator=="-") && addition.type.isWord) {
|
||||
// word + (byte<<1 as uword) (== word + byte*2) --> (word + (byte as word)) + (byte as word)
|
||||
val parent = addition.parent
|
||||
val index = parent.children.indexOf(addition)
|
||||
val addFirst = PtBinaryExpression(addition.operator, addition.type, addition.position)
|
||||
val addSecond = PtBinaryExpression(addition.operator, addition.type, addition.position)
|
||||
if(addition.left===node)
|
||||
addFirst.add(addition.right)
|
||||
else
|
||||
addFirst.add(addition.left)
|
||||
addFirst.add(typecast)
|
||||
addSecond.add(addFirst)
|
||||
addSecond.add(typecast.copy())
|
||||
parent.children[index] = addSecond
|
||||
addSecond.parent = parent
|
||||
changes++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
return changes
|
||||
}
|
||||
@@ -1,20 +1,22 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.sanitize
|
||||
import java.nio.file.Path
|
||||
import java.util.TreeMap
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// Resource caching "filesystem".
|
||||
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
|
||||
// Thread-safe: uses ConcurrentHashMap for all caches.
|
||||
|
||||
object ImportFileSystem {
|
||||
|
||||
val userHome: Path get() = Path.of(System.getProperty("user.home"))
|
||||
|
||||
fun expandTilde(path: String): String = if (path.startsWith("~")) {
|
||||
val userHome = System.getProperty("user.home")
|
||||
userHome + path.drop(1)
|
||||
userHome.toString() + path.drop(1)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
@@ -22,46 +24,46 @@ object ImportFileSystem {
|
||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||
|
||||
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||
val normalized = path.absolute().normalize()
|
||||
val cached = cache[normalized.toString()]
|
||||
if (cached != null)
|
||||
return cached
|
||||
val file = SourceCode.File(normalized, isLibrary)
|
||||
cache[normalized.toString()] = file
|
||||
return file
|
||||
val normalized = path.sanitize()
|
||||
return cache.computeIfAbsent(normalized.toString().lowercase()) {
|
||||
SourceCode.File(normalized, isLibrary)
|
||||
}
|
||||
}
|
||||
|
||||
fun getResource(name: String): SourceCode {
|
||||
val cached = cache[name]
|
||||
if (cached != null) return cached
|
||||
val resource = SourceCode.Resource(name)
|
||||
cache[name] = resource
|
||||
return resource
|
||||
return cache.computeIfAbsent(name.lowercase()) {
|
||||
SourceCode.Resource(name)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearCaches() {
|
||||
cache.clear()
|
||||
lineSpanCache.clear()
|
||||
}
|
||||
|
||||
fun retrieveSourceLine(position: Position): String {
|
||||
if(SourceCode.isLibraryResource(position.file)) {
|
||||
val cached = cache[SourceCode.withoutPrefix(position.file)]
|
||||
val key = SourceCode.withoutPrefix(position.file)
|
||||
val cached = cache[key.lowercase()]
|
||||
?: runCatching { getResource(key) }.getOrNull()
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
}
|
||||
val cached = cache[position.file]
|
||||
val cached = cache[position.file.lowercase()]
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
val path = Path(position.file).absolute().normalize()
|
||||
val cached2 = cache[path.toString()]
|
||||
val path = Path(position.file).sanitize()
|
||||
val cached2 = cache[path.toString().lowercase()]
|
||||
if(cached2 != null)
|
||||
return getLine(cached2, position.line)
|
||||
throw NoSuchElementException("cannot get source line $position, with path $path")
|
||||
}
|
||||
|
||||
private fun getLine(code: SourceCode, lineIndex: Int): String {
|
||||
var spans = lineSpanCache[code]
|
||||
if(spans==null) {
|
||||
val spans = lineSpanCache.computeIfAbsent(code) {
|
||||
val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first }
|
||||
val ends = lineSpans.drop(1) + code.text.length
|
||||
spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray()
|
||||
lineSpanCache[code] = spans
|
||||
lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray()
|
||||
}
|
||||
val span = spans[lineIndex - 1]
|
||||
return code.text.substring(span.start, span.end).trim()
|
||||
@@ -69,6 +71,6 @@ object ImportFileSystem {
|
||||
|
||||
private class LineSpan(val start: Int, val end: Int)
|
||||
|
||||
private val cache = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER)
|
||||
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
|
||||
}
|
||||
private val cache = ConcurrentHashMap<String, SourceCode>()
|
||||
private val lineSpanCache = ConcurrentHashMap<SourceCode, Array<LineSpan>>()
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.sanitize
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.text.Normalizer
|
||||
@@ -59,7 +60,7 @@ sealed class SourceCode {
|
||||
private const val LIBRARYFILEPREFIX = "library:"
|
||||
private const val STRINGSOURCEPREFIX = "string:"
|
||||
val curdir: Path = Path(".").absolute()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.absolute())
|
||||
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
|
||||
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||
|
||||
@@ -6,12 +6,18 @@ import prog8.code.target.zp.C128Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
class C128Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val supportsBankedCalls = true
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
override val defaultLauncherType = CbmPrgLauncherType.BASIC
|
||||
|
||||
companion object {
|
||||
const val NAME = "c128"
|
||||
@@ -22,18 +28,16 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE.toUInt()
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
override val BSSGOLDENRAM_START = 0u // TODO
|
||||
override val BSSGOLDENRAM_END = 0u // TODO
|
||||
override val BSSHIGHRAM_START = 0u // no high ram
|
||||
override val BSSHIGHRAM_END = 0u // no high ram
|
||||
override val BSSGOLDENRAM_START = 0x1300u
|
||||
override val BSSGOLDENRAM_END = 0x1bdfu // note: $1be0 - $1bff contains the 16 virtual registers
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
@@ -48,26 +52,29 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
if(!quiet)
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
|
||||
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu || address in 0xff00u..0xff04u
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C128Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,12 +7,18 @@ import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class C64Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val supportsBankedCalls = true
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
override val defaultLauncherType = CbmPrgLauncherType.BASIC
|
||||
|
||||
companion object {
|
||||
const val NAME = "c64"
|
||||
@@ -25,8 +31,7 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE.toUInt()
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
|
||||
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
|
||||
@@ -37,7 +42,6 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
@@ -52,18 +56,22 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
|
||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
@@ -79,7 +87,6 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,28 +15,31 @@ import kotlin.io.path.nameWithoutExtension
|
||||
|
||||
class ConfigFileTarget(
|
||||
override val name: String,
|
||||
override val supportsBankedCalls: Boolean,
|
||||
override val defaultEncoding: Encoding,
|
||||
override val cpu: CpuType,
|
||||
override val PROGRAM_LOAD_ADDRESS: UInt,
|
||||
override val PROGRAM_MEMTOP_ADDRESS: UInt,
|
||||
override val STARTUP_CODE_RESERVED_SIZE: UInt,
|
||||
override val BSSHIGHRAM_START: UInt,
|
||||
override val BSSHIGHRAM_END: UInt,
|
||||
override val BSSGOLDENRAM_START: UInt,
|
||||
override val BSSGOLDENRAM_END: UInt,
|
||||
override val defaultOutputType: OutputType,
|
||||
override val defaultLauncherType: CbmPrgLauncherType,
|
||||
override val libraryPath: Path,
|
||||
override val customLauncher: List<String>,
|
||||
override val additionalAssemblerOptions: String?,
|
||||
override val additionalAssemblerOptions: List<String>,
|
||||
val ioAddresses: List<UIntRange>,
|
||||
val zpScratchB1: UInt,
|
||||
val zpScratchReg: UInt,
|
||||
val zpScratchW1: UInt,
|
||||
val zpScratchW2: UInt,
|
||||
val zpScratchPtr: UInt,
|
||||
val virtualregistersStart: UInt,
|
||||
val zpFullsafe: List<UIntRange>,
|
||||
val zpKernalsafe: List<UIntRange>,
|
||||
val zpBasicsafe: List<UIntRange>
|
||||
): ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(8) {
|
||||
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -108,27 +111,37 @@ class ConfigFileTarget(
|
||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||
else emptyList()
|
||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||
val assemblerOptions = if(assemblerOptionsStr.isBlank()) null else assemblerOptionsStr
|
||||
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||
val launcherString = props.getProperty("launcher", "BASIC")
|
||||
val defaultLauncherType = try {
|
||||
CbmPrgLauncherType.valueOf(launcherString.uppercase())
|
||||
} catch (_: IllegalArgumentException) {
|
||||
CbmPrgLauncherType.BASIC
|
||||
}
|
||||
|
||||
return ConfigFileTarget(
|
||||
configfile.nameWithoutExtension,
|
||||
props.getProperty("supports_banked_calls", "false").toBoolean(),
|
||||
Encoding.entries.first { it.prefix==props.getString("encoding") },
|
||||
cpuType,
|
||||
props.getInteger("load_address"),
|
||||
props.getInteger("memtop"),
|
||||
0u, // used only in a very specific error condition check in a certain scenario...
|
||||
props.getInteger("bss_highram_start"),
|
||||
props.getInteger("bss_highram_end"),
|
||||
props.getInteger("bss_goldenram_start"),
|
||||
props.getInteger("bss_goldenram_end"),
|
||||
defaultOutputType,
|
||||
defaultLauncherType,
|
||||
libraryPath,
|
||||
customLauncher,
|
||||
assemblerOptions,
|
||||
if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "),
|
||||
ioAddresses,
|
||||
props.getInteger("zp_scratch_b1"),
|
||||
props.getInteger("zp_scratch_reg"),
|
||||
props.getInteger("zp_scratch_w1"),
|
||||
props.getInteger("zp_scratch_w2"),
|
||||
props.getInteger("zp_scratch_ptr"),
|
||||
props.getInteger("virtual_registers"),
|
||||
zpFullsafe,
|
||||
zpKernalsafe,
|
||||
@@ -140,15 +153,14 @@ class ConfigFileTarget(
|
||||
// TODO floats are not yet supported here, just enter some values
|
||||
override val FLOAT_MAX_POSITIVE = 9.999999999e97
|
||||
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
|
||||
override val FLOAT_MEM_SIZE = 8
|
||||
override val FLOAT_MEM_SIZE = 8u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam // TODO this is not yet used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||
}
|
||||
|
||||
@@ -156,13 +168,12 @@ class ConfigFileTarget(
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = ConfigurableZeropage(
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
|
||||
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr,
|
||||
virtualregistersStart,
|
||||
zpBasicsafe,
|
||||
zpKernalsafe,
|
||||
zpFullsafe,
|
||||
compilerOptions
|
||||
)
|
||||
// note: there's no golden ram yet
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,18 @@ import prog8.code.target.zp.CX16Zeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class Cx16Target: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val supportsBankedCalls = true
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
override val defaultLauncherType = CbmPrgLauncherType.BASIC
|
||||
|
||||
companion object {
|
||||
const val NAME = "cx16"
|
||||
@@ -22,8 +28,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE.toUInt()
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
|
||||
|
||||
@@ -33,7 +38,6 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
override val BSSGOLDENRAM_END = 0x07ffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
@@ -48,7 +52,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
val emulator: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
@@ -67,9 +71,13 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
}
|
||||
}
|
||||
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
@@ -79,7 +87,6 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
|
||||
// and https://en.wikipedia.org/wiki/IEEE_754-1985
|
||||
|
||||
val flt = num.toDouble()
|
||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||
if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE)
|
||||
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $flt")
|
||||
if (flt == 0.0)
|
||||
return zero
|
||||
|
||||
@@ -40,7 +40,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
|
||||
|
||||
return when {
|
||||
exponent < 0 -> zero // underflow, use zero instead
|
||||
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
||||
exponent > 255 -> throw InternalCompilerException("floating point overflow: $flt")
|
||||
exponent == 0 -> zero
|
||||
else -> {
|
||||
val mantLong = mantissa.toLong()
|
||||
|
||||
@@ -7,28 +7,33 @@ import prog8.code.core.IMemSizer
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.LONG -> numElements * 4
|
||||
BaseDataType.FLOAT-> numElements * floatsize
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
return numElements // treat it as the size of the given string with the length
|
||||
?: 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isPointer -> 2 // pointer is just a uword
|
||||
dt.isStructInstance -> dt.subType?.memsize(this) ?: 0
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,18 @@ import prog8.code.target.zp.PETZeropage
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
|
||||
class PETTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(true),
|
||||
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val supportsBankedCalls = false
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
override val defaultLauncherType = CbmPrgLauncherType.BASIC
|
||||
|
||||
companion object {
|
||||
const val NAME = "pet32"
|
||||
@@ -21,8 +27,7 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 20u
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE.toUInt()
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
|
||||
|
||||
@@ -32,7 +37,6 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
override val BSSGOLDENRAM_END = 0u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
@@ -47,17 +51,21 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting PET emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting PET emulator...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
@@ -66,7 +74,6 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = PETZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,16 +3,23 @@ package prog8.code.target
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.Encoder
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
class VMTarget: ICompilationTarget,
|
||||
IStringEncoding by Encoder(false),
|
||||
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
|
||||
|
||||
override val name = NAME
|
||||
override val supportsBankedCalls = false
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val customLauncher = emptyList<String>()
|
||||
override val additionalAssemblerOptions = emptyList<String>()
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
override val defaultLauncherType = CbmPrgLauncherType.BASIC
|
||||
|
||||
companion object {
|
||||
const val NAME = "virtual"
|
||||
@@ -21,10 +28,9 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE.toUInt()
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
|
||||
|
||||
@@ -33,13 +39,12 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
override val BSSGOLDENRAM_START = 0u // not actually used
|
||||
override val BSSGOLDENRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
override lateinit var golden: GoldenRam // not actually used
|
||||
|
||||
override fun getFloatAsmBytes(num: Number): String {
|
||||
// little endian binary representation
|
||||
val bits = num.toDouble().toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||
val parts = hexStr.chunked(2).map { "$$it" }
|
||||
return parts.joinToString(", ")
|
||||
}
|
||||
|
||||
@@ -63,59 +68,34 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
println("\nStarting Virtual Machine...")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
launchEmulatorWithTrace(programNameWithPath, quiet, traceEnabled = false)
|
||||
}
|
||||
|
||||
fun launchEmulatorWithTrace(programNameWithPath: Path, quiet: Boolean, traceEnabled: Boolean) {
|
||||
if(!quiet)
|
||||
println("\nStarting Virtual Machine...")
|
||||
|
||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText())
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText())
|
||||
else
|
||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||
}
|
||||
val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText(), quiet, traceEnabled)
|
||||
else
|
||||
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = VirtualZeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.FLOAT-> numElements * FLOAT_MEM_SIZE
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
}
|
||||
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(irSource: String)
|
||||
fun runProgram(irSource: String, quiet: Boolean, traceEnabled: Boolean = false)
|
||||
}
|
||||
|
||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
@@ -127,4 +107,6 @@ private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_W2: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
override val SCRATCH_PTR: UInt
|
||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||
}
|
||||
@@ -26,7 +26,7 @@ object AtasciiEncoding {
|
||||
'▖',
|
||||
|
||||
// $10
|
||||
'♣',
|
||||
'♣',
|
||||
'┌',
|
||||
'─',
|
||||
'┼',
|
||||
@@ -62,7 +62,7 @@ object AtasciiEncoding {
|
||||
'/',
|
||||
|
||||
// $30
|
||||
'0',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
@@ -80,7 +80,7 @@ object AtasciiEncoding {
|
||||
'?',
|
||||
|
||||
// $40
|
||||
'@',
|
||||
'@',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
@@ -197,6 +197,7 @@ object AtasciiEncoding {
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\r' -> 0x9bu
|
||||
'\u0000' -> 0u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
|
||||
@@ -285,6 +285,7 @@ object C64osEncoding {
|
||||
val screencode = encodingC64os[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 13u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
||||
@@ -5,19 +5,19 @@ import prog8.code.core.Encoding
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.InternalCompilerException
|
||||
|
||||
object Encoder: IStringEncoding {
|
||||
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
val coded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
|
||||
Encoding.ISO -> IsoEncoding.encode(str)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
|
||||
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.encode(str)
|
||||
Encoding.C64OS -> C64osEncoding.encode(str)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
@@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
|
||||
val decoded = when(encoding) {
|
||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
|
||||
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.CP437 -> Cp437Encoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
|
||||
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
|
||||
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
|
||||
Encoding.C64OS -> C64osEncoding.decode(bytes)
|
||||
else -> throw InternalCompilerException("unsupported encoding $encoding")
|
||||
}
|
||||
|
||||
@@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
|
||||
import java.io.CharConversionException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
|
||||
open class IsoEncodingBase(charsetName: String) {
|
||||
val charset: Charset = Charset.forName(charsetName)
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
@@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
||||
@@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
|
||||
object KatakanaEncoding {
|
||||
val charset: Charset = Charset.forName("JIS_X0201")
|
||||
|
||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
||||
return try {
|
||||
val mapped = str.map { chr ->
|
||||
when (chr) {
|
||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
||||
|
||||
'\u0000' -> 0u
|
||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
||||
@@ -112,9 +113,14 @@ object KatakanaEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
||||
return try {
|
||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||
Ok(String(bytes.map {
|
||||
when(it) {
|
||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
||||
else -> it.toByte()
|
||||
}
|
||||
}.toByteArray(), charset))
|
||||
} catch (ce: CharConversionException) {
|
||||
Err(ce)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.io.CharConversionException
|
||||
object PetsciiEncoding {
|
||||
|
||||
// decoding: from Petscii/Screencodes (0-255) to Unicode
|
||||
// character tables used from https://github.com/irmen/cbmcodecs2
|
||||
// character tables used from https://codeberg.org/irmen/cbmcodecs2.git
|
||||
|
||||
private val decodingPetsciiLowercase = charArrayOf(
|
||||
'\u0000', // 0x00 -> \u0000
|
||||
@@ -21,7 +21,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x07 -> UNDEFINED
|
||||
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\n', // 0x0A -> LINE FEED (RETURN)
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
@@ -1117,6 +1117,8 @@ object PetsciiEncoding {
|
||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||
return screencode?.toUByte() ?: when (chr) {
|
||||
'\u0000' -> 0u
|
||||
'\n' -> 141u
|
||||
'\r' -> 141u
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.code - 0x8000).toUByte()
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
// reference: "Mapping the C128" zeropage chapter.
|
||||
@@ -14,14 +11,9 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
override val SCRATCH_PTR = 0x0bu // temp storage for a pointer $0b+$0c
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
@@ -51,13 +43,13 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u
|
||||
))
|
||||
|
||||
// if(options.zeropage==ZeropageType.BASICSAFE) {
|
||||
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
|
||||
free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
|
||||
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
|
||||
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
|
||||
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
|
||||
// }
|
||||
// C128 doesn't support floating point calculations in Prog8, so we can use the FP zeropage locations
|
||||
// for general variables in both BASICSAFE and FLOATSAFE modes.
|
||||
// These addresses are normally used by the C128 BASIC floating point routines.
|
||||
free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
|
||||
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
|
||||
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
|
||||
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
@@ -69,6 +61,43 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.KERNALSAFE) {
|
||||
// C128 DESIGN: Virtual registers are ONLY allocated when sufficient zeropage space exists.
|
||||
// C128 has limited zeropage space in BASICSAFE/FLOATSAFE modes.
|
||||
// Virtual registers are only allocated in FULL and KERNALSAFE modes where space permits.
|
||||
allocateCx16VirtualRegisters()
|
||||
}
|
||||
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
val base = 0x0a // Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((base+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((base+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((base+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((base+1+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((base+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((base+1+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}bL"] = VarAllocation((base+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bL .. cx16.r15bL
|
||||
allocatedVariables["cx16.r${reg}bH"] = VarAllocation((base+1+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bH .. cx16.r15bH
|
||||
free.remove((base+reg*2).toUInt())
|
||||
free.remove((base+1+reg*2).toUInt())
|
||||
}
|
||||
|
||||
// 32 bits combined register pairs cx16.r0r1 .. cx16.r14r15
|
||||
allocatedVariables["cx16.r0r1sl"] = VarAllocation((base+0*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r2r3sl"] = VarAllocation((base+1*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r4r5sl"] = VarAllocation((base+2*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r6r7sl"] = VarAllocation((base+3*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r8r9sl"] = VarAllocation((base+4*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r10r11sl"] = VarAllocation((base+5*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r12r13sl"] = VarAllocation((base+6*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r14r15sl"] = VarAllocation((base+7*4).toUInt(), DataType.LONG, 4)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
override val SCRATCH_PTR = 0x04u // temp storage for a pointer $04+$05
|
||||
|
||||
|
||||
init {
|
||||
@@ -21,7 +22,6 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x02u..0xffu)
|
||||
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
@@ -57,7 +57,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
// add the free Zp addresses
|
||||
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||
free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
|
||||
0x92, 0x96, 0x9c, 0x9e, 0x9f, 0xa6,
|
||||
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
||||
} else {
|
||||
// don't use the zeropage at all
|
||||
@@ -72,7 +72,10 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
removeReservedFromFreePool()
|
||||
|
||||
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.KERNALSAFE) {
|
||||
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
|
||||
// C64 DESIGN: Virtual registers are ONLY allocated when sufficient zeropage space exists.
|
||||
// C64 has limited zeropage space compared to CX16. In BASICSAFE and FLOATSAFE modes,
|
||||
// there isn't enough contiguous space for all 32 bytes of virtual register variants.
|
||||
// Virtual registers are only allocated in FULL and KERNALSAFE modes where space permits.
|
||||
allocateCx16VirtualRegisters()
|
||||
}
|
||||
|
||||
@@ -83,16 +86,28 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
val base = 0x04 // Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
free.remove((4+reg*2).toUInt())
|
||||
free.remove((5+reg*2).toUInt())
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((base+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((base+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((base+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((base+1+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((base+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((base+1+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}bL"] = VarAllocation((base+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bL .. cx16.r15bL
|
||||
allocatedVariables["cx16.r${reg}bH"] = VarAllocation((base+1+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bH .. cx16.r15bH
|
||||
free.remove((base+reg*2).toUInt())
|
||||
free.remove((base+1+reg*2).toUInt())
|
||||
}
|
||||
|
||||
// 32 bits combined register pairs cx16.r0r1 .. cx16.r14r15
|
||||
allocatedVariables["cx16.r0r1sl"] = VarAllocation((base+0*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r2r3sl"] = VarAllocation((base+1*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r4r5sl"] = VarAllocation((base+2*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r6r7sl"] = VarAllocation((base+3*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r8r9sl"] = VarAllocation((base+4*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r10r11sl"] = VarAllocation((base+5*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r12r13sl"] = VarAllocation((base+6*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r14r15sl"] = VarAllocation((base+7*4).toUInt(), DataType.LONG, 4)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||
override val SCRATCH_PTR = 0x22u // temp storage for a pointer $22+$23
|
||||
|
||||
|
||||
init {
|
||||
@@ -21,48 +22,67 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||
|
||||
synchronized(this) {
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x22u..0xffu)
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
free.addAll(0xa9u..0xffu)
|
||||
}
|
||||
ZeropageType.FLOATSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
free.addAll(0xd4u..0xffu)
|
||||
}
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
// Initialize free list - no synchronization needed during construction
|
||||
// (object reference doesn't escape until fully constructed)
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x22u..0xffu)
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
free.addAll(0xa9u..0xffu)
|
||||
}
|
||||
ZeropageType.FLOATSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
free.addAll(0xd4u..0xffu)
|
||||
}
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0x22u..0x7fu)
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
allocateCx16VirtualRegisters()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
allocateCx16VirtualRegisters()
|
||||
retainAllowed()
|
||||
}
|
||||
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
// CX16 DESIGN: Virtual registers are ALWAYS allocated in zeropage for all ZeropageType modes.
|
||||
// The Commander X16 has ample zeropage space (0x22-0x7F in BASICSAFE, up to 0x22-0xFF in FULL),
|
||||
// so virtual registers can always fit. This ensures consistent behavior across all modes.
|
||||
// Virtual registers occupy addresses 0x02-0x21 (32 bytes for r0-r15 variants).
|
||||
//
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped*
|
||||
// elsewhere to fixed addresses. However, to be able for the compiler to "see" them as zeropage
|
||||
// variables, we have to register them here as well. This is important because the compiler
|
||||
// sometimes treats ZP variables more efficiently (for example if it's a pointer).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}bL"] = VarAllocation((2+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bL .. cx16.r15bL
|
||||
allocatedVariables["cx16.r${reg}bH"] = VarAllocation((3+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bH .. cx16.r15bH
|
||||
}
|
||||
|
||||
// 32 bits combined register pairs cx16.r0r1 .. cx16.r14r15
|
||||
allocatedVariables["cx16.r0r1sl"] = VarAllocation((2+0*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r2r3sl"] = VarAllocation((2+1*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r4r5sl"] = VarAllocation((2+2*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r6r7sl"] = VarAllocation((2+3*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r8r9sl"] = VarAllocation((2+4*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r10r11sl"] = VarAllocation((2+5*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r12r13sl"] = VarAllocation((2+6*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r14r15sl"] = VarAllocation((2+7*4).toUInt(), DataType.LONG, 4)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class ConfigurableZeropage(
|
||||
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
||||
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1: UInt, // temp storage 1 for a word
|
||||
override val SCRATCH_W2: UInt, // temp storage 2 for a word
|
||||
override val SCRATCH_PTR: UInt, // temp storage for a pointer
|
||||
|
||||
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
||||
basicsafe: List<UIntRange>,
|
||||
kernalsafe: List<UIntRange>,
|
||||
@@ -16,7 +21,7 @@ class ConfigurableZeropage(
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
TODO("floats")
|
||||
TODO("floats in configurable target zp")
|
||||
}
|
||||
|
||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
||||
@@ -27,7 +32,7 @@ class ConfigurableZeropage(
|
||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe")
|
||||
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
@@ -46,13 +51,30 @@ class ConfigurableZeropage(
|
||||
for(reg in 0..15) {
|
||||
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||
if(address<=0xffu) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}bL"] = VarAllocation(address, DataType.BOOL, 1) // cx16.r0bL .. cx16.r15bL
|
||||
allocatedVariables["cx16.r${reg}bH"] = VarAllocation(address+1u, DataType.BOOL, 1) // cx16.r0bH .. cx16.r15bH
|
||||
free.remove(address)
|
||||
free.remove(address+1u)
|
||||
}
|
||||
}
|
||||
|
||||
if(virtualRegistersStart<0xffu) {
|
||||
// 32 bits combined register pairs cx16.r0r1 .. cx16.r14r15
|
||||
val start = virtualRegistersStart.toInt()
|
||||
allocatedVariables["cx16.r0r1sl"] = VarAllocation((start + 0 * 4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r2r3sl"] = VarAllocation((start + 1 * 4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r4r5sl"] = VarAllocation((start + 2 * 4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r6r7sl"] = VarAllocation((start + 3 * 4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r8r9sl"] = VarAllocation((start + 4 * 4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r10r11sl"] = VarAllocation((start + 5 * 4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r12r13sl"] = VarAllocation((start + 6 * 4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r14r15sl"] = VarAllocation((start + 7 * 4).toUInt(), DataType.LONG, 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
// reference: http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
||||
@@ -14,12 +11,9 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
|
||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
override val SCRATCH_PTR = 0xb1u // temp storage for a pointer $b1+$b2
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
@@ -38,7 +32,10 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0xb3u..0xbau) // TODO more?
|
||||
// Only scratch register area is safe for transient use.
|
||||
// $b5 and $ba are clobbered by PET BASIC/Kernal calls like CHROUT ($ffd2).
|
||||
// User variables requiring persistent storage must NOT be placed here.
|
||||
free.addAll(listOf(0xb1u, 0xb2u, 0xb3u, 0xb4u, 0xb6u, 0xb7u, 0xb8u, 0xb9u, 0xfbu, 0xfcu, 0xfdu, 0xfeu))
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
@@ -50,6 +47,43 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.KERNALSAFE) {
|
||||
// PET DESIGN: Virtual registers are ONLY allocated when sufficient zeropage space exists.
|
||||
// PET has very limited zeropage space in BASICSAFE/FLOATSAFE modes (only 0xB1-0xBA).
|
||||
// Virtual registers are only allocated in FULL and KERNALSAFE modes where space permits.
|
||||
allocateCx16VirtualRegisters()
|
||||
}
|
||||
|
||||
retainAllowed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun allocateCx16VirtualRegisters() {
|
||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
val base = 0x04 // Unfortunately it cannot be the same as on the Commander X16 ($02). TODO: is this valid on PET?
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((base+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((base+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((base+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((base+1+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((base+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((base+1+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}bL"] = VarAllocation((base+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bL .. cx16.r15bL
|
||||
allocatedVariables["cx16.r${reg}bH"] = VarAllocation((base+1+reg*2).toUInt(), DataType.BOOL, 1) // cx16.r0bH .. cx16.r15bH
|
||||
free.remove((base+reg*2).toUInt())
|
||||
free.remove((base+1+reg*2).toUInt())
|
||||
}
|
||||
|
||||
// 32 bits combined register pairs cx16.r0r1 .. cx16.r14r15
|
||||
allocatedVariables["cx16.r0r1sl"] = VarAllocation((base+0*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r2r3sl"] = VarAllocation((base+1*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r4r5sl"] = VarAllocation((base+2*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r6r7sl"] = VarAllocation((base+3*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r8r9sl"] = VarAllocation((base+4*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r10r11sl"] = VarAllocation((base+5*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r12r13sl"] = VarAllocation((base+6*4).toUInt(), DataType.LONG, 4)
|
||||
allocatedVariables["cx16.r14r15sl"] = VarAllocation((base+7*4).toUInt(), DataType.LONG, 4)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package prog8tests.helpers
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
/**
|
||||
* Dummy IMemSizer implementation for testing.
|
||||
* Returns standard memory sizes for all data types.
|
||||
*/
|
||||
object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if (dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if (dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when (dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
|
||||
BaseDataType.LONG -> numElements * 4
|
||||
BaseDataType.FLOAT -> numElements * 5
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isFloat -> 5 * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun memorySize(dt: BaseDataType): Int {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy IStringEncoding implementation for testing.
|
||||
* Returns empty list/string for all operations.
|
||||
*/
|
||||
object DummyStringEncoder : IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ASCII IStringEncoding implementation for testing.
|
||||
* Encodes/decodes using simple ASCII mapping.
|
||||
*/
|
||||
object AsciiStringEncoder : IStringEncoding {
|
||||
override val defaultEncoding: Encoding = Encoding.ISO
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte> =
|
||||
str.map { it.code.toUByte() }
|
||||
|
||||
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String =
|
||||
bytes.joinToString("") { it.toInt().toChar().toString() }
|
||||
}
|
||||
Loaded 100 of 577 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user