mirror of
				https://github.com/irmen/prog8.git
				synced 2025-10-31 00:16:08 +00:00 
			
		
		
		
	Compare commits
	
		
			1379 Commits
		
	
	
		
			v10.0
			...
			v12.0-beta
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | ||
|  | 0814ea9711 | ||
|  | daefe839d8 | ||
|  | e6088dd315 | ||
|  | fc03d6f332 | ||
|  | 2aeb7a838e | ||
|  | 99ff5dd078 | ||
|  | 49982b49b6 | ||
|  | fd39c22616 | ||
|  | 9e79722a7f | ||
|  | 17334a1c58 | ||
|  | c7f0ff11ac | ||
|  | cd2cc89e6a | ||
|  | 069143092d | ||
|  | efd41260f2 | ||
|  | 8d2410622c | ||
|  | 60554389b3 | ||
|  | a940dc7d43 | ||
|  | 06ca68a625 | ||
|  | 5b58e5b158 | ||
|  | 74dd8fe80b | ||
|  | 75ddcda5f3 | ||
|  | 216825b98a | ||
|  | a96defab86 | ||
|  | 0864b0a1b7 | ||
|  | 8b158d9240 | ||
|  | f335251c2b | ||
|  | 67bc0b6931 | ||
|  | e646dd1ed1 | ||
|  | 2b7947f9b0 | ||
|  | ec0cfb4b3f | ||
|  | 9cdf53019c | ||
|  | 1a04a3eb3a | ||
|  | 105d3995e0 | ||
|  | 8ce3204f93 | ||
|  | d0f15f1285 | ||
|  | 66d6f67120 | ||
|  | a106c88054 | ||
|  | ee784e1ccc | ||
|  | bb75be0b44 | ||
|  | 2478aea316 | ||
|  | 1e17df5296 | ||
|  | 8583a96519 | ||
|  | d0c184c7de | ||
|  | 0191acb2b3 | ||
|  | 277a1a32b2 | ||
|  | 7a13f57ab0 | ||
|  | 0c882836d9 | ||
|  | 228be5cd04 | ||
|  | 08cd2fd6e8 | ||
|  | bc7b086f0f | ||
|  | e8f3af6981 | ||
|  | f9c7c7dab7 | ||
|  | 09a17743ad | ||
|  | 4f096a7511 | ||
|  | 2ab2130000 | ||
|  | 66558f7638 | ||
|  | a6f9ed07e7 | ||
|  | 7268a8736f | ||
|  | 8f6b5676d7 | ||
|  | ca9422bbe9 | ||
|  | 35d9412559 | ||
|  | f071c07dd9 | ||
|  | e5ff3c1ff3 | ||
|  | f0e8ff0326 | ||
|  | 3b5cda85ff | ||
|  | 420793f9e2 | ||
|  | cf1dbaf0d8 | ||
|  | d187cef6b7 | ||
|  | 3b3616afda | ||
|  | 0ffebc25d0 | ||
|  | 478e2b4ebd | ||
|  | a56ae7539a | ||
|  | 407773bda2 | ||
|  | 823eaa8918 | ||
|  | a2be42c5ca | ||
|  | a76b8d66ff | ||
|  | b7f47d354f | ||
|  | 5d33c93af9 | ||
|  | 4db6859f3f | ||
|  | 45fe1bb16e | ||
|  | b014facbd3 | ||
|  | 3b4b37f16b | ||
|  | 68d5983a14 | ||
|  | f2cfcfdf31 | ||
|  | 10b9162dc5 | ||
|  | c84cc8f8c9 | ||
|  | 78c71bbf0e | ||
|  | 37c2c1bf0b | ||
|  | c8996418da | ||
|  | 76b29aa629 | ||
|  | ee521793f8 | ||
|  | f42e12bc13 | ||
|  | 427451a23f | ||
|  | af7930d494 | ||
|  | e2882d37bf | ||
|  | 942d3ee640 | ||
|  | 7b4a82b91a | ||
|  | 056c0a24d9 | ||
|  | 827df04b32 | ||
|  | e174b31344 | ||
|  | 49959af752 | ||
|  | c86c0912f8 | ||
|  | 268b0c9365 | ||
|  | 099fe280ba | ||
|  | f786f60e9c | ||
|  | f40e1eb1f2 | ||
|  | 8b9da65357 | ||
|  | 2cbbe0d48a | ||
|  | b6e1fb3ba8 | ||
|  | bdccffbb8e | ||
|  | 5a85474712 | ||
|  | f50899c6fa | ||
|  | 4daa909f32 | ||
|  | 4555edf369 | ||
|  | 529ea5bf58 | ||
|  | fe011de934 | ||
|  | 0653d430a7 | ||
|  | a587f6e9a0 | ||
|  | 3850e1dbb5 | ||
|  | 91cde072e0 | ||
|  | 2ca4aed566 | ||
|  | 5071da6784 | ||
|  | 4c1e2f3110 | ||
|  | 2727a4dcb3 | ||
|  | 126d4c69e6 | ||
|  | 7657edcb7d | ||
|  | 580e786952 | ||
|  | c0ae35b3a3 | ||
|  | c3dc74788a | ||
|  | 379d241a0d | ||
|  | 1f49e8fe75 | ||
|  | d70cfbb661 | ||
|  | 5482ac0302 | ||
|  | 131d5ceb4f | ||
|  | 512ddd1694 | ||
|  | 14a213bff9 | ||
|  | d586846bc5 | ||
|  | ef4efcb112 | ||
|  | b01555d75e | ||
|  | 3804fba0f1 | ||
|  | f93b7e3303 | ||
|  | 73baaeff1f | ||
|  | 7c79cdbd2f | ||
|  | 8ea032ed66 | ||
|  | e7a0cb636c | ||
|  | 02f3f5d0f5 | ||
|  | 1e9bbd662b | ||
|  | 8644a4ae91 | ||
|  | 1e85f7812f | ||
|  | 80d88b3c61 | ||
|  | d2827a7431 | ||
|  | 28c721fa7d | ||
|  | 8f799567cf | ||
|  | 9e8cc8b54d | ||
|  | cc59069876 | ||
|  | 697d54e10a | ||
|  | 1679ca79b4 | ||
|  | 124ec77b58 | ||
|  | 3675d7961b | ||
|  | f8aaa2d13c | ||
|  | b7afda781a | ||
|  | 535ec13072 | ||
|  | 26d0a174db | ||
|  | b2e821755c | ||
|  | 2e303041c1 | ||
|  | 96bed8f57f | ||
|  | 86d4a4309f | ||
|  | 1a1ab0dac6 | ||
|  | ba8c3d14f7 | ||
|  | 617ea15c3a | ||
|  | ef192a5778 | ||
|  | 565973c520 | ||
|  | 25b1043572 | ||
|  | 1ebfff7c7b | ||
|  | 8341f9c066 | ||
|  | 28cac291de | ||
|  | 8fa14a10e2 | ||
|  | 55dbd095ed | ||
|  | 31ad8bdd8d | ||
|  | 181f3e9eb1 | ||
|  | 50c3d809dc | ||
|  | 58f696d00a | ||
|  | f603c543d3 | ||
|  | 6aaa0f928e | ||
|  | feb8aa435e | ||
|  | 310e8f15cd | ||
|  | da03941582 | ||
|  | dcbb36a3bd | ||
|  | 53558f5c1d | ||
|  | 189399d5f8 | ||
|  | 5406a992f5 | ||
|  | bc9683cc54 | ||
|  | 2eed75f602 | ||
|  | d58f9f56c4 | ||
|  | 2e35f3c3a3 | ||
|  | 5c6bd9c091 | ||
|  | 857d2eefca | ||
|  | 90f1e7fd6a | ||
|  | 18e37accf9 | ||
|  | cc53d698bf | ||
|  | cb86206698 | ||
|  | d77b1944fb | ||
|  | a58cb43c4a | ||
|  | 88574c87c4 | ||
|  | 3a7a7091c0 | ||
|  | 906b137a7c | ||
|  | 42e2c5f605 | ||
|  | cc13a51493 | ||
|  | f569ce6141 | ||
|  | 4958463e75 | ||
|  | 2360625927 | ||
|  | 8badc40883 | ||
|  | 844c97930f | ||
|  | 5c09dc10ae | ||
|  | 9fd9e9ab5f | ||
|  | 35c477b5a6 | ||
|  | ae0cadb383 | ||
|  | 984230e8fa | ||
|  | a874aec6a1 | ||
|  | ea1daa97d3 | ||
|  | fb0d9b46b0 | ||
|  | 9da70bdf05 | ||
|  | d640cfbe13 | ||
|  | 51a05ec4b7 | ||
|  | 1f5706bbeb | ||
|  | 25c9b2fea4 | ||
|  | 154f9b300f | ||
|  | d78ce77536 | ||
|  | b4fb43bc80 | ||
|  | e0e01f794e | ||
|  | 08865dbb4e | ||
|  | b9ad7e0e55 | ||
|  | 07158a6f1a | ||
|  | 957c42bc1d | ||
|  | f784da2da6 | ||
|  | c080fbe59a | ||
|  | d70b8303b1 | ||
|  | 1d38c3582a | ||
|  | 9438e996d7 | ||
|  | 3b4a5e27f7 | ||
|  | 648d9fc269 | ||
|  | 54fccec7d7 | ||
|  | 4f9693055e | ||
|  | 555c50ee10 | ||
|  | bf98ceca2c | ||
|  | 573cecb087 | ||
|  | 1b528491c2 | ||
|  | 4bdabe1961 | ||
|  | a3fa527378 | ||
|  | 84f5ffa426 | ||
|  | 25d2b42283 | ||
|  | 300d1a871c | ||
|  | 2fcb83a39f | ||
|  | 3ba1d00a7c | ||
|  | 64164c1c72 | ||
|  | 3ee6058524 | ||
|  | 93a0a41e73 | ||
|  | e7ab7b6d7a | ||
|  | 7d4dc3c063 | ||
|  | a50400b7d1 | ||
|  | f89f1a84d0 | ||
|  | 688dce6145 | ||
|  | b88f550c5b | ||
|  | 9864abd393 | ||
|  | c702c4a6df | ||
|  | 77e376f6bf | ||
|  | 491e5dbcfb | ||
|  | a5c7393561 | ||
|  | 7fd3e9bb7d | ||
|  | 459e9f8f3b | ||
|  | 5b1143bcb3 | ||
|  | fddd390d31 | ||
|  | e514eeba17 | ||
|  | c11a52b278 | ||
|  | 85e87dfe2e | ||
|  | cb47e2c149 | ||
|  | 0fc9aa6b2d | ||
|  | 155896c4c7 | ||
|  | 178e60bba0 | ||
|  | 9f84aa5fb2 | ||
|  | 66fc109ce5 | ||
|  | a231872821 | ||
|  | 7cfb33a448 | ||
|  | 3b798097b9 | ||
|  | 6fb05bdefc | ||
|  | 64ea72ed4d | ||
|  | 89425088ce | ||
|  | 925b9d845d | ||
|  | ad074076c2 | ||
|  | a2194c43a6 | ||
|  | 4b23b1dc86 | ||
|  | 9005c7994a | ||
|  | 4a47e15b1c | ||
|  | 09cbdf410a | ||
|  | df6a43c7f0 | ||
|  | 4ce130dc8b | ||
|  | 94d76aa82c | ||
|  | 73609636c5 | ||
|  | 66b06d6c40 | ||
|  | eeeb8d81f4 | ||
|  | 6f727aff88 | ||
|  | 518e5a30c2 | ||
|  | bbba4b3d60 | ||
|  | 967adb9a87 | ||
|  | 040a6c62de | ||
|  | 483d193ced | ||
|  | 62458216c9 | ||
|  | 76b05cb5fd | ||
|  | 570b574b93 | ||
|  | a82f211f9a | ||
|  | 504c80cddf | ||
|  | 4b4af9b527 | ||
|  | 28b383f888 | ||
|  | 40ce7725a1 | ||
|  | 1f2d46628e | ||
|  | c9535049c8 | ||
|  | 9317cf8a35 | ||
|  | 1cd754f05d | ||
|  | 97b8cb748d | ||
|  | 84d9040b57 | ||
|  | fdd18c615c | ||
|  | c14f6cfc2b | ||
|  | 326eab3dd1 | ||
|  | 6da1f7eb4c | ||
|  | 1e82483152 | ||
|  | 6e2fd41a8b | ||
|  | 9927af1095 | ||
|  | 7585b6ef6f | ||
|  | a6159702da | ||
|  | 0247fb0d84 | ||
|  | 6de760885f | ||
|  | 9851d14fb9 | ||
|  | d5fc69d3e4 | ||
|  | a40d120f2a | ||
|  | fcdd9414d9 | ||
|  | 272a1001a8 | ||
|  | 2a52241f1c | ||
|  | d8f1822c12 | ||
|  | ce7d094adb | ||
|  | a0cf1889a3 | ||
|  | 38ef394e15 | ||
|  | abbf7c7cb0 | ||
|  | ca5f7ae32f | ||
|  | cbc4b75e50 | ||
|  | 65ddcf91d0 | ||
|  | 5280e1b449 | ||
|  | b6ffb81909 | ||
|  | e9edffa9f0 | ||
|  | 0dd1c17ff4 | ||
|  | aef211e5f3 | ||
|  | 66829203d8 | ||
|  | 7a0eaf3148 | ||
|  | fa5479ee5f | ||
|  | 03412cacba | ||
|  | 01a38a0b11 | ||
|  | f43c14bd78 | ||
|  | fb23452383 | ||
|  | ab7dde1450 | ||
|  | 8d9bc2f5ff | ||
|  | 7651ccc84e | ||
|  | 1a6b95b388 | ||
|  | 78ec1e7512 | ||
|  | 7e38d26c33 | ||
|  | ed09dd4e9e | ||
|  | 5731b79554 | ||
|  | eaa22a9d13 | ||
|  | b2bdfe8482 | ||
|  | fea531be9a | ||
|  | 7c69d38588 | ||
|  | a088ee56b0 | ||
|  | ae669af904 | ||
|  | d1ddf05e38 | ||
|  | 51279a98b3 | ||
|  | bf33a4f82d | ||
|  | fff0d741c3 | ||
|  | e83d0ee820 | ||
|  | 09f3eecf56 | ||
|  | 2bd4326ff6 | ||
|  | c13168b60c | ||
|  | ea3871d0c4 | ||
|  | 70a2b11271 | ||
|  | 3cf39e072e | ||
|  | 413b86cc4a | ||
|  | a6107fcfdf | ||
|  | a064ade1e0 | ||
|  | df35aa7942 | ||
|  | cd49c5f88d | ||
|  | 1541ad2160 | ||
|  | c78b7b1a24 | ||
|  | 9c7a645e18 | ||
|  | 4acf38031a | ||
|  | 4cd7271e30 | ||
|  | 3f630ab1b0 | ||
|  | 04cb684fd4 | ||
|  | 4c843571ea | ||
|  | 1326498802 | ||
|  | b7ebd8c4a6 | ||
|  | 24e0a69480 | ||
|  | 4bcb2bdede | ||
|  | d27f3eb8a4 | ||
|  | d3e4481112 | ||
|  | 1d1d6b3d98 | ||
|  | 90b8a22a71 | ||
|  | 8dbfb8ab76 | ||
|  | e29ff1c848 | ||
|  | 585f6ffc9b | ||
|  | 46b94c17d6 | ||
|  | 7af8007447 | ||
|  | 16a2b2f566 | ||
|  | ea2a90c3c5 | ||
|  | 5cda750e5e | ||
|  | 4e143d45c8 | ||
|  | 4c50980d81 | ||
|  | 2954f5f04d | ||
|  | cac4c1eb1e | ||
|  | 0b1f30d98c | ||
|  | c7b1e8d772 | ||
|  | a4f7512d44 | ||
|  | 0d3ad80659 | ||
|  | aba1a73e28 | ||
|  | dca31b2ca3 | ||
|  | 0cb378ca31 | ||
|  | cf551d2cc7 | ||
|  | ac0c8a68f6 | ||
|  | 5986dcdd2f | ||
|  | 6be6eb2227 | ||
|  | d34015eec5 | ||
|  | 255c5bfaca | ||
|  | 01c6754928 | ||
|  | 8eaf884f69 | ||
|  | 699a2bb7ab | ||
|  | 4a2dcd20d1 | ||
|  | 4e98fb75d6 | ||
|  | 64e66e732f | ||
|  | 7aec627f6b | ||
|  | 59a2fec176 | ||
|  | edc5a5a94f | ||
|  | c5b7edad82 | ||
|  | 124ffac4e4 | ||
|  | 6d2a36fb2b | ||
|  | 28b43b3e1d | ||
|  | f7feaf158d | ||
|  | 2396f707c6 | ||
|  | d4d8e1b1ba | ||
|  | 44fec2c729 | ||
|  | a80a6913e3 | ||
|  | 0eac04c220 | ||
|  | 29dd758302 | ||
|  | 5c45adc7f0 | ||
|  | ad22cf08cd | ||
|  | 2c2ae64194 | ||
|  | 97c2dadd16 | ||
|  | b36e1e3baf | ||
|  | 2da35fec17 | ||
|  | bdeac74cfc | ||
|  | 6516d7cb15 | ||
|  | 31cf76042d | ||
|  | c4c4dcf2b3 | ||
|  | 03145630f8 | ||
|  | e2fcac322f | ||
|  | beaff4d650 | ||
|  | 79af96ddde | ||
|  | e439720c9d | ||
|  | 48d0185ea4 | ||
|  | e2592b4e0b | ||
|  | 2967866e3d | ||
|  | b566ea5c3f | ||
|  | 8f6eaeac2c | ||
|  | b4facaeb3c | ||
|  | d12b7ccc6b | ||
|  | 453e8bd0a0 | ||
|  | 9204d390ae | ||
|  | b70ce0015c | ||
|  | d113827753 | ||
|  | c67f877857 | ||
|  | 0ec719e429 | ||
|  | 17f7b11148 | ||
|  | 966b017670 | ||
|  | 4c98070b3c | ||
|  | 3681d6ee1c | ||
|  | 0af17cdc33 | ||
|  | 2aae1f5e30 | ||
|  | d18f2a7bfd | ||
|  | 9046fe8d3a | ||
|  | 78c7ee247a | ||
|  | d5adb85e5b | ||
|  | 69f953fd9b | ||
|  | 484677b4b1 | ||
|  | b10a8e728f | ||
|  | 25f25a8767 | ||
|  | 0c053e4a2c | ||
|  | 3f6521cc9b | ||
|  | a074491d5b | ||
|  | a291164953 | ||
|  | 43c55b58d2 | ||
|  | e7298f8162 | ||
|  | ddf990296b | ||
|  | ead8aa7800 | ||
|  | 7a9dd1ac9b | ||
|  | 1c97c22eff | ||
|  | bbf621a8c4 | ||
|  | 8efa89165c | ||
|  | 4f8aaf9244 | ||
|  | a97edef380 | ||
|  | eefae24aa3 | ||
|  | 54bffc91ae | ||
|  | 63f5ef9e14 | ||
|  | 034f27a8dd | ||
|  | c2f6311367 | ||
|  | 6f00a48772 | ||
|  | b3dba67405 | ||
|  | c9a4235669 | ||
|  | ae0d52274c | ||
|  | 8973763866 | ||
|  | 3d799ae7fe | ||
|  | 8b10115390 | ||
|  | d2e010c439 | ||
|  | 15867ab423 | ||
|  | 22c9e99fa3 | ||
|  | ee262f6aad | ||
|  | af64af2397 | ||
|  | 1feead2260 | ||
|  | d3dcd24b4d | ||
|  | fd1e6796ef | ||
|  | 3ea0f0cbaa | ||
|  | f3e3311598 | ||
|  | 0dc50a93a4 | ||
|  | fda8e61be4 | ||
|  | ac1d4b4a7a | ||
|  | c719e274d5 | ||
|  | e4990f8ec5 | ||
|  | 62afd3342e | ||
|  | 6e8a89e6f1 | ||
|  | aa2437cfb8 | ||
|  | 4a710ecdfc | ||
|  | 3ef5bdfeda | ||
|  | 7915dda35f | ||
|  | 9120e16683 | ||
|  | a1ebc7090d | ||
|  | 054b4636e0 | ||
|  | e3e7b060b7 | ||
|  | 5ac9c75521 | ||
|  | 07710e0995 | ||
|  | d6a67f5f2b | ||
|  | 2675623aea | ||
|  | 94263c43d0 | ||
|  | d8ec03874f | ||
|  | a7247f5b8b | ||
|  | 4d37581694 | ||
|  | 5d7ddebcad | ||
|  | 53df0eb707 | ||
|  | 8babad9c7c | ||
|  | 8db7aa07bd | ||
|  | 42f4b06ac8 | ||
|  | f4b50368ba | ||
|  | db80417bd7 | ||
|  | 7a6f2ecc8c | ||
|  | f5d556a7f9 | ||
|  | 2aae46d632 | ||
|  | 19ebc6d6b3 | ||
|  | f88c29e083 | ||
|  | 6ed9899dc7 | ||
|  | 9de7698a5c | ||
|  | 112d2d6058 | ||
|  | ddb8346711 | ||
|  | 8dd3faf395 | ||
|  | 35f3e8708b | ||
|  | cfe3fcc9e7 | ||
|  | 66a6659a6e | ||
|  | 88ae3daa42 | ||
|  | 08b8fe01ab | ||
|  | 731132d4b3 | ||
|  | 98acff802f | ||
|  | 5f11f485a2 | ||
|  | 34f3169dda | ||
|  | a3ef8f814b | ||
|  | 385dd6fc23 | ||
|  | 9af4168ae2 | ||
|  | a5e0e31b74 | ||
|  | b385dc8c26 | ||
|  | 92c012b55a | ||
|  | 641f6c05d8 | ||
|  | 788f6b44a6 | ||
|  | 63a4525f06 | ||
|  | 3e34a3ef72 | ||
|  | 0c5e8ca199 | ||
|  | ff23fb0086 | ||
|  | 56f41d5e34 | ||
|  | 4700a239b9 | ||
|  | bd5abfb969 | ||
|  | b93fa75377 | ||
|  | 681ce9c60c | ||
|  | dd0f0fe415 | ||
|  | 119040fc50 | ||
|  | 551e5688da | ||
|  | 56c1035581 | ||
|  | ba1e907c79 | ||
|  | 2a3a27c56d | ||
|  | 647af34f5b | ||
|  | 993be6394e | ||
|  | 9a27505315 | ||
|  | 2e37f5dee3 | ||
|  | 03e486c082 | ||
|  | edc83305a4 | ||
|  | 66e7c51064 | ||
|  | 60244aaf16 | ||
|  | 443391c700 | ||
|  | 47dbafacd4 | ||
|  | 5b6811d073 | ||
|  | 7516116bb7 | ||
|  | e6014ea4dd | ||
|  | 362abfe284 | ||
|  | ad4880997a | ||
|  | 592becc126 | ||
|  | c38765301e | ||
|  | 5f27426f59 | ||
|  | d924f8bff8 | ||
|  | d14c61b160 | ||
|  | fe2b67998c | ||
|  | 04df3c9f7f | ||
|  | de3d0b40dc | ||
|  | 4db4a5f1b2 | ||
|  | 5a0524ff4d | ||
|  | 5b7801eea1 | ||
|  | fbe231793b | ||
|  | 6a9269111e | ||
|  | a94cfd34f5 | ||
|  | 28eae5a0fd | ||
|  | 1818738fc8 | ||
|  | 7e1e7a0780 | ||
|  | 1fc79ff6dd | ||
|  | 3535c1acda | ||
|  | 33c8caac8f | ||
|  | 51d708bbdd | ||
|  | a5a918df84 | ||
|  | 820541e427 | ||
|  | e63a8f0c01 | ||
|  | c11a9b8709 | ||
|  | 80f39e8097 | ||
|  | 2a8b65e29c | ||
|  | 4bdf50145e | ||
|  | 3a9919a377 | ||
|  | eef8ae00b8 | ||
|  | ed15fac691 | ||
|  | f739e679e4 | ||
|  | fc0fae8caf | ||
|  | f46896fd74 | ||
|  | 52649a8e4f | ||
|  | bdfb01f6a0 | ||
|  | 1137e57393 | ||
|  | 267ea13e8c | ||
|  | 04f7b772a3 | ||
|  | 42c7569791 | ||
|  | 6d29b00a80 | ||
|  | 9f1bd2d7d6 | ||
|  | 9826d7c494 | ||
|  | c6bf57b390 | ||
|  | bfcf07c1a2 | ||
|  | 4d7e96d423 | ||
|  | 449461e412 | ||
|  | 607275ec66 | ||
|  | e55cde2a81 | ||
|  | 84afb374e6 | ||
|  | da1620807f | ||
|  | f39ef8f565 | ||
|  | fe8b6e820c | ||
|  | f29d24e96a | ||
|  | 620ffe54ec | ||
|  | ceaa4cd07d | ||
|  | af17f903ee | ||
|  | c532e28841 | ||
|  | dba0846866 | ||
|  | bed629998a | ||
|  | bc2ede76bf | ||
|  | 2a1fec2ed2 | ||
|  | 004048e5a7 | ||
|  | b941d6f1e4 | ||
|  | 37b346740b | ||
|  | f5e332daf7 | ||
|  | fe9a9fc5cb | ||
|  | cc57477b99 | ||
|  | a1574a7187 | ||
|  | a5110b1f96 | ||
|  | 006713fe13 | ||
|  | 7868e672e0 | ||
|  | e1a133c2c0 | ||
|  | c77cd0da39 | ||
|  | 577333f2c4 | ||
|  | 7d8cdcbfea | ||
|  | c5c4c6f111 | ||
|  | 73be754680 | ||
|  | acd841dbb6 | ||
|  | 6b52ba9397 | ||
|  | 10d12f73d6 | ||
|  | cd9119655c | ||
|  | 41afeccd51 | ||
|  | 6b87cbb703 | ||
|  | 32afcbfe42 | ||
|  | bc2b38daf4 | ||
|  | f40b7b62bb | ||
|  | 1ca3f64bf0 | ||
|  | 92527b4c1d | ||
|  | c48012c385 | ||
|  | a282b17286 | ||
|  | 58d9463f16 | ||
|  | 047decd552 | ||
|  | 82e0877e64 | ||
|  | 040d75dafa | ||
|  | 4e1686f6e3 | ||
|  | b5e691f367 | ||
|  | 325f55f22d | ||
|  | 9724f2db7d | ||
|  | 5f20f321f0 | ||
|  | d4b087ea3f | ||
|  | 8ff10724d1 | ||
|  | 1581381467 | ||
|  | 96b5a30f60 | ||
|  | 0e17a0474a | ||
|  | b27368175d | ||
|  | aba36f7c92 | ||
|  | a3fa946300 | ||
|  | 01bbc2234e | ||
|  | 58e1864144 | ||
|  | 88458f5355 | ||
|  | a4f697bae1 | ||
|  | 8201408f16 | ||
|  | 8b8caa1c2e | ||
|  | 4dc50cb551 | ||
|  | 5522a305ab | ||
|  | d7f72056fc | ||
|  | 64c9c9b7fe | ||
|  | 98e1c843e4 | ||
|  | 906d9d858c | ||
|  | 16c1309df1 | ||
|  | 6eacf1bddd | ||
|  | 6c8c8e11cc | ||
|  | e941d2665a | ||
|  | 68669dbef0 | ||
|  | 6a48de9a9f | ||
|  | 9d6d98930b | ||
|  | 3cc858db12 | ||
|  | 386a391fd9 | ||
|  | d33aed4ed5 | ||
|  | 73ec8c31ad | ||
|  | 24944ad49e | ||
|  | 26ed231f61 | ||
|  | 8485b8429f | ||
|  | 358215e4dd | ||
|  | f874942075 | ||
|  | 2cadb546d5 | ||
|  | 344a1b9eb8 | ||
|  | 3c77f8a020 | ||
|  | 8e00408e3e | ||
|  | abcdfd8e28 | ||
|  | b0f5b6925d | ||
|  | ef79d0c43e | ||
|  | 78b4288005 | ||
|  | 680f5d21ee | ||
|  | c71aa0895f | ||
|  | 9f8e61789a | ||
|  | 932035cdc5 | ||
|  | ef198f1493 | ||
|  | 48ef856c0b | ||
|  | 9aea2b22c4 | ||
|  | e0055bc431 | ||
|  | 9553248ed6 | ||
|  | 39d2194d8f | ||
|  | 0800033b47 | ||
|  | 64d8943b7d | ||
|  | 444e97b00b | ||
|  | 1816bda7ea | ||
|  | d4a2031c07 | ||
|  | 8cf0b6cf51 | ||
|  | f2010bf7a5 | ||
|  | 8f56a7fe69 | ||
|  | 64c132ee0a | ||
|  | 84a7e86fe3 | ||
|  | a8c09d6144 | ||
|  | 87c46ba730 | 
							
								
								
									
										17
									
								
								.aiignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.aiignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| # An .aiignore file follows the same syntax as a .gitignore file. | ||||
| # .gitignore documentation: https://git-scm.com/docs/gitignore | ||||
|  | ||||
| # you can ignore files | ||||
| .DS_Store | ||||
| *.log | ||||
| *.tmp | ||||
| *.bin | ||||
| *.prg | ||||
|  | ||||
| # or folders | ||||
| dist/ | ||||
| build/ | ||||
| out/ | ||||
| output/ | ||||
| .gradle/ | ||||
| .kotlin/ | ||||
							
								
								
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] | ||||
| #patreon: # Replace with a single Patreon username | ||||
| open_collective: # Replace with a single Open Collective username | ||||
| #open_collective: # Replace with a single Open Collective username | ||||
| ko_fi: irmen | ||||
| #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel | ||||
| #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry | ||||
| @@ -11,3 +11,4 @@ ko_fi: irmen | ||||
| #otechie: # Replace with a single Otechie username | ||||
| #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry | ||||
| #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] | ||||
| custom: ['https://paypal.me/irmendejong'] | ||||
|   | ||||
							
								
								
									
										25
									
								
								.github/workflows/all-ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/all-ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -10,7 +10,7 @@ jobs: | ||||
|  | ||||
|     steps: | ||||
|       - name: Checkout code | ||||
|         uses: actions/checkout@v3 | ||||
|         uses: actions/checkout@v4 | ||||
|  | ||||
|       - name: build and install recent 64tass | ||||
|         run: | | ||||
| @@ -18,19 +18,24 @@ jobs: | ||||
|           git clone --depth=1 https://github.com/irmen/64tass | ||||
|           cd 64tass | ||||
|           make -j4 | ||||
|           sudo make install | ||||
|           sudo make install           | ||||
|  | ||||
|       - name: Set up JDK 11 | ||||
|         uses: actions/setup-java@v3 | ||||
|       - name: Set up JDK 17 | ||||
|         uses: actions/setup-java@v4 | ||||
|         with: | ||||
|           java-version: 11 | ||||
|           distribution: adopt | ||||
|           java-version: 17 | ||||
|           distribution: temurin | ||||
|  | ||||
|       - name: Build and test with Gradle | ||||
|         run: ./gradlew build shadowJar --no-daemon | ||||
|  | ||||
|         run: | | ||||
|           ./gradlew build shadowJar --no-daemon | ||||
|           sha256sum -b compiler/build/libs/*-all.jar > compiler/build/libs/hash.txt | ||||
|          | ||||
|       - name: Create compiler shadowJar artifact | ||||
|         uses: actions/upload-artifact@v3 | ||||
|         uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: prog8-compiler-jar-zipped | ||||
|           path: compiler/build/libs/*-all.jar | ||||
|           path: | | ||||
|             compiler/build/libs/*-all.jar | ||||
|             compiler/build/libs/hash.txt | ||||
|  | ||||
|   | ||||
							
								
								
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,17 +1,23 @@ | ||||
| .idea/workspace.xml | ||||
| .idea/discord.xml | ||||
| .idea/developer-tools.xml | ||||
| .idea/usage.statistics.xml | ||||
| .idea/shelf/ | ||||
| build/ | ||||
| dist/ | ||||
| output/ | ||||
| out/ | ||||
| out-new/ | ||||
| out-old/ | ||||
| .*cache/ | ||||
| *.directory | ||||
| *.prg | ||||
| *.bin | ||||
| *.p8ir | ||||
| *.labels.txt | ||||
| *.vm.txt | ||||
| *.vice-mon-list | ||||
| docs/build | ||||
| out/ | ||||
| parser/**/*.interp | ||||
| parser/**/*.tokens | ||||
| parser/**/*.java | ||||
| @@ -22,6 +28,7 @@ compiler/src/prog8/buildversion/* | ||||
| .eggs/ | ||||
| /MANIFEST | ||||
| .tox/ | ||||
| .kotlin/ | ||||
| __pycache__/ | ||||
| parser.out | ||||
| parsetab.py | ||||
| @@ -30,8 +37,6 @@ parsetab.py | ||||
| compiler/lib/ | ||||
|  | ||||
| .gradle | ||||
| **/BuildVersion.kt | ||||
| /prog8compiler.jar | ||||
| sd*.img | ||||
| *.d64 | ||||
|  | ||||
|   | ||||
							
								
								
									
										1581
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1581
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										12
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,9 +1,19 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="Kotlin2JsCompilerArguments"> | ||||
|     <option name="moduleKind" value="plain" /> | ||||
|   </component> | ||||
|   <component name="Kotlin2JvmCompilerArguments"> | ||||
|     <option name="jvmTarget" value="11" /> | ||||
|   </component> | ||||
|   <component name="KotlinCommonCompilerArguments"> | ||||
|     <option name="apiVersion" value="2.2" /> | ||||
|     <option name="languageVersion" value="2.2" /> | ||||
|   </component> | ||||
|   <component name="KotlinCompilerSettings"> | ||||
|     <option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" /> | ||||
|   </component> | ||||
|   <component name="KotlinJpsPluginSettings"> | ||||
|     <option name="version" value="1.9.20" /> | ||||
|     <option name="version" value="2.2.0" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										20
									
								
								.idea/libraries/KotlinJavaRuntime.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								.idea/libraries/KotlinJavaRuntime.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,23 +1,23 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="KotlinJavaRuntime" type="repository"> | ||||
|     <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22" /> | ||||
|     <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" /> | ||||
|     </JAVADOC> | ||||
|     <SOURCES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" /> | ||||
|     </SOURCES> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										6
									
								
								.idea/libraries/antlr_antlr4.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/libraries/antlr_antlr4.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,13 +1,13 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="antlr.antlr4" type="repository"> | ||||
|     <properties maven-id="org.antlr:antlr4:4.13.1"> | ||||
|     <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.1/antlr4-4.13.1.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.1/antlr4-runtime-4.13.1.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/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!/" /> | ||||
|   | ||||
							
								
								
									
										13
									
								
								.idea/libraries/eclipse_lsp4j.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.idea/libraries/eclipse_lsp4j.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="eclipse.lsp4j" type="repository"> | ||||
|     <properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.13.1/gson-2.13.1.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.38.0/error_prone_annotations-2.38.0.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC /> | ||||
|     <SOURCES /> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										25
									
								
								.idea/libraries/github_hypfvieh_dbus_java.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										25
									
								
								.idea/libraries/github_hypfvieh_dbus_java.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,25 +0,0 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="github.hypfvieh.dbus.java" type="repository"> | ||||
|     <properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC /> | ||||
|     <SOURCES /> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										10
									
								
								.idea/libraries/glassfish_javax_json.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								.idea/libraries/glassfish_javax_json.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,10 +0,0 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="glassfish.javax.json" type="repository"> | ||||
|     <properties include-transitive-deps="false" maven-id="org.glassfish:javax.json:1.1.4" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC /> | ||||
|     <SOURCES /> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										23
									
								
								.idea/libraries/io_kotest_assertions_core_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										23
									
								
								.idea/libraries/io_kotest_assertions_core_jvm.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,23 +0,0 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="io.kotest.assertions.core.jvm" type="repository"> | ||||
|     <properties maven-id="io.kotest:kotest-assertions-core-jvm:5.8.0" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.8.0/kotest-assertions-core-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.8.0/kotest-assertions-shared-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.8.0/kotest-common-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.8.0/kotest-assertions-api-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC /> | ||||
|     <SOURCES /> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										23
									
								
								.idea/libraries/io_kotest_framework_datatest.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.idea/libraries/io_kotest_framework_datatest.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| <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> | ||||
							
								
								
									
										39
									
								
								.idea/libraries/io_kotest_runner_junit5_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										39
									
								
								.idea/libraries/io_kotest_runner_junit5_jvm.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,30 +1,30 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="io.kotest.runner.junit5.jvm" type="repository"> | ||||
|     <properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.8.0" /> | ||||
|     <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.8.0/kotest-runner-junit5-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.8.0/kotest-framework-api-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.8.0/kotest-assertions-shared-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/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.7.0/kotlinx-coroutines-test-jvm-1.7.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.8.0/kotest-common-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.8.0/kotest-framework-engine-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.162/classgraph-4.8.162.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/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.7.0/kotlinx-coroutines-debug-1.7.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.8.0/kotest-framework-discovery-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.8.0/kotest-assertions-core-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.8.0/kotest-assertions-api-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.8.0/kotest-extensions-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.8.0/kotest-framework-concurrency-jvm-5.8.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/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!/" /> | ||||
| @@ -32,11 +32,8 @@ | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" /> | ||||
|       <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 /> | ||||
|   | ||||
							
								
								
									
										21
									
								
								.idea/libraries/michael_bull_kotlin_result_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								.idea/libraries/michael_bull_kotlin_result_jvm.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,15 +1,20 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="michael.bull.kotlin.result.jvm" type="repository"> | ||||
|     <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" /> | ||||
|     <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.18/kotlin-result-jvm-1.1.18.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC /> | ||||
|     <SOURCES /> | ||||
|     <JAVADOC> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" /> | ||||
|     </JAVADOC> | ||||
|     <SOURCES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> | ||||
|     </SOURCES> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										11
									
								
								.idea/libraries/slf4j_simple.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								.idea/libraries/slf4j_simple.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,11 +0,0 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="slf4j.simple" type="repository"> | ||||
|     <properties maven-id="org.slf4j:slf4j-simple:2.0.11" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.11/slf4j-simple-2.0.11.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.11/slf4j-api-2.0.11.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC /> | ||||
|     <SOURCES /> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										18
									
								
								.idea/libraries/takes.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								.idea/libraries/takes.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,18 +0,0 @@ | ||||
| <component name="libraryTable"> | ||||
|   <library name="takes" type="repository"> | ||||
|     <properties maven-id="org.takes:takes:1.24.4" /> | ||||
|     <CLASSES> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" /> | ||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" /> | ||||
|     </CLASSES> | ||||
|     <JAVADOC /> | ||||
|     <SOURCES /> | ||||
|   </library> | ||||
| </component> | ||||
							
								
								
									
										10
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							| @@ -4,14 +4,15 @@ | ||||
|     <option name="perGrammarGenerationSettings"> | ||||
|       <list> | ||||
|         <PerGrammarGenerationSettings> | ||||
|           <option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" /> | ||||
|           <option name="fileName" value="$PROJECT_DIR$/parser/src/main/antlr/Prog8ANTLR.g4" /> | ||||
|           <option name="autoGen" value="true" /> | ||||
|           <option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" /> | ||||
|           <option name="libDir" value="" /> | ||||
|           <option name="encoding" value="" /> | ||||
|           <option name="pkg" value="" /> | ||||
|           <option name="language" value="" /> | ||||
|           <option name="language" value="Java" /> | ||||
|           <option name="generateListener" value="false" /> | ||||
|           <option name="generateVisitor" value="true" /> | ||||
|         </PerGrammarGenerationSettings> | ||||
|       </list> | ||||
|     </option> | ||||
| @@ -22,7 +23,10 @@ | ||||
|   <component name="FrameworkDetectionExcludesConfiguration"> | ||||
|     <type id="Python" /> | ||||
|   </component> | ||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_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> | ||||
							
								
								
									
										7
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							| @@ -2,6 +2,8 @@ | ||||
| <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" /> | ||||
| @@ -10,12 +12,13 @@ | ||||
|       <module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" /> | ||||
|       <module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
|   | ||||
| @@ -9,17 +9,19 @@ version: 2 | ||||
| build: | ||||
|   os: ubuntu-22.04 | ||||
|   tools: | ||||
|     python: "3.11" | ||||
|  | ||||
| # Build documentation in the docs/ directory with Sphinx | ||||
| sphinx: | ||||
|   configuration: docs/source/conf.py | ||||
|  | ||||
| # If using Sphinx, optionally build your docs in additional formats such as PDF | ||||
| formats: | ||||
|   - pdf | ||||
|     python: "3.12" | ||||
|  | ||||
| # Optionally declare the Python requirements required to build your docs | ||||
| python: | ||||
|   install: | ||||
|     - requirements: docs/requirements.txt | ||||
|  | ||||
| # Build documentation in the docs/ directory with Sphinx | ||||
| sphinx: | ||||
|   configuration: docs/source/conf.py | ||||
|   fail_on_warning: true | ||||
|  | ||||
| # If using Sphinx, optionally build your docs in additional formats such as PDF | ||||
| formats: | ||||
|   - pdf | ||||
|   - epub | ||||
|   | ||||
							
								
								
									
										12
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| # super simple Makefile to lauch the main gradle targets to build and/or test the prog8 compiler | ||||
|  | ||||
| .PHONY: all test | ||||
|  | ||||
| all: | ||||
| 	gradle installdist installshadowdist | ||||
| 	@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c" | ||||
|  | ||||
| test: | ||||
| 	gradle build | ||||
| 	@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c" | ||||
|  | ||||
							
								
								
									
										65
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,4 +1,7 @@ | ||||
| [](https://ko-fi.com/H2H6S0FFF) | ||||
|  | ||||
| PayPal: [](https://paypal.me/irmendejong) | ||||
|  | ||||
| [](https://prog8.readthedocs.io/) | ||||
|  | ||||
| Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors | ||||
| @@ -14,7 +17,7 @@ which aims to provide many conveniences over raw assembly code (even when using | ||||
|  | ||||
| This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can. | ||||
| If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,  | ||||
| you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).  | ||||
| you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong) | ||||
|  | ||||
|  | ||||
| Documentation | ||||
| @@ -29,6 +32,10 @@ How to get it/build it | ||||
| - Or, if you want/need a bleeding edge development version, you can: | ||||
|   - download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions). | ||||
|   - you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html). | ||||
|     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: | ||||
|   - Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8) | ||||
|  | ||||
| Community | ||||
| --------- | ||||
| @@ -49,26 +56,33 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code: | ||||
| What does Prog8 provide? | ||||
| ------------------------ | ||||
|  | ||||
| - reduction of source code length over raw assembly | ||||
| - fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8. | ||||
| - modularity, symbol scoping, subroutines | ||||
| - 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 | ||||
| - modularity, symbol scoping, subroutines. No need for forward declarations. | ||||
| - various data types other than just bytes (16-bit words, floats, strings) | ||||
| - floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do) | ||||
| - Structs and typed pointers | ||||
| - floating point math is supported on certain targets | ||||
| - access to most Kernal ROM routines as external subroutine definitions you can call normally | ||||
| - tight control over Zeropage usage | ||||
| - programs can be run multiple times without reloading because of automatic variable (re)initializations. | ||||
| - programs can be configured to execute in ROM | ||||
| - strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents. | ||||
| - automatic static variable allocations, automatic string and array variables and string sharing | ||||
| - subroutines with input parameters and result values | ||||
| - high-level program optimizations | ||||
| - small program boilerplate/compilersupport overhead | ||||
| - programs can be run multiple times without reloading because of automatic variable (re)initializations. | ||||
| - conditional branches | ||||
| - conditional branches 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  | ||||
| - many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse`` | ||||
| - ``defer`` statement to help write concise and robust subroutine cleanup logic | ||||
| - several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror`` | ||||
| - various powerful built-in libraries to do I/O, number conversions, graphics and more   | ||||
| - convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses | ||||
| - subroutines can return more than one result value | ||||
| - inline assembly allows you to have full control when every cycle or byte matters | ||||
| - supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64. | ||||
| - encode strings and characters into petscii or screencodes as desired (C64/Cx16) | ||||
| - 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  | ||||
|  | ||||
| *Rapid edit-compile-run-debug cycle:* | ||||
|  | ||||
| @@ -79,11 +93,11 @@ What does Prog8 provide? | ||||
|  | ||||
| *Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!): | ||||
|  | ||||
| - "cx16": [CommanderX16](https://www.commanderx16.com)  (65c02 CPU) | ||||
| - "c64": Commodore-64  (6502 like CPU) | ||||
| - "c128": Commodore-128  (6502 like CPU - the Z80 cpu mode is not supported) | ||||
| - "cx16": [CommanderX16](https://www.commanderx16.com)  (65c02 CPU) | ||||
| - "pet32": Commodore PET (experimental) | ||||
| - "atari": Atari 8 bit such as 800XL (experimental) | ||||
| - "pet32": Commodore PET (limited support) | ||||
| - 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) | ||||
|  | ||||
|  | ||||
| @@ -116,14 +130,13 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm:: | ||||
|  | ||||
|     %import textio | ||||
|     %zeropage basicsafe | ||||
|      | ||||
|  | ||||
|     main { | ||||
|      | ||||
|         ubyte[256] sieve | ||||
|         bool[256] sieve | ||||
|         ubyte candidate_prime = 2       ; is increased in the loop | ||||
|      | ||||
|         sub start() { | ||||
|             sys.memset(sieve, 256, false)   ; clear the sieve | ||||
|             sys.memset(sieve, 256, 0)   ; clear the sieve | ||||
|             txt.print("prime numbers up to 255:\n\n") | ||||
|             ubyte amount=0 | ||||
|             repeat { | ||||
| @@ -159,9 +172,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm:: | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| when compiled an ran on a C-64 you'll get: | ||||
|  | ||||
|  | ||||
| @@ -183,3 +193,12 @@ 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) | ||||
|   | ||||
| @@ -10,8 +10,15 @@ | ||||
|     <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="github.hypfvieh.dbus.java" level="project" /> | ||||
|     <orderEntry type="library" name="slf4j.simple" level="project" /> | ||||
|     <orderEntry type="module" module-name="codeCore" /> | ||||
|     <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> | ||||
							
								
								
									
										65
									
								
								beanshell/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								beanshell/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| 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") | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								beanshell/lib/bsh-3.0.0-SNAPSHOT.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								beanshell/lib/bsh-3.0.0-SNAPSHOT.jar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										48
									
								
								beanshell/src/prog8beanshell/CommandLineReader.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								beanshell/src/prog8beanshell/CommandLineReader.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| 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 | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										23
									
								
								beanshell/src/prog8beanshell/Main.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								beanshell/src/prog8beanshell/Main.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| 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 | ||||
|     )) | ||||
| } | ||||
							
								
								
									
										26
									
								
								benchmark-c/benchmark.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								benchmark-c/benchmark.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # 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. | ||||
|  | ||||
| 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 the smallest by a fair bit. | ||||
| * Prog8 execution speed places it more or less in the middle of the stack. | ||||
|  | ||||
| Measured with Prog8 V12.0 . | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										71
									
								
								benchmark-c/crc16.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								benchmark-c/crc16.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| %import textio | ||||
| %import floats | ||||
|  | ||||
| ; note: Prog8 has a complete CRC16 routine in its library: math.crc16 | ||||
|  | ||||
|  | ||||
| main { | ||||
|     sub start() { | ||||
|         txt.lowercase() | ||||
|         test.benchmark_name() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         test.benchmark() | ||||
|         txt.print_f(floats.time() / 60) | ||||
|         txt.print(" seconds\n") | ||||
|         void test.benchmark_check() | ||||
|         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]") | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         txt.print(" [FAIL] - expected ") | ||||
|         txt.print_uwhex(EXPECTED, true) | ||||
|         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 | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										72
									
								
								benchmark-c/crc32.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								benchmark-c/crc32.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| %import textio | ||||
| %import floats | ||||
|  | ||||
| ; note: Prog8 has a complete CRC32 routine in its library: math.crc32 | ||||
|  | ||||
|  | ||||
| main { | ||||
|     sub start() { | ||||
|         txt.lowercase() | ||||
|         test.benchmark_name() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         test.benchmark() | ||||
|         txt.print_f(floats.time() / 60) | ||||
|         txt.print(" seconds\n") | ||||
|         void test.benchmark_check() | ||||
|         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]") | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         txt.print(" [FAIL] - expected ") | ||||
|         txt.print_ulhex(EXPECTED, true) | ||||
|         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 | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										72
									
								
								benchmark-c/crc8.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								benchmark-c/crc8.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| %import textio | ||||
| %import floats | ||||
|  | ||||
|  | ||||
| main { | ||||
|     sub start() { | ||||
|         txt.lowercase() | ||||
|         test.benchmark_name() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         test.benchmark() | ||||
|         txt.print_f(floats.time() / 60) | ||||
|         txt.print(" seconds\n") | ||||
|         void test.benchmark_check() | ||||
|         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]") | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         txt.print(" [FAIL] - expected ") | ||||
|         txt.print_ubhex(EXPECTED, true) | ||||
|         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 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										93
									
								
								benchmark-c/pow.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								benchmark-c/pow.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| %import textio | ||||
| %import floats | ||||
|  | ||||
|  | ||||
| main { | ||||
|     sub start() { | ||||
|         txt.lowercase() | ||||
|         test.benchmark_name() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         test.benchmark() | ||||
|         txt.print_f(floats.time() / 60) | ||||
|         txt.print(" seconds\n") | ||||
|         void test.benchmark_check() | ||||
|         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 | ||||
|     } | ||||
|      | ||||
|  | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								benchmark-c/result-crc16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								benchmark-c/result-crc16.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 78 KiB | 
							
								
								
									
										
											BIN
										
									
								
								benchmark-c/result-crc32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								benchmark-c/result-crc32.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 80 KiB | 
							
								
								
									
										
											BIN
										
									
								
								benchmark-c/result-crc8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								benchmark-c/result-crc8.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 79 KiB | 
							
								
								
									
										
											BIN
										
									
								
								benchmark-c/result-pow.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								benchmark-c/result-pow.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 74 KiB | 
							
								
								
									
										
											BIN
										
									
								
								benchmark-c/result-sieve-bit.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								benchmark-c/result-sieve-bit.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 85 KiB | 
							
								
								
									
										
											BIN
										
									
								
								benchmark-c/result-sieve.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								benchmark-c/result-sieve.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 82 KiB | 
							
								
								
									
										85
									
								
								benchmark-c/sieve.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								benchmark-c/sieve.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| %import textio | ||||
| %import floats | ||||
|  | ||||
|  | ||||
| main { | ||||
|     sub start() { | ||||
|         txt.lowercase() | ||||
|         test.benchmark_name() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         test.benchmark() | ||||
|         txt.print_f(floats.time() / 60) | ||||
|         txt.print(" seconds\n") | ||||
|         void test.benchmark_check() | ||||
|         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]") | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         txt.print(" [FAIL] - expected ") | ||||
|         txt.print_uw(EXPECTED) | ||||
|         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 | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										107
									
								
								benchmark-c/sieve_bit.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								benchmark-c/sieve_bit.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| %import textio | ||||
| %import floats | ||||
|  | ||||
|  | ||||
| main { | ||||
|     sub start() { | ||||
|         txt.lowercase() | ||||
|         test.benchmark_name() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         test.benchmark() | ||||
|         txt.print_f(floats.time() / 60) | ||||
|         txt.print(" seconds\n") | ||||
|         void test.benchmark_check() | ||||
|         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]") | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         txt.print(" [FAIL] - expected ") | ||||
|         txt.print_uw(EXPECTED) | ||||
|         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 | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										10
									
								
								benchmark-program/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								benchmark-program/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| .PHONY:  clean run | ||||
|  | ||||
| run: | ||||
| 	prog8c -target cx16 benchmark.p8 | ||||
| 	x16emu -run -prg benchmark.prg -warp | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.prg *.PRG *.asm *.vice-* *.BIN *.PAL *.zip *.7z | ||||
|  | ||||
|  | ||||
							
								
								
									
										109
									
								
								benchmark-program/b_3d.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								benchmark-program/b_3d.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| %import textio | ||||
| %import math | ||||
|  | ||||
| rotate3d { | ||||
|     const ubyte WIDTH = 40 | ||||
|     const ubyte HEIGHT = 30 | ||||
|  | ||||
|     sub benchmark(uword max_time) -> uword { | ||||
|  | ||||
|         uword anglex | ||||
|         uword angley | ||||
|         uword anglez | ||||
|         uword frames | ||||
|  | ||||
|         txt.nl() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|  | ||||
|         while cbm.RDTIM16()<max_time { | ||||
|             matrix_math.rotate_vertices(msb(anglex), msb(angley), msb(anglez)) | ||||
|             draw_edges()   ; doesn't really draw anything in the benchmark, but does do the screen calculations | ||||
|             anglex+=500 | ||||
|             angley+=215 | ||||
|             anglez+=453 | ||||
|             frames++ | ||||
|         } | ||||
|  | ||||
|         return frames | ||||
|     } | ||||
|  | ||||
|     sub draw_edges() { | ||||
|  | ||||
|         ; plot the points of the 3d cube | ||||
|         ; first the points on the back, then the points on the front (painter algorithm) | ||||
|  | ||||
|         ubyte @zp i | ||||
|         word @zp rz | ||||
|         word @zp persp | ||||
|         byte @shared sx | ||||
|         byte @shared sy | ||||
|  | ||||
|         for i in 0 to len(matrix_math.xcoor)-1 { | ||||
|             rz = matrix_math.rotatedz[i] | ||||
|             if rz >= 10 { | ||||
|                 persp = 600 + rz/64 | ||||
|                 sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2 | ||||
|                 sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2 | ||||
|                 ;; txt.setcc(sx as ubyte, sy as ubyte, 46, 7) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for i in 0 to len(matrix_math.xcoor)-1 { | ||||
|             rz = matrix_math.rotatedz[i] | ||||
|             if rz < 10 { | ||||
|                 persp = 600 + rz/64 | ||||
|                 sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2 | ||||
|                 sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2 | ||||
|                 ;; txt.setcc(sx as ubyte, sy as ubyte, 81, 7) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         txt.chrout('.') | ||||
|     } | ||||
| } | ||||
|  | ||||
| matrix_math { | ||||
|     ; vertices | ||||
|     word[] xcoor = [ -40, -40, -40, -40,  40,  40,  40, 40 ] | ||||
|     word[] ycoor = [ -40, -40,  40,  40, -40, -40,  40, 40 ] | ||||
|     word[] zcoor = [ -40,  40, -40,  40, -40,  40, -40, 40 ] | ||||
|  | ||||
|     ; storage for rotated coordinates | ||||
|     word[len(xcoor)] rotatedx | ||||
|     word[len(ycoor)] rotatedy | ||||
|     word[len(zcoor)] rotatedz | ||||
|  | ||||
|     sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) { | ||||
|         ; rotate around origin (0,0,0) | ||||
|  | ||||
|         ; set up the 3d rotation matrix values | ||||
|         word wcosa = math.cos8(ax) | ||||
|         word wsina = math.sin8(ax) | ||||
|         word wcosb = math.cos8(ay) | ||||
|         word wsinb = math.sin8(ay) | ||||
|         word wcosc = math.cos8(az) | ||||
|         word wsinc = math.sin8(az) | ||||
|  | ||||
|         word wcosa_sinb = wcosa*wsinb / 128 | ||||
|         word wsina_sinb = wsina*wsinb / 128 | ||||
|  | ||||
|         word Axx = wcosa*wcosb / 128 | ||||
|         word Axy = (wcosa_sinb*wsinc - wsina*wcosc) / 128 | ||||
|         word Axz = (wcosa_sinb*wcosc + wsina*wsinc) / 128 | ||||
|         word Ayx = wsina*wcosb / 128 | ||||
|         word Ayy = (wsina_sinb*wsinc + wcosa*wcosc) / 128 | ||||
|         word Ayz = (wsina_sinb*wcosc - wcosa*wsinc) / 128 | ||||
|         word Azx = -wsinb | ||||
|         word Azy = wcosb*wsinc / 128 | ||||
|         word Azz = wcosb*wcosc / 128 | ||||
|  | ||||
|         ubyte @zp i | ||||
|         for i in 0 to len(xcoor)-1 { | ||||
|             ; don't normalize by dividing by 128, instead keep some precision for perspective calc later | ||||
|             rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i] | ||||
|             rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i] | ||||
|             rotatedz[i] = Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i] | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										89
									
								
								benchmark-program/b_adpcm.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								benchmark-program/b_adpcm.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| adpcm { | ||||
|  | ||||
|     sub decode_benchmark(uword max_time) -> uword { | ||||
|         uword num_blocks | ||||
|         txt.nl() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|  | ||||
|         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 | ||||
|             repeat 252/2 { | ||||
|                 unroll 2 { | ||||
|                     ubyte @zp nibble = @(nibbles_ptr) | ||||
|                     adpcm.decode_nibble(nibble & 15)     ; first word  (note: upper nibble needs to be zero!) | ||||
|                     adpcm.decode_nibble(nibble>>4)       ; second word  (note: upper nibble is zero, after the shifts.) | ||||
|                     nibbles_ptr++ | ||||
|                 } | ||||
|             } | ||||
|             num_blocks++ | ||||
|             txt.chrout('.') | ||||
|         } | ||||
|  | ||||
|         return num_blocks | ||||
|     } | ||||
|  | ||||
|     ; IMA ADPCM decoder.  Supports mono and stereo streams. | ||||
|  | ||||
|     byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8] | ||||
|     uword[] t_step = [ | ||||
|             7, 8, 9, 10, 11, 12, 13, 14, | ||||
|             16, 17, 19, 21, 23, 25, 28, 31, | ||||
|             34, 37, 41, 45, 50, 55, 60, 66, | ||||
|             73, 80, 88, 97, 107, 118, 130, 143, | ||||
|             157, 173, 190, 209, 230, 253, 279, 307, | ||||
|             337, 371, 408, 449, 494, 544, 598, 658, | ||||
|             724, 796, 876, 963, 1060, 1166, 1282, 1411, | ||||
|             1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, | ||||
|             3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, | ||||
|             7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, | ||||
|             15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, | ||||
|             32767] | ||||
|  | ||||
|     uword @requirezp predict       ; decoded 16 bit pcm sample for first channel. | ||||
|     ubyte @requirezp index | ||||
|     uword @requirezp pstep | ||||
|  | ||||
|     sub init(uword startPredict, ubyte startIndex) { | ||||
|         ; initialize first decoding channel. | ||||
|         predict = startPredict | ||||
|         index = startIndex | ||||
|         pstep = t_step[index] | ||||
|     } | ||||
|  | ||||
|     sub decode_nibble(ubyte @zp nibble) { | ||||
|         ; Decoder for a single nibble for the first channel. (value of 'nibble' needs to be strictly 0-15 !) | ||||
|         ; This is the hotspot of the decoder algorithm! | ||||
|         ; Note that the generated assembly from this is pretty efficient, | ||||
|         ; rewriting it by hand in asm seems to improve it only ~10%. | ||||
|         cx16.r0s = 0                ; difference | ||||
|         if nibble & %0100 !=0 | ||||
|             cx16.r0s += pstep | ||||
|         pstep >>= 1 | ||||
|         if nibble & %0010 !=0 | ||||
|             cx16.r0s += pstep | ||||
|         pstep >>= 1 | ||||
|         if nibble & %0001 !=0 | ||||
|             cx16.r0s += pstep | ||||
|         pstep >>= 1 | ||||
|         cx16.r0s += pstep | ||||
|         if nibble & %1000 !=0 | ||||
|             predict -= cx16.r0 | ||||
|         else | ||||
|             predict += cx16.r0 | ||||
|  | ||||
|         ; NOTE: the original C/Python code uses a 32 bits prediction value and clips it to a 16 bit word | ||||
|         ;       but for speed reasons we only work with 16 bit words here all the time (with possible clipping error) | ||||
|         ; if predicted > 32767: | ||||
|         ;    predicted = 32767 | ||||
|         ; elif predicted < -32767: | ||||
|         ;    predicted = - 32767 | ||||
|  | ||||
|         index += t_index[nibble] as ubyte | ||||
|         if_neg | ||||
|             index = 0 | ||||
|         else if index >= len(t_step)-1 | ||||
|             index = len(t_step)-1 | ||||
|         pstep = t_step[index] | ||||
|     } | ||||
| } | ||||
							
								
								
									
										234
									
								
								benchmark-program/b_btree.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								benchmark-program/b_btree.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | ||||
| ; Binary Search Tree. | ||||
| ; It's a simple implementation for test/demonstration purposes of the pointer support; | ||||
| ; no balancing is done and memory is not freed when elements are removed. | ||||
|  | ||||
| %import textio | ||||
|  | ||||
| btree { | ||||
|  | ||||
|     sub benchmark(uword max_time) -> uword { | ||||
|         txt.nl() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         uword score | ||||
|         while cbm.RDTIM16() < max_time { | ||||
|             bench_operations() | ||||
|             txt.chrout('.') | ||||
|             score++ | ||||
|         } | ||||
|         txt.nl() | ||||
|         return score | ||||
|     } | ||||
|  | ||||
|     sub bench_operations() { | ||||
|         arena.freeall() | ||||
|         btree.root = 0 | ||||
|  | ||||
|         for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716] | ||||
|             btree.add(cx16.r0) | ||||
|  | ||||
|         cx16.r0L = btree.size() | ||||
|         btree.process_tree_inorder() | ||||
|         btree.process_tree_preorder() | ||||
|  | ||||
|         void btree.contains(203) | ||||
|         void btree.contains(204) | ||||
|         void btree.contains(605) | ||||
|         void btree.contains(606) | ||||
|  | ||||
|         btree.remove(9999) | ||||
|         btree.remove(97) | ||||
|         btree.remove(187) | ||||
|         btree.remove(203) | ||||
|         btree.remove(275) | ||||
|         btree.remove(321) | ||||
|         btree.remove(520) | ||||
|         btree.remove(562) | ||||
|         btree.remove(606) | ||||
|         btree.remove(719) | ||||
|         btree.remove(794) | ||||
|  | ||||
|         cx16.r0L = btree.size() | ||||
|         btree.process_tree_inorder() | ||||
|         btree.process_tree_preorder() | ||||
|     } | ||||
|  | ||||
|     struct Node { | ||||
|         ^^Node left | ||||
|         ^^Node right | ||||
|         uword value | ||||
|     } | ||||
|  | ||||
|     ^^Node root = 0 | ||||
|  | ||||
|     sub add(uword value) { | ||||
|         ^^Node node = arena.alloc(sizeof(Node)) | ||||
|         node.value = value | ||||
|         node.left = node.right = 0 | ||||
|  | ||||
|         if root==0 | ||||
|             root=node | ||||
|         else { | ||||
|             ^^Node parent = root | ||||
|             repeat { | ||||
|                 if parent.value >= value { | ||||
|                     if parent.left!=0 | ||||
|                         parent = parent.left | ||||
|                     else { | ||||
|                         parent.left = node | ||||
|                         return | ||||
|                     } | ||||
|                 } else { | ||||
|                     if parent.right!=0 | ||||
|                         parent = parent.right | ||||
|                     else { | ||||
|                         parent.right = node | ||||
|                         return | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub contains(uword value) -> bool { | ||||
|         ^^Node r = root | ||||
|         while r!=0 { | ||||
|             if r.value==value | ||||
|                 return true | ||||
|             if r.value>value | ||||
|                 r = r.left | ||||
|             else | ||||
|                 r = r.right | ||||
|         } | ||||
|         return false | ||||
|     } | ||||
|  | ||||
|     sub size() -> ubyte { | ||||
|         ubyte count | ||||
|  | ||||
|         if root!=0 | ||||
|             count_node(root) | ||||
|  | ||||
|         return count | ||||
|  | ||||
|         sub count_node(^^Node r) { | ||||
|             count++ | ||||
|             if r.left!=0 { | ||||
|                 sys.pushw(r) | ||||
|                 count_node(r.left) | ||||
|                 r = sys.popw() | ||||
|             } | ||||
|             if r.right!=0 { | ||||
|                 sys.pushw(r) | ||||
|                 count_node(r.right) | ||||
|                 r = sys.popw() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub remove(uword value) { | ||||
|         ; note: we don't deallocate the memory from the node, for simplicity sake | ||||
|         ^^Node n = root | ||||
|         ^^Node parent = 0 | ||||
|         while n!=0 { | ||||
|             if n.value==value { | ||||
|                 if n.left==0 | ||||
|                     replacechild(parent, n, n.right) | ||||
|                 else if n.right==0 | ||||
|                     replacechild(parent, n, n.left) | ||||
|                 else { | ||||
|                     ; Both left & right subtrees are present. | ||||
|                     ; N = node to delete. | ||||
|                     ;    Find N's successor S. (N's right subtree's minimum element) | ||||
|                     ;    Attach N's left subtree to S.left (S doesn't have a left child) | ||||
|                     ;    Attach N's right subtree to Parent in place of N. | ||||
|                     ^^Node successor = find_successor(n) | ||||
|                     successor.left = n.left | ||||
|                     replacechild(parent, n, n.right) | ||||
|                 } | ||||
|                 return | ||||
|             } | ||||
|             parent = n | ||||
|             if n.value>value | ||||
|                 n = n.left | ||||
|             else | ||||
|                 n = n.right | ||||
|         } | ||||
|  | ||||
|         sub find_successor(^^Node p) -> ^^Node { | ||||
|             ^^Node succ = p | ||||
|             p = p.right | ||||
|             while p!=0 { | ||||
|                 succ = p | ||||
|                 p = p.left | ||||
|             } | ||||
|             return succ | ||||
|         } | ||||
|  | ||||
|         sub replacechild(^^Node p, ^^Node child, ^^Node newchild) { | ||||
|             if p.left==child | ||||
|                 p.left = newchild | ||||
|             else | ||||
|                 p.right = newchild | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     sub process_tree_inorder() { | ||||
|         if root!=0 | ||||
|             process_tree(root) | ||||
|  | ||||
|         sub process_tree(^^Node r) { | ||||
|             if r.left!=0 { | ||||
|                 sys.pushw(r) | ||||
|                 process_tree(r.left) | ||||
|                 r = sys.popw() | ||||
|             } | ||||
|             cx16.r0 = r.value | ||||
|             if r.right!=0 { | ||||
|                 sys.pushw(r) | ||||
|                 process_tree(r.right) | ||||
|                 r = sys.popw() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     sub process_tree_preorder() { | ||||
|         if root!=0 | ||||
|             process_tree(root,0) | ||||
|  | ||||
|         sub process_tree(^^Node r, ubyte depth) { | ||||
|             cx16.r0 = r.value | ||||
|             if r.left!=0 { | ||||
|                 sys.pushw(r) | ||||
|                 sys.push(depth) | ||||
|                 process_tree(r.left, depth+1) | ||||
|                 depth = sys.pop() | ||||
|                 r = sys.popw() | ||||
|             } | ||||
|             if r.right!=0 { | ||||
|                 sys.pushw(r) | ||||
|                 sys.push(depth) | ||||
|                 process_tree(r.right, depth+1) | ||||
|                 depth = sys.pop() | ||||
|                 r = sys.popw() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| arena { | ||||
|     ; extremely trivial arena allocator (that never frees) | ||||
|     uword buffer = memory("arena", 2000, 0) | ||||
|     uword next = buffer | ||||
|  | ||||
|     sub alloc(ubyte size) -> uword { | ||||
|         defer next += size | ||||
|         return next | ||||
|     } | ||||
|  | ||||
|     sub freeall() { | ||||
|         next = buffer | ||||
|     } | ||||
| } | ||||
							
								
								
									
										111
									
								
								benchmark-program/b_circles.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								benchmark-program/b_circles.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| %import gfx_lores | ||||
| %import math | ||||
|  | ||||
| circles { | ||||
|     const ubyte MAX_NUM_CIRCLES = 80 | ||||
|     const ubyte GROWTH_RATE = 4 | ||||
|     uword[MAX_NUM_CIRCLES] circle_x | ||||
|     uword[MAX_NUM_CIRCLES] circle_y | ||||
|     ubyte[MAX_NUM_CIRCLES] circle_radius | ||||
|     ubyte color | ||||
|     uword total_num_circles | ||||
|  | ||||
|     sub draw(bool use_kernal, uword max_time) -> uword { | ||||
|         if use_kernal | ||||
|             cx16.set_screen_mode(128) | ||||
|         else | ||||
|             gfx_lores.graphics_mode() | ||||
|  | ||||
|         math.rndseed(12345,6789) | ||||
|         cbm.SETTIM(0,0,0) | ||||
|  | ||||
|         total_num_circles = 0 | ||||
|         color = 16 | ||||
|  | ||||
|         while cbm.RDTIM16()<max_time { | ||||
|             if use_kernal { | ||||
|                 cx16.GRAPH_set_colors(0,0,0) | ||||
|                 cx16.GRAPH_clear() | ||||
|             } | ||||
|             else | ||||
|                 gfx_lores.clear_screen(0) | ||||
|             total_num_circles += draw_circles(use_kernal, max_time) | ||||
|         } | ||||
|  | ||||
|         if use_kernal | ||||
|             cx16.set_screen_mode(3) | ||||
|         else { | ||||
|             gfx_lores.text_mode() | ||||
|         } | ||||
|  | ||||
|         return total_num_circles | ||||
|     } | ||||
|  | ||||
|     sub draw_circles(bool use_kernal, uword max_time) -> uword { | ||||
|         uword @zp x | ||||
|         uword @zp y | ||||
|         ubyte @zp radius | ||||
|  | ||||
|         ubyte num_circles | ||||
|  | ||||
|         while num_circles<MAX_NUM_CIRCLES and cbm.RDTIM16()<max_time { | ||||
|             x = math.rndw() % 320 | ||||
|             y = math.rndw() % 240 | ||||
|             radius = GROWTH_RATE | ||||
|             if not_colliding() { | ||||
|                 while not_edge() and not_colliding() { | ||||
|                     radius += GROWTH_RATE | ||||
|                 } | ||||
|                 radius -= GROWTH_RATE | ||||
|                 if radius>0 { | ||||
|                     color++ | ||||
|                     if color==0 | ||||
|                         color=16 | ||||
|                     if use_kernal { | ||||
|                         cx16.GRAPH_set_colors(color, 255-color, 0) | ||||
|                         cx16.GRAPH_draw_oval(x-radius, y-radius, radius*2, radius*2, true) | ||||
|                     } | ||||
|                     else | ||||
|                         gfx_lores.disc(x, y as ubyte, radius, color) | ||||
|                     circle_x[num_circles] = x | ||||
|                     circle_y[num_circles] = y | ||||
|                     circle_radius[num_circles] = radius | ||||
|                     num_circles++ | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return num_circles | ||||
|  | ||||
|         sub not_colliding() -> bool { | ||||
|             if num_circles==0 | ||||
|                 return true | ||||
|             ubyte @zp c | ||||
|             for c in 0 to num_circles-1 { | ||||
|                 if distance(c) < (radius as uword) + circle_radius[c] | ||||
|                     return false | ||||
|             } | ||||
|             return true | ||||
|         } | ||||
|  | ||||
|         sub distance(ubyte cix) -> uword { | ||||
|             word dx = x as word - circle_x[cix] | ||||
|             word dy = y as word - circle_y[cix] | ||||
|             uword sqx = dx*dx as uword | ||||
|             uword sqy = dy*dy as uword | ||||
|             return sqrt(sqx + sqy) | ||||
|         } | ||||
|  | ||||
|         sub not_edge() -> bool { | ||||
|             if x as word - radius < 0 | ||||
|                 return false | ||||
|             if x + radius >= 320 | ||||
|                 return false | ||||
|             if y as word - radius < 0 | ||||
|                 return false | ||||
|             if y + radius >= 240 | ||||
|                 return false | ||||
|             return true | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										123
									
								
								benchmark-program/b_life.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								benchmark-program/b_life.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| ; conway's game of life. | ||||
|  | ||||
| %import math | ||||
| %import textio | ||||
|  | ||||
| life { | ||||
|     const ubyte WIDTH = 40 | ||||
|     const ubyte HEIGHT = 30 | ||||
|     const uword STRIDE = $0002+WIDTH | ||||
|     uword world1 = memory("world1", (WIDTH+2)*(HEIGHT+2), 0) | ||||
|     uword world2 = memory("world2", (WIDTH+2)*(HEIGHT+2), 0) | ||||
|     uword @requirezp active_world = world1 | ||||
|  | ||||
|    sub benchmark(uword max_time) -> uword { | ||||
|         txt.clear_screen() | ||||
|         sys.memset(world1, (WIDTH+2)*(HEIGHT+2), 0) | ||||
|         sys.memset(world2, (WIDTH+2)*(HEIGHT+2), 0) | ||||
|  | ||||
|         set_start_gen() | ||||
|  | ||||
|         uword gen | ||||
|         cbm.SETTIM(0,0,0) | ||||
|  | ||||
|         while cbm.RDTIM16()<max_time { | ||||
|             next_gen() | ||||
|             gen++ | ||||
|         } | ||||
|  | ||||
|         return gen | ||||
|     } | ||||
|  | ||||
|     sub set_start_gen() { | ||||
|  | ||||
| ; some way to set a custom start generation: | ||||
| ;        str start_gen = "                " + | ||||
| ;                        "                " + | ||||
| ;                        "                " + | ||||
| ;                        "          **    " + | ||||
| ;                        "        *    *  " + | ||||
| ;                        "       *        " + | ||||
| ;                        "       *     *  " + | ||||
| ;                        "       ******   " + | ||||
| ;                        "                " + | ||||
| ;                        "                " + | ||||
| ;                        "                " + | ||||
| ;                        "                " + | ||||
| ;                        "                " + | ||||
| ;                        "                " + | ||||
| ;                        "                " + | ||||
| ;                        "               " | ||||
| ; | ||||
| ;        for y in 0 to 15 { | ||||
| ;            for x in 0 to 15 { | ||||
| ;                if start_gen[y*16 + x]=='*' | ||||
| ;                    active_world[offset + x] = 1 | ||||
| ;            } | ||||
| ;            offset += STRIDE | ||||
| ;        } | ||||
|  | ||||
|         ; randomize whole world | ||||
|         math.rndseed(12345,9999) | ||||
|         uword offset = STRIDE+1 | ||||
|         ubyte x | ||||
|         ubyte y | ||||
|         for y in 0 to HEIGHT-1 { | ||||
|             for x in 0 to WIDTH-1 { | ||||
|                 active_world[offset+x] = math.rnd() & 1 | ||||
|             } | ||||
|             offset += STRIDE | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub next_gen() { | ||||
|         const ubyte DXOFFSET = 0 | ||||
|         const ubyte DYOFFSET = 0 | ||||
|         ubyte[2] cell_chars = [sc:' ', sc:'●'] | ||||
|  | ||||
|         uword @requirezp new_world = world1 | ||||
|         if active_world == world1 | ||||
|             new_world = world2 | ||||
|  | ||||
|         ; To avoid re-calculating word index lookups into the new- and active world arrays, | ||||
|         ; we calculate the required pointer values upfront. | ||||
|         ; Inside the loop we can use ptr+x just fine (results in efficient LDA (ptr),Y instruction because x is a byte type), | ||||
|         ; and for each row we simply add the stride to the pointer. | ||||
|         ; It's more readable to use active_world[offset] etc, but offset is a word value, and this produces | ||||
|         ; inefficient assembly code because we can't use a register indexed mode in this case. Costly inside a loop. | ||||
|  | ||||
|         uword @requirezp new_world_ptr = new_world + STRIDE+1-DXOFFSET | ||||
|         uword @requirezp active_world_ptr = active_world + STRIDE+1-DXOFFSET | ||||
|  | ||||
|         ubyte x | ||||
|         ubyte y | ||||
|         for y in DYOFFSET to HEIGHT+DYOFFSET-1 { | ||||
|  | ||||
|             cx16.vaddr_autoincr(1, $b000 + 256*y, 0, 2)     ;  allows us to use simple Vera data byte assigns later instead of setchr() calls | ||||
|  | ||||
|             for x in DXOFFSET to WIDTH+DXOFFSET-1 { | ||||
|                 ; count the living neighbors | ||||
|                 ubyte cell = @(active_world_ptr + x) | ||||
|                 uword @requirezp ptr = active_world_ptr + x - STRIDE - 1 | ||||
|                 ubyte neighbors = @(ptr) + @(ptr+1) + @(ptr+2) + | ||||
|                                   @(ptr+STRIDE) + cell + @(ptr+STRIDE+2) + | ||||
|                                   @(ptr+STRIDE*2) + @(ptr+STRIDE*2+1) + @(ptr+STRIDE*2+2) | ||||
|  | ||||
|                 ; apply game of life rules | ||||
|                 if neighbors==3 | ||||
|                     cell=1 | ||||
|                 else if neighbors!=4 | ||||
|                     cell=0 | ||||
|                 @(new_world_ptr + x) = cell | ||||
|  | ||||
|                 ; draw new cell | ||||
|                 ; txt.setchr(x,y,cell_chars[cell]) | ||||
|                 cx16.VERA_DATA0 = cell_chars[cell] | ||||
|             } | ||||
|             active_world_ptr += STRIDE | ||||
|             new_world_ptr += STRIDE | ||||
|         } | ||||
|  | ||||
|         active_world = new_world | ||||
|     } | ||||
| } | ||||
							
								
								
									
										54
									
								
								benchmark-program/b_mandelbrot.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								benchmark-program/b_mandelbrot.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| %import textio | ||||
| %import floats | ||||
|  | ||||
| mandelbrot { | ||||
|     const ubyte width = 39 | ||||
|     const ubyte height = 29 | ||||
|     const ubyte max_iter = 15 | ||||
|  | ||||
|     sub calc(uword max_time) -> uword  { | ||||
|         uword num_pixels | ||||
|         ubyte pixelx | ||||
|         ubyte pixely | ||||
|  | ||||
|         txt.home() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|  | ||||
|         while cbm.RDTIM16() < max_time { | ||||
|             for pixely in 0 to height-1 { | ||||
|                 float yy = (pixely as float)/0.40/height - 1.3 | ||||
|  | ||||
|                 for pixelx in 0 to width-1 { | ||||
|                     float xx = (pixelx as float)/0.32/width - 2.2 | ||||
|  | ||||
|                     float xsquared = 0.0 | ||||
|                     float ysquared = 0.0 | ||||
|                     float x = 0.0 | ||||
|                     float y = 0.0 | ||||
|                     ubyte iter = 0 | ||||
|  | ||||
|                     while iter<max_iter and xsquared+ysquared<4.0 { | ||||
|                         y = x*y*2.0 + yy | ||||
|                         x = xsquared - ysquared + xx | ||||
|                         xsquared = x*x | ||||
|                         ysquared = y*y | ||||
|                         iter++ | ||||
|                     } | ||||
|                     txt.color2(1, max_iter-iter) | ||||
|                     txt.spc() | ||||
|                     num_pixels++ | ||||
|  | ||||
|                     if cbm.RDTIM16()>=max_time | ||||
|                         goto finished | ||||
|                 } | ||||
|                 txt.nl() | ||||
|             } | ||||
|  | ||||
|             txt.clear_screen() | ||||
|         } | ||||
|  | ||||
| finished: | ||||
|         txt.color2(1, 6) | ||||
|         return num_pixels | ||||
|     } | ||||
| } | ||||
							
								
								
									
										343
									
								
								benchmark-program/b_maze.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								benchmark-program/b_maze.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,343 @@ | ||||
| %import textio | ||||
| %import math | ||||
|  | ||||
| ; Even though prog8 only has support for extremely limited recursion, | ||||
| ; you can write recursive algorithms with a bit of extra work by building your own explicit stack structure. | ||||
| ; This program shows a depth-first maze generation algorithm (1 possible path from start to finish), | ||||
| ; and a depth-first maze solver algorithm, both using a stack to store the path taken. | ||||
|  | ||||
| ; Note: this program can be compiled for multiple target systems. | ||||
|  | ||||
| maze { | ||||
|     uword score | ||||
|  | ||||
|     sub bench(uword max_time) -> uword { | ||||
|         txt.nl() | ||||
|         score=0 | ||||
|         math.rndseed(2345,44332) | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         while cbm.RDTIM16()<max_time { | ||||
|             maze.initialize() | ||||
|             maze.drawStartFinish() | ||||
|             if maze.generate(max_time) { | ||||
|                 maze.openpassages() | ||||
|                 maze.drawStartFinish() | ||||
|                 if maze.solve(max_time) { | ||||
|                     maze.drawStartFinish() | ||||
|                 } else break | ||||
|             } else break | ||||
|         } | ||||
|  | ||||
|         return score | ||||
|     } | ||||
|  | ||||
|     const uword screenwidth = 40 | ||||
|     const uword screenheight = 30 | ||||
|  | ||||
|     const ubyte numCellsHoriz = (screenwidth-1) / 2 | ||||
|     const ubyte numCellsVert = (screenheight-1) / 2 | ||||
|  | ||||
|     ; maze start and finish cells | ||||
|     const ubyte startCx = 0 | ||||
|     const ubyte startCy = 0 | ||||
|     const ubyte finishCx = numCellsHoriz-1 | ||||
|     const ubyte finishCy = numCellsVert-1 | ||||
|  | ||||
|     ; cell properties | ||||
|     const ubyte STONE = 128 | ||||
|     const ubyte WALKED = 64 | ||||
|     const ubyte BACKTRACKED = 32 | ||||
|     const ubyte UP = 1 | ||||
|     const ubyte RIGHT = 2 | ||||
|     const ubyte DOWN = 4 | ||||
|     const ubyte LEFT = 8 | ||||
|     const ubyte WALLCOLOR = 12 | ||||
|     const ubyte EMPTYCOLOR = 0 | ||||
|  | ||||
|     ; unfortunately on larger screens (cx16), the number of cells exceeds 256 and doesn't fit in a regular array anymore. | ||||
|     uword cells = memory("cells", numCellsHoriz*numCellsVert, 0) | ||||
|  | ||||
|     ubyte[256] cx_stack | ||||
|     ubyte[256] cy_stack | ||||
|     ubyte stackptr | ||||
|  | ||||
|     ubyte[4] directionflags = [LEFT,RIGHT,UP,DOWN] | ||||
|  | ||||
|     sub generate(uword max_time) -> bool { | ||||
|         ubyte cx = startCx | ||||
|         ubyte cy = startCy | ||||
|  | ||||
|         stackptr = 0 | ||||
|         @(celladdr(cx,cy)) &= ~STONE | ||||
|         drawCell(cx, cy) | ||||
|         uword cells_to_carve = numCellsHoriz * numCellsVert - 1 | ||||
|  | ||||
|         while cbm.RDTIM16()<max_time { | ||||
| carve_restart_after_repath: | ||||
|             ubyte direction = choose_uncarved_direction() | ||||
|             if direction==0 { | ||||
|                 ;backtrack | ||||
|                 stackptr-- | ||||
|                 if stackptr==255 { | ||||
|                     ; stack empty. | ||||
|                     ; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit) | ||||
|                     if cells_to_carve!=0 { | ||||
|                         if repath() | ||||
|                             goto carve_restart_after_repath | ||||
|                     } | ||||
|                     return true | ||||
|                 } | ||||
|                 cx = cx_stack[stackptr] | ||||
|                 cy = cy_stack[stackptr] | ||||
|             } else { | ||||
|                 cx_stack[stackptr] = cx | ||||
|                 cy_stack[stackptr] = cy | ||||
|                 stackptr++ | ||||
|                 if stackptr==0 { | ||||
|                     ; stack overflow, we can't track our path any longer. | ||||
|                     ; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit) | ||||
|                     if cells_to_carve!=0 { | ||||
|                         if repath() | ||||
|                             goto carve_restart_after_repath | ||||
|                     } | ||||
|                     return true | ||||
|                 } | ||||
|                 @(celladdr(cx,cy)) |= direction | ||||
|                 when direction { | ||||
|                     UP -> { | ||||
|                         cy-- | ||||
|                         @(celladdr(cx,cy)) |= DOWN | ||||
|                     } | ||||
|                     RIGHT -> { | ||||
|                         cx++ | ||||
|                         @(celladdr(cx,cy)) |= LEFT | ||||
|  | ||||
|                         score++ | ||||
|                     } | ||||
|                     DOWN -> { | ||||
|                         cy++ | ||||
|                         @(celladdr(cx,cy)) |= UP | ||||
|                     } | ||||
|                     LEFT -> { | ||||
|                         cx-- | ||||
|                         @(celladdr(cx,cy)) |= RIGHT | ||||
|                     } | ||||
|                 } | ||||
|                 @(celladdr(cx,cy)) &= ~STONE | ||||
|                 cells_to_carve-- | ||||
|                 drawCell(cx, cy) | ||||
|             } | ||||
|         } | ||||
|         return false | ||||
|  | ||||
|         sub repath() -> bool { | ||||
|             ; repath: try to find a new start cell with possible directions. | ||||
|             ; we limit our number of searches so that the algorith doesn't get stuck | ||||
|             ; for too long on bad rng... just accept a few unused cells in that case. | ||||
|             repeat 255 { | ||||
|                 do { | ||||
|                     cx = math.rnd() % numCellsHoriz | ||||
|                     cy = math.rnd() % numCellsVert | ||||
|                 } until @(celladdr(cx, cy)) & STONE ==0 | ||||
|                 if available_uncarved()!=0 | ||||
|                     return true | ||||
|             } | ||||
|             return false | ||||
|         } | ||||
|  | ||||
|         sub available_uncarved() -> ubyte { | ||||
|             ubyte candidates = 0 | ||||
|             if cx>0 and @(celladdr(cx-1, cy)) & STONE !=0 | ||||
|                 candidates |= LEFT | ||||
|             if cx<numCellsHoriz-1 and @(celladdr(cx+1, cy)) & STONE !=0 | ||||
|                 candidates |= RIGHT | ||||
|             if cy>0 and @(celladdr(cx, cy-1)) & STONE !=0 | ||||
|                 candidates |= UP | ||||
|             if cy<numCellsVert-1 and @(celladdr(cx, cy+1)) & STONE !=0 | ||||
|                 candidates |= DOWN | ||||
|             return candidates | ||||
|         } | ||||
|  | ||||
|         sub choose_uncarved_direction() -> ubyte { | ||||
|             ubyte candidates =  available_uncarved() | ||||
|             if candidates==0 | ||||
|                 return 0 | ||||
|  | ||||
|             repeat { | ||||
|                 ubyte choice = candidates & directionflags[math.rnd() & 3] | ||||
|                 if choice!=0 | ||||
|                     return choice | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub openpassages() { | ||||
|         ; open just a few extra passages, so that multiple routes are possible in theory. | ||||
|         ubyte numpassages | ||||
|         ubyte cx | ||||
|         ubyte cy | ||||
|         do { | ||||
|             do { | ||||
|                 cx = math.rnd() % (numCellsHoriz-2) + 1 | ||||
|                 cy = math.rnd() % (numCellsVert-2) + 1 | ||||
|             } until @(celladdr(cx, cy)) & STONE ==0 | ||||
|             ubyte direction = directionflags[math.rnd() & 3] | ||||
|             if @(celladdr(cx, cy)) & direction == 0 { | ||||
|                 when direction { | ||||
|                     LEFT -> { | ||||
|                         if @(celladdr(cx-1,cy)) & STONE == 0 { | ||||
|                             @(celladdr(cx,cy)) |= LEFT | ||||
|                             drawCell(cx,cy) | ||||
|                             numpassages++ | ||||
|                         } | ||||
|                     } | ||||
|                     RIGHT -> { | ||||
|                         if @(celladdr(cx+1,cy)) & STONE == 0 { | ||||
|                             @(celladdr(cx,cy)) |= RIGHT | ||||
|                             drawCell(cx,cy) | ||||
|                             numpassages++ | ||||
|                         } | ||||
|                     } | ||||
|                     UP -> { | ||||
|                         if @(celladdr(cx,cy-1)) & STONE == 0 { | ||||
|                             @(celladdr(cx,cy)) |= UP | ||||
|                             drawCell(cx,cy) | ||||
|                             numpassages++ | ||||
|                         } | ||||
|                     } | ||||
|                     DOWN -> { | ||||
|                         if @(celladdr(cx,cy+1)) & STONE == 0 { | ||||
|                             @(celladdr(cx,cy)) |= DOWN | ||||
|                             drawCell(cx,cy) | ||||
|                             numpassages++ | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } until numpassages==10 | ||||
|     } | ||||
|  | ||||
|     sub solve(uword max_time) -> bool { | ||||
|         ubyte cx = startCx | ||||
|         ubyte cy = startCy | ||||
|         const uword max_path_length = 1024 | ||||
|  | ||||
|         ; the path through the maze can be longer than 256 so doesn't fit in a regular array.... :( | ||||
|         uword pathstack = memory("pathstack", max_path_length, 0) | ||||
|         uword pathstackptr = 0 | ||||
|  | ||||
|         @(celladdr(cx,cy)) |= WALKED | ||||
|         ; txt.setcc(cx*2+1, cy*2+1, 81, 1) | ||||
|  | ||||
|         while cbm.RDTIM16()<max_time { | ||||
| solve_loop: | ||||
|             if cx==finishCx and cy==finishCy { | ||||
|                 ;txt.home() | ||||
|                 txt.print("found! path length: ") | ||||
|                 txt.print_uw(pathstackptr) | ||||
|                 txt.nl() | ||||
|                 return true | ||||
|             } | ||||
|  | ||||
|             ubyte cell = @(celladdr(cx,cy)) | ||||
|             if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0 { | ||||
|                 @(pathstack + pathstackptr) = UP | ||||
|                 ;txt.setcc(cx*2+1, cy*2, 81, 3) | ||||
|                 cy-- | ||||
|             } | ||||
|             else if cell & DOWN !=0 and @(celladdr(cx,cy+1)) & (WALKED|BACKTRACKED) ==0 { | ||||
|                 @(pathstack + pathstackptr) = DOWN | ||||
|                 ;txt.setcc(cx*2+1, cy*2+2, 81, 3) | ||||
|                 cy++ | ||||
|             } | ||||
|             else if cell & LEFT !=0 and @(celladdr(cx-1,cy)) & (WALKED|BACKTRACKED) ==0 { | ||||
|                 @(pathstack + pathstackptr) = LEFT | ||||
|                 ;txt.setcc(cx*2, cy*2+1, 81, 3) | ||||
|                 cx-- | ||||
|             } | ||||
|             else if cell & RIGHT !=0 and @(celladdr(cx+1,cy)) & (WALKED|BACKTRACKED) ==0 { | ||||
|                 @(pathstack + pathstackptr) = RIGHT | ||||
|                 ;txt.setcc(cx*2+2, cy*2+1, 81, 3) | ||||
|                 cx++ | ||||
|             } | ||||
|             else { | ||||
|                 ; dead end, pop stack | ||||
|                 pathstackptr-- | ||||
|                 if pathstackptr==65535 { | ||||
|                     txt.print("no solution?!\n") | ||||
|                     return true | ||||
|                 } | ||||
|                 @(celladdr(cx,cy)) |= BACKTRACKED | ||||
|                 ;txt.setcc(cx*2+1, cy*2+1, 81, 2) | ||||
|                 when @(pathstack + pathstackptr) { | ||||
|                     UP -> { | ||||
|                         ;txt.setcc(cx*2+1, cy*2+2, 81, 9) | ||||
|                         cy++ | ||||
|                     } | ||||
|                     DOWN -> { | ||||
|                         ;txt.setcc(cx*2+1, cy*2, 81, 9) | ||||
|                         cy-- | ||||
|                     } | ||||
|                     LEFT -> { | ||||
|                         ;txt.setcc(cx*2+2, cy*2+1, 81, 9) | ||||
|                         cx++ | ||||
|                     } | ||||
|                     RIGHT -> { | ||||
|                         ;txt.setcc(cx*2, cy*2+1, 81, 9) | ||||
|                         cx-- | ||||
|  | ||||
|                         score++ | ||||
|                     } | ||||
|                 } | ||||
|                 goto solve_loop | ||||
|             } | ||||
|             pathstackptr++ | ||||
|             if pathstackptr==max_path_length { | ||||
|                 txt.print("stack overflow, path too long\n") | ||||
|                 return true | ||||
|             } | ||||
|             @(celladdr(cx,cy)) |= WALKED | ||||
|             ;txt.setcc(cx*2+1, cy*2+1, 81, 1) | ||||
|         } | ||||
|         return false | ||||
|     } | ||||
|  | ||||
|     sub celladdr(ubyte cx, ubyte cy) -> uword { | ||||
|         return cells+(numCellsHoriz as uword)*cy+cx | ||||
|     } | ||||
|  | ||||
|     sub drawCell(ubyte cx, ubyte cy) { | ||||
|         return | ||||
| ;        ubyte x = cx * 2 + 1 | ||||
| ;        ubyte y = cy * 2 + 1 | ||||
| ;        ubyte doors = @(celladdr(cx,cy)) | ||||
| ;        if doors & UP !=0 | ||||
| ;            txt.setcc(x, y-1, ' ', EMPTYCOLOR) | ||||
| ;        if doors & RIGHT !=0 | ||||
| ;            txt.setcc(x+1, y, ' ', EMPTYCOLOR) | ||||
| ;        if doors & DOWN !=0 | ||||
| ;            txt.setcc(x, y+1, ' ', EMPTYCOLOR) | ||||
| ;        if doors & LEFT !=0 | ||||
| ;            txt.setcc(x-1, y, ' ', EMPTYCOLOR) | ||||
| ;        if doors & STONE !=0 | ||||
| ;            txt.setcc(x, y, 160, WALLCOLOR) | ||||
| ;        else | ||||
| ;            txt.setcc(x, y, 32, EMPTYCOLOR) | ||||
| ; | ||||
| ;        if doors & WALKED !=0 | ||||
| ;            txt.setcc(x, y, 81, 1) | ||||
| ;        if doors & BACKTRACKED !=0 | ||||
| ;            txt.setcc(x, y, 81, 2) | ||||
|     } | ||||
|  | ||||
|     sub initialize() { | ||||
|         sys.memset(cells, numCellsHoriz*numCellsVert, STONE) | ||||
|         ; txt.fill_screen(160, WALLCOLOR) | ||||
|         drawStartFinish() | ||||
|     } | ||||
|  | ||||
|     sub drawStartFinish() { | ||||
|         ; txt.setcc(startCx*2+1,startCy*2+1,sc:'s',5) | ||||
|         ; txt.setcc(finishCx*2+1, finishCy*2+1, sc:'f', 13) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										63
									
								
								benchmark-program/b_queens.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								benchmark-program/b_queens.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| %import textio | ||||
|  | ||||
| ; Recursive N-Queens solver. | ||||
| ; The problem is: find all possible ways to place 8 Queen chess pieces on a chess board, so that none of them attacks any other. | ||||
| ; (this program prints all solutions without taking mirroring and flipping the chess board into account) | ||||
| ; Note: this program can be compiled for multiple target systems. | ||||
|  | ||||
| queens { | ||||
|     const ubyte NUMQUEENS=8 | ||||
|     ubyte[NUMQUEENS] board | ||||
|  | ||||
|     sub could_place(ubyte row, ubyte col) -> bool { | ||||
|         if row==0 | ||||
|             return true | ||||
|         ubyte i | ||||
|         for i in 0 to row-1 { | ||||
|             if board[i]==col or board[i]-i==col-row or board[i]+i==col+row | ||||
|                 return false | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
|  | ||||
|     uword solution_count | ||||
|     uword maximum_duration | ||||
|  | ||||
|     sub place_queen(ubyte row) -> bool { | ||||
|         if row == NUMQUEENS { | ||||
|             solution_count++ | ||||
|             txt.chrout('.') | ||||
|             return cbm.RDTIM16()<maximum_duration | ||||
|         } | ||||
|         bool continue_running=true | ||||
|         ubyte col | ||||
|         for col in 0 to NUMQUEENS-1 { | ||||
|             if could_place(row, col) { | ||||
|                 board[row] = col | ||||
|                 ; we need to save the local variables row and col. | ||||
|                 sys.push(row) | ||||
|                 sys.push(col) | ||||
|                 continue_running = place_queen(row + 1) | ||||
|                 ; restore the local variables after the recursive call. | ||||
|                 col = sys.pop() | ||||
|                 row = sys.pop() | ||||
|                 board[row] = 0 | ||||
|  | ||||
|                 if not continue_running | ||||
|                     break | ||||
|             } | ||||
|         } | ||||
|         return continue_running | ||||
|     } | ||||
|  | ||||
|     sub bench(uword max_time) -> uword { | ||||
|         solution_count = 0 | ||||
|         maximum_duration = max_time | ||||
|         txt.nl() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         while cbm.RDTIM16() < maximum_duration { | ||||
|             void place_queen(0) | ||||
|         } | ||||
|         return solution_count | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								benchmark-program/b_sprites.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								benchmark-program/b_sprites.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										989
									
								
								benchmark-program/b_textelite.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										989
									
								
								benchmark-program/b_textelite.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,989 @@ | ||||
| %import textio | ||||
| %import conv | ||||
| %import strings | ||||
|  | ||||
|  | ||||
| textelite { | ||||
|  | ||||
|     const ubyte numforLave = 7      ;  Lave is 7th generated planet in galaxy one | ||||
|     const ubyte numforZaonce = 129 | ||||
|     const ubyte numforDiso = 147 | ||||
|     const ubyte numforRiedquat = 46 | ||||
|     uword num_commands | ||||
|  | ||||
|     sub bench(uword max_time) -> uword { | ||||
|         num_commands = 0 | ||||
|         txt.lowercase() | ||||
|         cbm.SETTIM(0,0,0) | ||||
|         while cbm.RDTIM16()<max_time { | ||||
|             reinit() | ||||
|             run_commands(max_time) | ||||
|         } | ||||
|         return num_commands | ||||
|     } | ||||
|  | ||||
|     sub reinit() { | ||||
|         ;txt.clear_screen() | ||||
|         ;txt.print("\n --- TextElite v1.3 ---\n") | ||||
|         txt.print("\nnew game\n") | ||||
|         elite_planet.set_seed(0, 0) | ||||
|         elite_galaxy.travel_to(1, numforLave) | ||||
|         elite_market.init(0)  ;  Lave's market is seeded with 0 | ||||
|         elite_ship.init() | ||||
|         elite_planet.display(false, 0) | ||||
|         input_index = 0 | ||||
|     } | ||||
|  | ||||
|     sub run_commands(uword max_time) { | ||||
|         while cbm.RDTIM16() < max_time { | ||||
|             str input = "????????" | ||||
|             ;txt.print("\nCash: ") | ||||
|             ;elite_util.print_10s(elite_ship.cash) | ||||
|             ;txt.print("\nCommand (?=help): ") | ||||
|             ubyte num_chars = next_input(input) | ||||
|             ;txt.nl() | ||||
|             if num_chars!=0 { | ||||
|                 when input[0] { | ||||
|                     'q' -> { | ||||
|                         bool has_error = false | ||||
|                         if elite_galaxy.number != 2 { | ||||
|                             txt.print("\nERROR: galaxy is not 2: ") | ||||
|                             txt.print_ub(elite_galaxy.number) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if elite_planet.number != 164 { | ||||
|                             txt.print("\nERROR: planet is not 164: ") | ||||
|                             txt.print_ub(elite_planet.number) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if elite_planet.x != 116 { | ||||
|                             txt.print("\nERROR: planet.x is not 116: ") | ||||
|                             txt.print_ub(elite_planet.x) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if elite_planet.y != 201 { | ||||
|                             txt.print("\nERROR: planet.y is not 201: ") | ||||
|                             txt.print_ub(elite_planet.y) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if "ribeen" != elite_planet.name { | ||||
|                             txt.print("\nERROR: planet.name is not 'ribeen': ") | ||||
|                             txt.print(elite_planet.name) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if elite_ship.cash != 1212 { | ||||
|                             txt.print("\nERROR: cash is not 1212: ") | ||||
|                             txt.print_uw(elite_ship.cash) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if elite_ship.fuel != 50 { | ||||
|                             txt.print("\nERROR: fuel is not 50:") | ||||
|                             txt.print_ub(elite_ship.fuel) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if elite_ship.cargohold[0] != 3 { | ||||
|                             txt.print("\nERROR: food is not 3:") | ||||
|                             txt.print_ub(elite_ship.cargohold[0]) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if elite_ship.cargohold[1] != 0 { | ||||
|                             txt.print("\nERROR: textiles is not 0:") | ||||
|                             txt.print_ub(elite_ship.cargohold[1]) | ||||
|                             txt.nl() | ||||
|                             has_error=true | ||||
|                         } | ||||
|                         if has_error | ||||
|                             sys.exit(1) | ||||
|                         return | ||||
|                     } | ||||
|                     'b' -> elite_trader.do_buy() | ||||
|                     's' -> elite_trader.do_sell() | ||||
|                     'f' -> elite_trader.do_fuel() | ||||
|                     'j' -> elite_trader.do_jump() | ||||
|                     't' -> elite_trader.do_teleport() | ||||
|                     'g' -> elite_trader.do_next_galaxy() | ||||
|                     'i' -> elite_trader.do_info() | ||||
|                     'm' -> { | ||||
|                         if input[1]=='a' and input[2]=='p' | ||||
|                             elite_trader.do_map() | ||||
|                         else | ||||
|                             elite_trader.do_show_market() | ||||
|                     } | ||||
|                     'l' -> elite_trader.do_local() | ||||
|                     'c' -> elite_trader.do_cash() | ||||
|                     'h' -> elite_trader.do_hold() | ||||
|                 } | ||||
|                 num_commands++ | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     str[] inputs = [ | ||||
|         "i", | ||||
|         "diso", | ||||
|         "i", | ||||
|         "lave", | ||||
|         "m", | ||||
|         "b", | ||||
|         "food", | ||||
|         "15", | ||||
|         "map", | ||||
|         "g", | ||||
|         "map", | ||||
|         "l", | ||||
|         "j", | ||||
|         "zao", | ||||
|         "s", | ||||
|         "food", | ||||
|         "12", | ||||
|         "tele", | ||||
|         "quti", | ||||
|         "tele", | ||||
|         "aro", | ||||
|         "i", | ||||
|         "diso", | ||||
|         "i", | ||||
|         "lave", | ||||
|         "i", | ||||
|         "zao", | ||||
|         "galhyp", | ||||
|         "fuel", | ||||
|         "20", | ||||
|         "j", | ||||
|         "rib", | ||||
|         "i", | ||||
|         "rib", | ||||
|         "i", | ||||
|         "tiri", | ||||
|         "q", | ||||
|         0 | ||||
|     ] | ||||
|  | ||||
|     ubyte input_index | ||||
|  | ||||
|     sub next_input(str buffer) -> ubyte { | ||||
|         input_index++ | ||||
|         return strings.copy(inputs[input_index], buffer) | ||||
|     } | ||||
| } | ||||
|  | ||||
| elite_trader { | ||||
|     str input = "??????????" | ||||
|     ubyte num_chars | ||||
|  | ||||
|     sub do_jump() { | ||||
|         ;txt.print("\nJump to what system? ") | ||||
|         jump_to_system() | ||||
|     } | ||||
|  | ||||
|     sub do_teleport() { | ||||
|         ;txt.print("\nCheat! Teleport to what system? ") | ||||
|         ubyte fuel = elite_ship.fuel | ||||
|         elite_ship.fuel = 255 | ||||
|         jump_to_system() | ||||
|         elite_ship.fuel = fuel | ||||
|     } | ||||
|  | ||||
|     sub jump_to_system() { | ||||
|         void textelite.next_input(input) | ||||
|         ubyte current_planet = elite_planet.number | ||||
|         ubyte x = elite_planet.x | ||||
|         ubyte y = elite_planet.y | ||||
|         if elite_galaxy.search_closest_planet(input) { | ||||
|             ubyte distance = elite_planet.distance(x, y) | ||||
|             if distance <= elite_ship.fuel { | ||||
|                 elite_galaxy.init_market_for_planet() | ||||
|                 elite_ship.fuel -= distance | ||||
|                 ;txt.print("\n\nHyperspace jump! Arrived at:\n") | ||||
|                 elite_planet.display(true,0 ) | ||||
|                 return | ||||
|             } | ||||
|             ;txt.print("\nInsufficient fuel\n") | ||||
|         } else { | ||||
|             ;txt.print(" Not found!\n") | ||||
|         } | ||||
|         elite_galaxy.travel_to(elite_galaxy.number, current_planet) | ||||
|     } | ||||
|  | ||||
|     sub do_buy() { | ||||
|         ;txt.print("\nBuy what commodity? ") | ||||
|         str commodity = "???????????????" | ||||
|         void textelite.next_input(commodity) | ||||
|         ubyte ci = elite_market.match(commodity) | ||||
|         if ci & 128 !=0 { | ||||
|             txt.print("Unknown\n") | ||||
|         } else { | ||||
|             ;txt.print("\nHow much? ") | ||||
|             void textelite.next_input(input) | ||||
|             ubyte amount = conv.str2ubyte(input) | ||||
|             if elite_market.current_quantity[ci] < amount { | ||||
|                 txt.print(" Insufficient supply!\n") | ||||
|             } else { | ||||
|                 uword price = elite_market.current_price[ci] * amount | ||||
|                 ;txt.print(" Total price: ") | ||||
|                 ;elite_util.print_10s(price) | ||||
|                 if price > elite_ship.cash { | ||||
|                     txt.print(" Not enough cash!\n") | ||||
|                 } else { | ||||
|                     elite_ship.cash -= price | ||||
|                     elite_ship.cargohold[ci] += amount | ||||
|                     elite_market.current_quantity[ci] -= amount | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub do_sell() { | ||||
|         ;txt.print("\nSell what commodity? ") | ||||
|         str commodity = "???????????????" | ||||
|         void textelite.next_input(commodity) | ||||
|         ubyte ci = elite_market.match(commodity) | ||||
|         if ci & 128 !=0 { | ||||
|             txt.print("Unknown\n") | ||||
|         } else { | ||||
|             ;txt.print("\nHow much? ") | ||||
|             void textelite.next_input(input) | ||||
|             ubyte amount = conv.str2ubyte(input) | ||||
|             if elite_ship.cargohold[ci] < amount { | ||||
|                 txt.print(" Insufficient supply!\n") | ||||
|             } else { | ||||
|                 uword price = elite_market.current_price[ci] * amount | ||||
|                 ;txt.print(" Total price: ") | ||||
|                 ;elite_util.print_10s(price) | ||||
|                 elite_ship.cash += price | ||||
|                 elite_ship.cargohold[ci] -= amount | ||||
|                 elite_market.current_quantity[ci] += amount | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub do_fuel() { | ||||
|         ;txt.print("\nBuy fuel. Amount? ") | ||||
|         void textelite.next_input(input) | ||||
|         ubyte buy_fuel = 10*conv.str2ubyte(input) | ||||
|         ubyte max_fuel = elite_ship.Max_fuel - elite_ship.fuel | ||||
|         if buy_fuel > max_fuel | ||||
|             buy_fuel = max_fuel | ||||
|         uword price = buy_fuel as uword * elite_ship.Fuel_cost | ||||
|         if price > elite_ship.cash { | ||||
|             txt.print("Not enough cash!\n") | ||||
|         } else { | ||||
|             elite_ship.cash -= price | ||||
|             elite_ship.fuel += buy_fuel | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub do_cash() { | ||||
|         ;txt.print("\nCheat! Set cash amount: ") | ||||
|         void textelite.next_input(input) | ||||
|         elite_ship.cash = conv.str2uword(input) | ||||
|     } | ||||
|  | ||||
|     sub do_hold() { | ||||
|         ;txt.print("\nCheat! Set cargohold size: ") | ||||
|         void textelite.next_input(input) | ||||
|         elite_ship.Max_cargo = conv.str2ubyte(input) | ||||
|     } | ||||
|  | ||||
|     sub do_next_galaxy() { | ||||
|         txt.print("\n>>>>> Galaxy Hyperjump!\n") | ||||
|         elite_galaxy.travel_to(elite_galaxy.number+1, elite_planet.number) | ||||
|         elite_planet.display(false, 0) | ||||
|     } | ||||
|  | ||||
|     sub do_info() { | ||||
|         ;txt.print("\nSystem name (empty=current): ") | ||||
|         num_chars = textelite.next_input(input) | ||||
|         if num_chars!=0 { | ||||
|             ubyte current_planet = elite_planet.number | ||||
|             ubyte x = elite_planet.x | ||||
|             ubyte y = elite_planet.y | ||||
|             if elite_galaxy.search_closest_planet(input) { | ||||
|                 ubyte distance = elite_planet.distance(x, y) | ||||
|                 elite_planet.display(false, distance) | ||||
|             } else { | ||||
|                 ;txt.print(" Not found!") | ||||
|             } | ||||
|             elite_galaxy.travel_to(elite_galaxy.number, current_planet) | ||||
|         } else { | ||||
|             elite_planet.display(false, 0) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub do_local() { | ||||
|         elite_galaxy.local_area() | ||||
|     } | ||||
|  | ||||
|     sub do_map() { | ||||
|         ;txt.print("\n(l)ocal or (g)alaxy starmap? ") | ||||
|         num_chars = textelite.next_input(input) | ||||
|         if num_chars!=0 { | ||||
|             elite_galaxy.starmap(input[0]=='l') | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub do_show_market() { | ||||
|         elite_market.display() | ||||
|         ;txt.print("\nFuel: ") | ||||
|         ;elite_util.print_10s(elite_ship.fuel) | ||||
|         ;txt.print("   Cargohold space: ") | ||||
|         ;txt.print_ub(elite_ship.cargo_free()) | ||||
|         ;txt.print("t\n") | ||||
|     } | ||||
| } | ||||
|  | ||||
| elite_ship { | ||||
|     const ubyte Max_fuel = 70 | ||||
|     const ubyte Fuel_cost = 2 | ||||
|     ubyte Max_cargo = 20 | ||||
|  | ||||
|     ubyte fuel | ||||
|     uword cash | ||||
|     ubyte[17] cargohold | ||||
|  | ||||
|     sub init() { | ||||
|         sys.memset(cargohold, len(cargohold), 0) | ||||
|         fuel = Max_fuel | ||||
|         cash = 1000 | ||||
|     } | ||||
| } | ||||
|  | ||||
| elite_market { | ||||
|     ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] | ||||
|     byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] | ||||
|     ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] | ||||
|     ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] | ||||
|     str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", | ||||
|                      "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] | ||||
|  | ||||
|     ubyte[17] current_quantity | ||||
|     uword[17] current_price | ||||
|  | ||||
|     sub init(ubyte fluct) { | ||||
|         ; Prices and availabilities are influenced by the planet's economy type | ||||
|         ; (0-7) and a random "fluctuation" byte that was kept within the saved | ||||
|         ; commander position to keep the market prices constant over gamesaves. | ||||
|         ; Availabilities must be saved with the game since the player alters them | ||||
|         ; by buying (and selling(?)) | ||||
|         ; | ||||
|         ; Almost all commands are one byte only and overflow "errors" are | ||||
|         ; extremely frequent and exploited. | ||||
|         ; | ||||
|         ; Trade Item prices are held internally in a single byte=true value/4. | ||||
|         ; The decimal point in prices is introduced only when printing them. | ||||
|         ; Internally, all prices are integers. | ||||
|         ; The player's cash is held in four bytes. | ||||
|         ubyte ci | ||||
|         for ci in 0 to len(names)-1 { | ||||
|             word product | ||||
|             byte changing | ||||
|             product = elite_planet.economy as word * gradients[ci] | ||||
|             changing = fluct & maskbytes[ci]  as byte | ||||
|             ubyte q = (basequants[ci] as word + changing - product) as ubyte | ||||
|             if q & $80 !=0 | ||||
|                 q = 0  ; clip to positive 8-bit | ||||
|             current_quantity[ci] = q & $3f | ||||
|             q = (baseprices[ci] + changing + product) as ubyte | ||||
|             current_price[ci] = q * $0004 | ||||
|         } | ||||
|         current_quantity[16] = 0        ; force nonavailability of Alien Items | ||||
|     } | ||||
|  | ||||
|     sub display() { | ||||
|         return | ||||
| ;        ubyte ci | ||||
| ;        txt.nl() | ||||
| ;        elite_planet.print_name_uppercase() | ||||
| ;        txt.print(" trade market:\n    COMMODITY / PRICE / AVAIL / IN HOLD\n") | ||||
| ;        for ci in 0 to len(names)-1 { | ||||
| ;            elite_util.print_right(13, names[ci]) | ||||
| ;            txt.print("   ") | ||||
| ;            elite_util.print_10s(current_price[ci]) | ||||
| ;            txt.column(24) | ||||
| ;            txt.print_ub(current_quantity[ci]) | ||||
| ;            txt.chrout(' ') | ||||
| ;            when units[ci] { | ||||
| ;                0 -> txt.chrout('t') | ||||
| ;                1 -> txt.print("kg") | ||||
| ;                2 -> txt.chrout('g') | ||||
| ;            } | ||||
| ;            txt.column(32) | ||||
| ;            txt.print_ub(elite_ship.cargohold[ci]) | ||||
| ;            txt.nl() | ||||
| ;        } | ||||
|     } | ||||
|  | ||||
|     sub match(uword nameptr) -> ubyte { | ||||
|         ubyte ci | ||||
|         for ci in 0 to len(names)-1 { | ||||
|             if elite_util.prefix_matches(nameptr, names[ci]) | ||||
|                 return ci | ||||
|         } | ||||
|         return 255 | ||||
|     } | ||||
| } | ||||
|  | ||||
| elite_galaxy { | ||||
|     const uword GALSIZE = 256 | ||||
|     const uword base0 = $5A4A       ; seeds for the first galaxy | ||||
|     const uword base1 = $0248 | ||||
|     const uword base2 = $B753 | ||||
|  | ||||
|     str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" | ||||
|  | ||||
|     ubyte number | ||||
|  | ||||
|     uword[3] seed | ||||
|  | ||||
|     sub init(ubyte galaxynum) { | ||||
|         number = 1 | ||||
|         elite_planet.number = 255 | ||||
|         seed[0] = base0 | ||||
|         seed[1] = base1 | ||||
|         seed[2] = base2 | ||||
|         repeat galaxynum-1 { | ||||
|             nextgalaxy() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub nextgalaxy() { | ||||
|         textelite.num_commands++ | ||||
|  | ||||
|         seed[0] = twist(seed[0]) | ||||
|         seed[1] = twist(seed[1]) | ||||
|         seed[2] = twist(seed[2]) | ||||
|         number++ | ||||
|         if number==9 | ||||
|             number = 1 | ||||
|     } | ||||
|  | ||||
|     sub travel_to(ubyte galaxynum, ubyte system) { | ||||
|         init(galaxynum) | ||||
|         generate_next_planet()   ; always at least planet 0  (separate to avoid repeat ubyte overflow) | ||||
|         repeat system { | ||||
|             generate_next_planet() | ||||
|             textelite.num_commands++ | ||||
|         } | ||||
|         elite_planet.name = make_current_planet_name() | ||||
|         init_market_for_planet() | ||||
|     } | ||||
|  | ||||
|     sub init_market_for_planet() { | ||||
|         elite_market.init(lsb(seed[0])+msb(seed[2])) | ||||
|     } | ||||
|  | ||||
|     sub search_closest_planet(uword nameptr) -> bool { | ||||
|         textelite.num_commands++ | ||||
|  | ||||
|         ubyte x = elite_planet.x | ||||
|         ubyte y = elite_planet.y | ||||
|         ubyte current_planet_num = elite_planet.number | ||||
|  | ||||
|         init(number) | ||||
|         bool found = false | ||||
|         ubyte current_closest_pi | ||||
|         ubyte current_distance = 127 | ||||
|         ubyte pi | ||||
|         for pi in 0 to 255 { | ||||
|             generate_next_planet() | ||||
|             elite_planet.name = make_current_planet_name() | ||||
|             if elite_util.prefix_matches(nameptr, elite_planet.name) { | ||||
|                 ubyte distance = elite_planet.distance(x, y) | ||||
|                 if distance < current_distance { | ||||
|                     current_distance = distance | ||||
|                     current_closest_pi = pi | ||||
|                     found = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if found | ||||
|             travel_to(number, current_closest_pi) | ||||
|         else | ||||
|             travel_to(number, current_planet_num) | ||||
|  | ||||
|         return found | ||||
|     } | ||||
|  | ||||
|     sub local_area() { | ||||
|         ubyte current_planet = elite_planet.number | ||||
|         ubyte px = elite_planet.x | ||||
|         ubyte py = elite_planet.y | ||||
|         ubyte pn = 0 | ||||
|  | ||||
|         init(number) | ||||
| ;        txt.print("\nGalaxy #") | ||||
| ;        txt.print_ub(number) | ||||
| ;        txt.print(" - systems in vicinity:\n") | ||||
|         do { | ||||
|             generate_next_planet() | ||||
|             ubyte distance = elite_planet.distance(px, py) | ||||
|             if distance <= elite_ship.Max_fuel { | ||||
| ;                if distance <= elite_ship.fuel | ||||
| ;                    txt.chrout('*') | ||||
| ;                else | ||||
| ;                    txt.chrout('-') | ||||
| ;                txt.spc() | ||||
|                 elite_planet.name = make_current_planet_name() | ||||
|                 elite_planet.display(true, distance) | ||||
|             } | ||||
|             pn++ | ||||
|         } until pn==0 | ||||
|  | ||||
|         travel_to(number, current_planet) | ||||
|     } | ||||
|  | ||||
|     sub starmap(bool local) { | ||||
|         ubyte current_planet = elite_planet.number | ||||
|         ubyte px = elite_planet.x | ||||
|         ubyte py = elite_planet.y | ||||
|         str current_name = "        "       ; 8 max | ||||
|         ubyte pn = 0 | ||||
|  | ||||
|         current_name = elite_planet.name | ||||
|         init(number) | ||||
| ;        txt.clear_screen() | ||||
| ;        txt.print("Galaxy #") | ||||
| ;        txt.print_ub(number) | ||||
| ;        if local | ||||
| ;            txt.print(" - local systems") | ||||
| ;        else | ||||
| ;            txt.print(" - galaxy") | ||||
| ;        txt.print(" starmap:\n") | ||||
|         ubyte max_distance = 255 | ||||
|         if local | ||||
|             max_distance = elite_ship.Max_fuel | ||||
|         ubyte home_sx | ||||
|         ubyte home_sy | ||||
|         ubyte home_distance | ||||
|  | ||||
|         do { | ||||
|             generate_next_planet() | ||||
|             ubyte distance = elite_planet.distance(px, py) | ||||
|             if distance <= max_distance { | ||||
|                 elite_planet.name = make_current_planet_name() | ||||
|                 elite_planet.name[0] = strings.upperchar(elite_planet.name[0]) | ||||
|                 uword tx = elite_planet.x | ||||
|                 uword ty = elite_planet.y | ||||
|                 if local { | ||||
|                     tx = tx + 24 - px | ||||
|                     ty = ty + 24 - py | ||||
|                 } | ||||
|                 ubyte sx = display_scale_x(tx) | ||||
|                 ubyte sy = display_scale_y(ty) | ||||
|                 ubyte char = '*' | ||||
|                 if elite_planet.number==current_planet | ||||
|                     char = '%' | ||||
|                 if local { | ||||
|                     print_planet_details(elite_planet.name, sx, sy, distance) | ||||
|                 } else if elite_planet.number==current_planet { | ||||
|                     home_distance = distance | ||||
|                     home_sx = sx | ||||
|                     home_sy = sy | ||||
|                 } | ||||
|                 ; txt.setchr(2+sx, 2+sy, char) | ||||
|             } | ||||
|             pn++ | ||||
|         } until pn==0 | ||||
|  | ||||
|         if not local | ||||
|             print_planet_details(current_name, home_sx, home_sy, home_distance) | ||||
|  | ||||
| ;        if local | ||||
| ;            txt.plot(0, display_scale_y(64) + 4) | ||||
| ;        else | ||||
| ;            txt.plot(0, display_scale_y(256) + 4 as ubyte) | ||||
|         travel_to(number, current_planet) | ||||
|  | ||||
|         sub print_planet_details(str name, ubyte screenx, ubyte screeny, ubyte d) { | ||||
|             return | ||||
| ;            txt.plot(2+screenx-2, 2+screeny+1) | ||||
| ;            txt.print(name) | ||||
| ;            if d!=0 { | ||||
| ;                txt.plot(2+screenx-2, 2+screeny+2) | ||||
| ;                elite_util.print_10s(d) | ||||
| ;                txt.print(" LY") | ||||
| ;            } | ||||
|         } | ||||
|  | ||||
|         sub display_scale_x(uword x) -> ubyte { | ||||
|             if local | ||||
|                 return x/2 as ubyte | ||||
|             return x/8 as ubyte | ||||
|         } | ||||
|  | ||||
|         sub display_scale_y(uword y) -> ubyte { | ||||
|             if local | ||||
|                 return y/4 as ubyte | ||||
|             return y/16 as ubyte | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ubyte pn_pair1 | ||||
|     ubyte pn_pair2 | ||||
|     ubyte pn_pair3 | ||||
|     ubyte pn_pair4 | ||||
|     bool longname | ||||
|  | ||||
|     sub generate_next_planet() { | ||||
|         determine_planet_properties() | ||||
|         longname = lsb(seed[0]) & 64 !=0 | ||||
|  | ||||
|         ; Always four iterations of random number | ||||
|         pn_pair1 = (msb(seed[2]) & 31) * 2 | ||||
|         tweakseed() | ||||
|         pn_pair2 = (msb(seed[2]) & 31) * 2 | ||||
|         tweakseed() | ||||
|         pn_pair3 = (msb(seed[2]) & 31) * 2 | ||||
|         tweakseed() | ||||
|         pn_pair4 = (msb(seed[2]) & 31) * 2 | ||||
|         tweakseed() | ||||
|     } | ||||
|  | ||||
|     sub make_current_planet_name() -> str { | ||||
|         ubyte ni = 0 | ||||
|         str name = "         "    ; max 8 | ||||
|  | ||||
|         if pn_pairs[pn_pair1] != '.' { | ||||
|             name[ni] = pn_pairs[pn_pair1] | ||||
|             ni++ | ||||
|         } | ||||
|         if pn_pairs[pn_pair1+1] != '.' { | ||||
|             name[ni] = pn_pairs[pn_pair1+1] | ||||
|             ni++ | ||||
|         } | ||||
|         if pn_pairs[pn_pair2] != '.' { | ||||
|             name[ni] = pn_pairs[pn_pair2] | ||||
|             ni++ | ||||
|         } | ||||
|         if pn_pairs[pn_pair2+1] != '.' { | ||||
|             name[ni] = pn_pairs[pn_pair2+1] | ||||
|             ni++ | ||||
|         } | ||||
|         if pn_pairs[pn_pair3] != '.' { | ||||
|             name[ni] = pn_pairs[pn_pair3] | ||||
|             ni++ | ||||
|         } | ||||
|         if pn_pairs[pn_pair3+1] != '.' { | ||||
|             name[ni] = pn_pairs[pn_pair3+1] | ||||
|             ni++ | ||||
|         } | ||||
|  | ||||
|         if longname { | ||||
|             if pn_pairs[pn_pair4] != '.' { | ||||
|                 name[ni] = pn_pairs[pn_pair4] | ||||
|                 ni++ | ||||
|             } | ||||
|             if pn_pairs[pn_pair4+1] != '.' { | ||||
|                 name[ni] = pn_pairs[pn_pair4+1] | ||||
|                 ni++ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         name[ni] = 0 | ||||
|         return name | ||||
|     } | ||||
|  | ||||
|     sub determine_planet_properties() { | ||||
|         ; create the planet's characteristics | ||||
|         elite_planet.number++ | ||||
|         elite_planet.x = msb(seed[1]) | ||||
|         elite_planet.y = msb(seed[0]) | ||||
|         elite_planet.govtype = lsb(seed[1]) >> 3 & 7  ; bits 3,4 &5 of w1 | ||||
|         elite_planet.economy = msb(seed[0]) & 7  ; bits 8,9 &A of w0 | ||||
|         if elite_planet.govtype <= 1 | ||||
|             elite_planet.economy = (elite_planet.economy | 2) | ||||
|         elite_planet.techlevel = (msb(seed[1]) & 3) + (elite_planet.economy ^ 7) | ||||
|         elite_planet.techlevel += elite_planet.govtype >> 1 | ||||
|         if elite_planet.govtype & 1 !=0 | ||||
|             elite_planet.techlevel++ | ||||
|         elite_planet.population = 4 * elite_planet.techlevel + elite_planet.economy | ||||
|         elite_planet.population += elite_planet.govtype + 1 | ||||
|         elite_planet.productivity = ((elite_planet.economy ^ 7) + 3) * (elite_planet.govtype + 4) | ||||
|         elite_planet.productivity *= elite_planet.population * 8 | ||||
|         ubyte seed2_msb = msb(seed[2]) | ||||
|         elite_planet.radius = mkword((seed2_msb & 15) + 11, elite_planet.x) | ||||
|         elite_planet.species_is_alien = lsb(seed[2]) & 128 !=0      ; bit 7 of w2_lo | ||||
|         if elite_planet.species_is_alien { | ||||
|             elite_planet.species_size = (seed2_msb >> 2) & 7      ; bits 2-4 of w2_hi | ||||
|             elite_planet.species_color = seed2_msb >> 5           ; bits 5-7 of w2_hi | ||||
|             elite_planet.species_look = (seed2_msb ^ msb(seed[1])) & 7   ;bits 0-2 of (w0_hi EOR w1_hi) | ||||
|             elite_planet.species_kind = (elite_planet.species_look + (seed2_msb & 3)) & 7      ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result | ||||
|         } | ||||
|  | ||||
|         elite_planet.goatsoup_seed[0] = lsb(seed[1]) | ||||
|         elite_planet.goatsoup_seed[1] = msb(seed[1]) | ||||
|         elite_planet.goatsoup_seed[2] = lsb(seed[2]) | ||||
|         elite_planet.goatsoup_seed[3] = seed2_msb | ||||
|     } | ||||
|  | ||||
|     sub tweakseed() { | ||||
|         uword temp = seed[0] + seed[1] + seed[2] | ||||
|         seed[0] = seed[1] | ||||
|         seed[1] = seed[2] | ||||
|         seed[2] = temp | ||||
|     } | ||||
|  | ||||
|     sub twist(uword x) -> uword { | ||||
|         ubyte xh = msb(x) | ||||
|         ubyte xl = lsb(x) | ||||
|         xh <<= 1        ; make sure carry flag is not used on first shift! | ||||
|         rol(xl) | ||||
|         return mkword(xh, xl) | ||||
|     } | ||||
| } | ||||
|  | ||||
| elite_planet { | ||||
|     str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"] | ||||
|     str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""] | ||||
|     str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"] | ||||
|     str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] | ||||
|     str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] | ||||
|     str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] | ||||
|     str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] | ||||
|     str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] | ||||
|     str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] | ||||
|     str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] | ||||
|     str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] | ||||
|     str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] | ||||
|     str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] | ||||
|     str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] | ||||
|     str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] | ||||
|     str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] | ||||
|     str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"] | ||||
|     str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"] | ||||
|     str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"] | ||||
|     str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] | ||||
|     str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] | ||||
|     str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] | ||||
|     str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] | ||||
|     str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] | ||||
|     str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] | ||||
|     str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] | ||||
|     str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"] | ||||
|     str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] | ||||
|     str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] | ||||
|     str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] | ||||
|     str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"] | ||||
|     str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] | ||||
|     str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] | ||||
|     str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] | ||||
|     str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] | ||||
|     str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] | ||||
|  | ||||
|     uword[] @shared wordlists = [ | ||||
|         words81, words82, words83, words84, words85, words86, words87, words88, | ||||
|         words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, | ||||
|         words91, words92, words93, words94, words95, words96, words97, words98, | ||||
|         words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, | ||||
|         wordsA1, wordsA2, wordsA3, wordsA4] | ||||
|  | ||||
|     str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" | ||||
|  | ||||
|     ubyte[4] goatsoup_rnd = [0, 0, 0, 0] | ||||
|     ubyte[4] goatsoup_seed = [0, 0, 0, 0] | ||||
|  | ||||
|     str name = "        "       ; 8 max | ||||
|     ubyte number                ; starts at 0 in new galaxy, then increases by 1 for each generated planet | ||||
|     ubyte x | ||||
|     ubyte y | ||||
|     ubyte economy | ||||
|     ubyte govtype | ||||
|     ubyte techlevel | ||||
|     ubyte population | ||||
|     uword productivity | ||||
|     uword radius | ||||
|     bool species_is_alien      ; otherwise "Human Colonials" | ||||
|     ubyte species_size | ||||
|     ubyte species_color | ||||
|     ubyte species_look | ||||
|     ubyte species_kind | ||||
|  | ||||
|     sub set_seed(uword s1, uword s2) { | ||||
|         goatsoup_seed[0] = lsb(s1) | ||||
|         goatsoup_seed[1] = msb(s1) | ||||
|         goatsoup_seed[2] = lsb(s2) | ||||
|         goatsoup_seed[3] = msb(s2) | ||||
|         reset_rnd() | ||||
|     } | ||||
|  | ||||
|     sub reset_rnd() { | ||||
|         goatsoup_rnd[0] = goatsoup_seed[0] | ||||
|         goatsoup_rnd[1] = goatsoup_seed[1] | ||||
|         goatsoup_rnd[2] = goatsoup_seed[2] | ||||
|         goatsoup_rnd[3] = goatsoup_seed[3] | ||||
|     } | ||||
|  | ||||
|     sub random_name() -> str { | ||||
|         ubyte ii | ||||
|         str randname = "        "       ; 8 chars max | ||||
|         ubyte nx = 0 | ||||
|         for ii in 0 to goatsoup_rnd_number() & 3 { | ||||
|             ubyte xx = goatsoup_rnd_number() & $3e | ||||
|             if pairs0[xx] != '.' { | ||||
|                 randname[nx] = pairs0[xx] | ||||
|                 nx++ | ||||
|             } | ||||
|             xx++ | ||||
|             if pairs0[xx] != '.' { | ||||
|                 randname[nx] = pairs0[xx] | ||||
|                 nx++ | ||||
|             } | ||||
|         } | ||||
|         randname[nx] = 0 | ||||
|         randname[0] = strings.upperchar(randname[0]) | ||||
|         return randname | ||||
|     } | ||||
|  | ||||
|     sub goatsoup_rnd_number() -> ubyte { | ||||
|         ubyte xx = goatsoup_rnd[0] * 2 | ||||
|         uword a = xx as uword + goatsoup_rnd[2] | ||||
|         if goatsoup_rnd[0] > 127 | ||||
|             a ++ | ||||
|         goatsoup_rnd[0] = lsb(a) | ||||
|         goatsoup_rnd[2] = xx | ||||
|         xx = goatsoup_rnd[1] | ||||
|         ubyte ac = xx + goatsoup_rnd[3] + msb(a) | ||||
|         goatsoup_rnd[1] = ac | ||||
|         goatsoup_rnd[3] = xx | ||||
|         return ac | ||||
|     } | ||||
|  | ||||
|     sub distance(ubyte px, ubyte py) -> ubyte { | ||||
|         uword ax | ||||
|         uword ay | ||||
|         if px>x | ||||
|             ax=px-x | ||||
|         else | ||||
|             ax=x-px | ||||
|         if py>y | ||||
|             ay=py-y | ||||
|         else | ||||
|             ay=y-py | ||||
|         ay /= 2 | ||||
|         ubyte d = sqrt(ax*ax + ay*ay) | ||||
|         if d>63 | ||||
|             return 255 | ||||
|         return d*4 | ||||
|     } | ||||
|  | ||||
|     sub soup() -> str { | ||||
|         str planet_result = " " * 160 | ||||
|         uword[6] source_stack | ||||
|         ubyte stack_ptr = 0 | ||||
|         str start_source = "\x8F is \x97." | ||||
|         uword source_ptr = &start_source | ||||
|         uword result_ptr = &planet_result | ||||
|  | ||||
|         reset_rnd() | ||||
|         recursive_soup() | ||||
|         return planet_result | ||||
|  | ||||
|         sub recursive_soup() { | ||||
|             repeat { | ||||
|                 ubyte c = @(source_ptr) | ||||
|                 source_ptr++ | ||||
|                 if c == $00 { | ||||
|                     @(result_ptr) = 0 | ||||
|                     return | ||||
|                 } | ||||
|                 else if c <= $80 { | ||||
|                     @(result_ptr) = c | ||||
|                     result_ptr++ | ||||
|                 } | ||||
|                 else { | ||||
|                     if c <= $a4 { | ||||
|                         ubyte rnr = goatsoup_rnd_number() | ||||
|                         ubyte wordNr = ((rnr >= $33) as ubyte) + ((rnr >= $66) as ubyte) + ((rnr >= $99) as ubyte) + ((rnr >= $CC) as ubyte) | ||||
|                         source_stack[stack_ptr] = source_ptr | ||||
|                         stack_ptr++ | ||||
|                         source_ptr = getword(c, wordNr) | ||||
|                         recursive_soup()    ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case | ||||
|                         stack_ptr-- | ||||
|                         source_ptr = source_stack[stack_ptr] | ||||
|                     } else { | ||||
|                         if c == $b0 { | ||||
|                             @(result_ptr) = strings.upperchar(name[0]) | ||||
|                             result_ptr++ | ||||
|                             concat_string(&name + 1) | ||||
|                         } | ||||
|                         else if c == $b1 { | ||||
|                             @(result_ptr) = strings.upperchar(name[0]) | ||||
|                             result_ptr++ | ||||
|                             ubyte ni | ||||
|                             for ni in 1 to len(name) { | ||||
|                                 ubyte cc = name[ni] | ||||
|                                 if cc in ['e', 'o', 0] | ||||
|                                     break | ||||
|                                 else { | ||||
|                                     @(result_ptr) = cc | ||||
|                                     result_ptr++ | ||||
|                                 } | ||||
|                             } | ||||
|                             @(result_ptr) = 'i' | ||||
|                             result_ptr++ | ||||
|                             @(result_ptr) = 'a' | ||||
|                             result_ptr++ | ||||
|                             @(result_ptr) = 'n' | ||||
|                             result_ptr++ | ||||
|                         } | ||||
|                         else if c == $b2 { | ||||
|                             concat_string(random_name()) | ||||
|                         } | ||||
|                         else { | ||||
|                             @(result_ptr) = c | ||||
|                             result_ptr++ | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         sub concat_string(uword str_ptr) { | ||||
|             repeat { | ||||
|                 ubyte c = @(str_ptr) | ||||
|                 if c==0 | ||||
|                     break | ||||
|                 else { | ||||
|                     @(result_ptr) = c | ||||
|                     str_ptr++ | ||||
|                     result_ptr++ | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sub display(bool compressed, ubyte distance) { | ||||
|         txt.print(soup()) | ||||
|         txt.nl() | ||||
|     } | ||||
|  | ||||
|     sub getword(ubyte listnum, ubyte wordidx) -> uword { | ||||
|         uword list = wordlists[listnum-$81] | ||||
|         return peekw(list + wordidx*2) | ||||
|     } | ||||
| } | ||||
|  | ||||
| elite_util { | ||||
|     sub prefix_matches(uword prefixptr, uword stringptr) -> bool { | ||||
|         repeat { | ||||
|             ubyte pc = @(prefixptr) | ||||
|             ubyte sc = @(stringptr) | ||||
|             if pc == 0 | ||||
|                 return true | ||||
|             ; to lowercase for case insensitive compare: | ||||
|             if strings.lowerchar(pc)!=strings.lowerchar(sc) | ||||
|                 return false | ||||
|             prefixptr++ | ||||
|             stringptr++ | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										118
									
								
								benchmark-program/benchmark.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								benchmark-program/benchmark.p8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
|  | ||||
| ;  This benchmark program is meant to check for regressions in the | ||||
| ;  Prog8 compiler's code-generator (performance wise). | ||||
| ; | ||||
| ;  As the X16 computer is a more or less fixed system, it's not very useful | ||||
| ;  to benchmark the computer itself with. | ||||
|  | ||||
|  | ||||
| %import textio | ||||
| %import b_adpcm | ||||
| %import b_circles | ||||
| %import b_3d | ||||
| %import b_life | ||||
| %import b_mandelbrot | ||||
| %import b_queens | ||||
| %import b_textelite | ||||
| %import b_maze | ||||
| %import b_sprites | ||||
| %import b_btree | ||||
|  | ||||
| %zeropage basicsafe | ||||
| %option no_sysinit | ||||
|  | ||||
|  | ||||
| main { | ||||
|  | ||||
|     str[20] benchmark_names | ||||
|     uword[20] benchmark_score | ||||
|  | ||||
|  | ||||
|     sub start() { | ||||
|         ubyte benchmark_number | ||||
|  | ||||
|         cx16.set_screen_mode(3) | ||||
|         txt.color2(1, 6) | ||||
|         txt.clear_screen() | ||||
|  | ||||
|         txt.print("\n\n\n  prog8 compiler benchmark tests.\n\n  benchmark version v12\n\n") | ||||
|         sys.wait(60) | ||||
|  | ||||
|         benchmark_number = 0 | ||||
|  | ||||
|         announce_benchmark("maze solver") | ||||
|         benchmark_score[benchmark_number]  = maze.bench(300) | ||||
|         benchmark_number++ | ||||
|  | ||||
|         announce_benchmark("n-queens") | ||||
|         benchmark_score[benchmark_number]  = queens.bench(300) | ||||
|         benchmark_number++ | ||||
|  | ||||
|         announce_benchmark("mandelbrot (floating point)") | ||||
|         benchmark_score[benchmark_number]  = mandelbrot.calc(400) | ||||
|         benchmark_number++ | ||||
|  | ||||
|         announce_benchmark("game of life") | ||||
|         benchmark_score[benchmark_number]  = life.benchmark(300) | ||||
|         benchmark_number++ | ||||
|  | ||||
|         announce_benchmark("3d model rotation") | ||||
|         benchmark_score[benchmark_number]  = rotate3d.benchmark(300) | ||||
|         benchmark_number++ | ||||
|  | ||||
|         announce_benchmark("adpcm audio decoding") | ||||
|         benchmark_score[benchmark_number]  = adpcm.decode_benchmark(300) | ||||
|         benchmark_number++ | ||||
|  | ||||
|         announce_benchmark("circles with gfx_lores") | ||||
|         benchmark_score[benchmark_number]  = circles.draw(false, 400) | ||||
|         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 | ||||
|  | ||||
|         cx16.set_screen_mode(3) | ||||
|         txt.uppercase() | ||||
|         txt.color2(1, 6) | ||||
|         uword total_score | ||||
|         benchmark_number = 0 | ||||
|         txt.print("\nscore benchmark (v12)\n\n") | ||||
|         do { | ||||
|             txt.spc() | ||||
|             txt.print_uw(benchmark_score[benchmark_number]) | ||||
|             txt.column(6) | ||||
|             txt.print(benchmark_names[benchmark_number]) | ||||
|             total_score += benchmark_score[benchmark_number] | ||||
|             txt.nl() | ||||
|             benchmark_number++ | ||||
|         } until benchmark_names[benchmark_number]==0 | ||||
|  | ||||
|         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 | ||||
|             cx16.set_screen_mode(3) | ||||
|             txt.uppercase() | ||||
|             txt.color2(1, 6) | ||||
|             txt.clear_screen() | ||||
|             txt.plot(4, 6) | ||||
|             txt.print(benchmark_names[benchmark_number]) | ||||
|             txt.nl() | ||||
|             sys.wait(60) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -1,10 +0,0 @@ | ||||
| plugins { | ||||
|     id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false | ||||
| } | ||||
|  | ||||
| allprojects { | ||||
|     repositories { | ||||
|         mavenLocal() | ||||
|         mavenCentral() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										38
									
								
								build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| import org.jetbrains.kotlin.gradle.dsl.JvmTarget | ||||
| import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode | ||||
| import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature | ||||
| import org.jetbrains.kotlin.gradle.dsl.KotlinVersion | ||||
|  | ||||
|  | ||||
| plugins { | ||||
|     kotlin("jvm") version "2.2.20" | ||||
| } | ||||
|  | ||||
| allprojects { | ||||
|     apply(plugin="kotlin") | ||||
|  | ||||
|     repositories { | ||||
|         mavenLocal() | ||||
|         mavenCentral() | ||||
|     } | ||||
|  | ||||
|     kotlin { | ||||
|         compilerOptions { | ||||
|             freeCompilerArgs = listOf("-Xwhen-guards") | ||||
|             jvmTarget = JvmTarget.JVM_11 | ||||
|             jvmDefault = JvmDefaultMode.NO_COMPATIBILITY | ||||
|             // languageVersion.set(KotlinVersion.KOTLIN_2_3) | ||||
|         } | ||||
|         sourceSets.all { | ||||
|             languageSettings { | ||||
|                 // enable language features like so: | ||||
|                 // enableLanguageFeature(LanguageFeature.WhenGuards.name) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     java { | ||||
|         targetCompatibility = JavaVersion.VERSION_11 | ||||
|         sourceCompatibility = JavaVersion.VERSION_11 | ||||
|     } | ||||
| } | ||||
| @@ -1,43 +0,0 @@ | ||||
|  | ||||
| plugins { | ||||
|     id 'java' | ||||
|     id 'application' | ||||
|     id "org.jetbrains.kotlin.jvm" | ||||
| } | ||||
|  | ||||
| java { | ||||
|     toolchain { | ||||
|         languageVersion = JavaLanguageVersion.of(javaVersion) | ||||
|     } | ||||
| } | ||||
|  | ||||
| compileKotlin { | ||||
|     kotlinOptions { | ||||
|         jvmTarget = javaVersion | ||||
|     } | ||||
| } | ||||
|  | ||||
| compileTestKotlin { | ||||
|     kotlinOptions { | ||||
|         jvmTarget = javaVersion | ||||
|     } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     // should have no dependencies to other modules | ||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" | ||||
|     implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" | ||||
| } | ||||
|  | ||||
| sourceSets { | ||||
|     main { | ||||
|         java { | ||||
|             srcDir "${project.projectDir}/src" | ||||
|         } | ||||
|         resources { | ||||
|             srcDir "${project.projectDir}/res" | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // note: there are no unit tests in this module! | ||||
							
								
								
									
										22
									
								
								codeCore/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								codeCore/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| plugins { | ||||
|     kotlin("jvm") | ||||
| } | ||||
|  | ||||
| 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.1.0") | ||||
| } | ||||
|  | ||||
| sourceSets { | ||||
|     main { | ||||
|         java { | ||||
|             srcDir("${project.projectDir}/src") | ||||
|         } | ||||
|         resources { | ||||
|             srcDir("${project.projectDir}/res") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // note: there are no unit tests in this module! | ||||
							
								
								
									
										32
									
								
								codeCore/src/prog8/code/Globals.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								codeCore/src/prog8/code/Globals.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| package prog8.code | ||||
|  | ||||
| import java.io.IOException | ||||
| import java.nio.file.Path | ||||
| import kotlin.io.path.absolute | ||||
|  | ||||
|  | ||||
| const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings" | ||||
|  | ||||
| val PROG8_CONTAINER_MODULES = arrayOf(INTERNED_STRINGS_MODULENAME)      // option to add more if needed one day | ||||
|  | ||||
| // all automatically generated labels everywhere need to have the same label name prefix: | ||||
| const val GENERATED_LABEL_PREFIX = "p8_label_gen_" | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Returns the absolute path of the given path, | ||||
|  * where links are replaced by the actual directories, | ||||
|  * and containing no redundant path elements. | ||||
|  * If the path doesn't refer to an existing directory or file on the file system, | ||||
|  * it is returned unchanged. | ||||
|  */ | ||||
| fun Path.sanitize(): Path { | ||||
|     return try { | ||||
|         this.toRealPath().normalize() | ||||
|     } catch (_: java.nio.file.NoSuchFileException) { | ||||
|         this.absolute().normalize() | ||||
|         //throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) } | ||||
|     } catch (iox: IOException) { | ||||
|         throw FileSystemException(this.toFile()).also { it.initCause(iox) } | ||||
|     } | ||||
| } | ||||
| @@ -1,256 +0,0 @@ | ||||
| package prog8.code | ||||
|  | ||||
| import prog8.code.ast.PtNode | ||||
| import prog8.code.ast.PtProgram | ||||
| import prog8.code.core.* | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Tree structure containing all symbol definitions in the program | ||||
|  * (blocks, subroutines, variables (all types), memoryslabs, and labels). | ||||
|  */ | ||||
| class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) { | ||||
|     /** | ||||
|      * The table as a flat mapping of scoped names to the StNode. | ||||
|      * This gives the fastest lookup possible (no need to traverse tree nodes) | ||||
|      */ | ||||
|  | ||||
|     private var cachedFlat: Map<String, StNode>? = null | ||||
|  | ||||
|     val flat: Map<String, StNode> get()  { | ||||
|         if(cachedFlat!=null) | ||||
|             return cachedFlat!! | ||||
|  | ||||
|         val result = mutableMapOf<String, StNode>() | ||||
|         fun collect(node: StNode) { | ||||
|             for(child in node.children) { | ||||
|                 result[child.value.scopedName] = child.value | ||||
|                 collect(child.value) | ||||
|             } | ||||
|         } | ||||
|         collect(this) | ||||
|         cachedFlat = result | ||||
|         return result | ||||
|     } | ||||
|  | ||||
|     fun resetCachedFlat() { | ||||
|         cachedFlat = null | ||||
|     } | ||||
|  | ||||
|     val allVariables: Collection<StStaticVariable> by lazy { | ||||
|         val vars = mutableListOf<StStaticVariable>() | ||||
|         fun collect(node: StNode) { | ||||
|             for(child in node.children) { | ||||
|                 if(child.value.type== StNodeType.STATICVAR) | ||||
|                     vars.add(child.value as StStaticVariable) | ||||
|                 else | ||||
|                     collect(child.value) | ||||
|             } | ||||
|         } | ||||
|         collect(this) | ||||
|         vars | ||||
|     } | ||||
|  | ||||
|     val allMemMappedVariables: Collection<StMemVar> by lazy { | ||||
|         val vars = mutableListOf<StMemVar>() | ||||
|         fun collect(node: StNode) { | ||||
|             for(child in node.children) { | ||||
|                 if(child.value.type== StNodeType.MEMVAR) | ||||
|                     vars.add(child.value as StMemVar) | ||||
|                 else | ||||
|                     collect(child.value) | ||||
|             } | ||||
|         } | ||||
|         collect(this) | ||||
|         vars | ||||
|     } | ||||
|  | ||||
|     val allMemorySlabs: Collection<StMemorySlab> by lazy { | ||||
|         val vars = mutableListOf<StMemorySlab>() | ||||
|         fun collect(node: StNode) { | ||||
|             for(child in node.children) { | ||||
|                 if(child.value.type== StNodeType.MEMORYSLAB) | ||||
|                     vars.add(child.value as StMemorySlab) | ||||
|                 else | ||||
|                     collect(child.value) | ||||
|             } | ||||
|         } | ||||
|         collect(this) | ||||
|         vars | ||||
|     } | ||||
|  | ||||
|     override fun lookup(scopedName: String) = flat[scopedName] | ||||
|  | ||||
|     fun getLength(name: String): Int? { | ||||
|         val node = flat[name] | ||||
|         return when(node) { | ||||
|             is StMemVar -> node.length | ||||
|             is StMemorySlab -> node.size.toInt() | ||||
|             is StStaticVariable -> node.length | ||||
|             else -> null | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| enum class StNodeType { | ||||
|     GLOBAL, | ||||
|     // MODULE,     // not used with current scoping rules | ||||
|     BLOCK, | ||||
|     SUBROUTINE, | ||||
|     ROMSUB, | ||||
|     LABEL, | ||||
|     STATICVAR, | ||||
|     MEMVAR, | ||||
|     CONSTANT, | ||||
|     BUILTINFUNC, | ||||
|     MEMORYSLAB | ||||
| } | ||||
|  | ||||
|  | ||||
| open class StNode(val name: String, | ||||
|                   val type: StNodeType, | ||||
|                   val astNode: PtNode, | ||||
|                   val children: MutableMap<String, StNode> = mutableMapOf() | ||||
| ) { | ||||
|  | ||||
|     lateinit var parent: StNode | ||||
|  | ||||
|     val scopedName: String by lazy { scopedNameList.joinToString(".") } | ||||
|  | ||||
|     open fun lookup(scopedName: String) = | ||||
|         lookup(scopedName.split('.')) | ||||
|  | ||||
|     fun lookupUnscopedOrElse(name: String, default: () -> StNode) = | ||||
|         lookupUnscoped(name) ?: default() | ||||
|  | ||||
|     fun lookupOrElse(scopedName: String, default: () -> StNode): StNode = | ||||
|         lookup(scopedName.split('.')) ?: default() | ||||
|  | ||||
|     fun lookupUnscoped(name: String): StNode? { | ||||
|         // first consider the builtin functions | ||||
|         var globalscope = this | ||||
|         while(globalscope.type!= StNodeType.GLOBAL) | ||||
|             globalscope = globalscope.parent | ||||
|         val globalNode = globalscope.children[name] | ||||
|         if(globalNode!=null && globalNode.type== StNodeType.BUILTINFUNC) | ||||
|             return globalNode | ||||
|  | ||||
|         // search for the unqualified name in the current scope or its parent scopes | ||||
|         var scope=this | ||||
|         while(true) { | ||||
|             val node = scope.children[name] | ||||
|             if(node!=null) | ||||
|                 return node | ||||
|             if(scope.type== StNodeType.GLOBAL) | ||||
|                 return null | ||||
|             else | ||||
|                 scope = scope.parent | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun add(child: StNode) { | ||||
|         children[child.name] = child | ||||
|         child.parent = this | ||||
|     } | ||||
|  | ||||
|     private val scopedNameList: List<String> by lazy { | ||||
|         if(type==StNodeType.GLOBAL) | ||||
|             emptyList() | ||||
|         else | ||||
|             parent.scopedNameList + name | ||||
|     } | ||||
|  | ||||
|     private fun lookup(scopedName: List<String>): StNode? { | ||||
|         // a scoped name refers to a name in another namespace, and always stars from the root. | ||||
|         var node = this | ||||
|         while(node.type!=StNodeType.GLOBAL) | ||||
|             node = node.parent | ||||
|  | ||||
|         for(name in scopedName) { | ||||
|             if(name in node.children) | ||||
|                 node = node.children.getValue(name) | ||||
|             else | ||||
|                 return null | ||||
|         } | ||||
|         return node | ||||
|     } | ||||
| } | ||||
|  | ||||
| class StStaticVariable(name: String, | ||||
|                        val dt: DataType, | ||||
|                        val onetimeInitializationNumericValue: Double?,      // regular (every-run-time) initialization is done via regular assignments | ||||
|                        val onetimeInitializationStringValue: StString?, | ||||
|                        val onetimeInitializationArrayValue: StArray?, | ||||
|                        val length: Int?,            // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte | ||||
|                        val zpwish: ZeropageWish,    // used in the variable allocator | ||||
|                        astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) { | ||||
|  | ||||
|     val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null | ||||
|  | ||||
|     init { | ||||
|         if(length!=null) { | ||||
|             require(onetimeInitializationNumericValue == null) | ||||
|             if(onetimeInitializationArrayValue!=null) | ||||
|                 require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length) | ||||
|         } | ||||
|         if(onetimeInitializationNumericValue!=null) { | ||||
|             require(dt in NumericDatatypes) | ||||
|         } | ||||
|         if(onetimeInitializationArrayValue!=null) { | ||||
|             require(dt in ArrayDatatypes) | ||||
|             require(length==onetimeInitializationArrayValue.size) | ||||
|         } | ||||
|         if(onetimeInitializationStringValue!=null) { | ||||
|             require(dt == DataType.STR) | ||||
|             require(length == onetimeInitializationStringValue.first.length+1) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) : | ||||
|     StNode(name, StNodeType.CONSTANT, astNode) | ||||
|  | ||||
|  | ||||
| class StMemVar(name: String, | ||||
|                val dt: DataType, | ||||
|                val address: UInt, | ||||
|                val length: Int?,             // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte | ||||
|                astNode: PtNode) : | ||||
|     StNode(name, StNodeType.MEMVAR, astNode) { | ||||
|  | ||||
|     init{ | ||||
|         if(dt in ArrayDatatypes || dt == DataType.STR) | ||||
|             require(length!=null) { "memory mapped array or string must have known length" } | ||||
|     } | ||||
| } | ||||
|  | ||||
| class StMemorySlab( | ||||
|     name: String, | ||||
|     val size: UInt, | ||||
|     val align: UInt, | ||||
|     astNode: PtNode | ||||
| ): | ||||
|     StNode(name, StNodeType.MEMORYSLAB, astNode) | ||||
|  | ||||
|  | ||||
| class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) : | ||||
|         StNode(name, StNodeType.SUBROUTINE, astNode) | ||||
|  | ||||
|  | ||||
| class StRomSub(name: String, | ||||
|                val address: UInt?,      // null in case of asmsub, specified in case of romsub | ||||
|                val parameters: List<StRomSubParameter>, | ||||
|                val returns: List<StRomSubParameter>, | ||||
|                astNode: PtNode) : | ||||
|     StNode(name, StNodeType.ROMSUB, astNode) | ||||
|  | ||||
|  | ||||
|  | ||||
| class StSubroutineParameter(val name: String, val type: DataType) | ||||
| class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType) | ||||
| class StArrayElement(val number: Double?, val addressOfSymbol: String?) | ||||
|  | ||||
| typealias StString = Pair<String, Encoding> | ||||
| typealias StArray = List<StArrayElement> | ||||
| @@ -1,172 +0,0 @@ | ||||
| package prog8.code.ast | ||||
|  | ||||
| import prog8.code.core.* | ||||
|  | ||||
| /** | ||||
|  * Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root), | ||||
|  * passing it as a String to the specified receiver function. | ||||
|  */ | ||||
| fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) { | ||||
|     fun type(dt: DataType) = "!${dt.name.lowercase()}!" | ||||
|     fun txt(node: PtNode): String { | ||||
|         return when(node) { | ||||
|             is PtAssignTarget -> "<target>" | ||||
|             is PtAssignment -> "<assign>" | ||||
|             is PtAugmentedAssign -> "<inplace-assign> ${node.operator}" | ||||
|             is PtBreakpoint -> "%breakpoint" | ||||
|             is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" | ||||
|             is PtAddressOf -> "&" | ||||
|             is PtArray -> "array len=${node.children.size} ${type(node.type)}" | ||||
|             is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}" | ||||
|             is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}" | ||||
|             is PtBuiltinFunctionCall -> { | ||||
|                 val str = if(node.void) "void " else "" | ||||
|                 str + node.name + "()" | ||||
|             } | ||||
|             is PtContainmentCheck -> "in" | ||||
|             is PtFunctionCall -> { | ||||
|                 val str = if(node.void) "void " else "" | ||||
|                 str + node.name + "()" | ||||
|             } | ||||
|             is PtIdentifier -> "${node.name} ${type(node.type)}" | ||||
|             is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}" | ||||
|             is PtMemoryByte -> "@()" | ||||
|             is PtNumber -> { | ||||
|                 val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex() | ||||
|                 "$numstr ${type(node.type)}" | ||||
|             } | ||||
|             is PtPrefix -> node.operator | ||||
|             is PtRange -> "<range>" | ||||
|             is PtString -> "\"${node.value.escape()}\"" | ||||
|             is PtTypeCast -> "as ${node.type.name.lowercase()}" | ||||
|             is PtForLoop -> "for" | ||||
|             is PtIfElse -> "ifelse" | ||||
|             is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}" | ||||
|             is PtInlineAssembly -> { | ||||
|                 if(node.isIR) | ||||
|                     "%ir {{ ...${node.assembly.length} characters... }}" | ||||
|                 else | ||||
|                     "%asm {{ ...${node.assembly.length} characters... }}" | ||||
|             } | ||||
|             is PtJump -> { | ||||
|                 if(node.identifier!=null) | ||||
|                     "goto ${node.identifier.name}" | ||||
|                 else if(node.address!=null) | ||||
|                     "goto ${node.address.toHex()}" | ||||
|                 else | ||||
|                     "???" | ||||
|             } | ||||
|             is PtAsmSub -> { | ||||
|                 val params = node.parameters.map { | ||||
|                                 val register = it.first.registerOrPair | ||||
|                                 val statusflag = it.first.statusflag | ||||
|                                 "${it.second.type} ${it.second.name} @${register ?: statusflag}" | ||||
|                             }.joinToString(", ") | ||||
|                 val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}" | ||||
|                 val returns = if (node.returns.isEmpty()) "" else { | ||||
|                     "-> ${node.returns.map { | ||||
|                         val register = it.first.registerOrPair | ||||
|                         val statusflag = it.first.statusflag | ||||
|                         "${it.second} @${register ?: statusflag}"} | ||||
|                         .joinToString(", ") | ||||
|                     }" | ||||
|                 } | ||||
|                 val str = if (node.inline) "inline " else "" | ||||
|                 if(node.address==null) { | ||||
|                     str + "asmsub ${node.name}($params) $clobbers $returns" | ||||
|                 } else { | ||||
|                     str + "romsub ${node.address.toHex()} = ${node.name}($params) $clobbers $returns" | ||||
|                 } | ||||
|             } | ||||
|             is PtBlock -> { | ||||
|                 val addr = if(node.options.address==null) "" else "@${node.options.address.toHex()}" | ||||
|                 val align = if(node.options.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.options.alignment}" | ||||
|                 "\nblock '${node.name}' $addr $align" | ||||
|             } | ||||
|             is PtConstant -> { | ||||
|                 val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString() | ||||
|                 "const ${node.type.name.lowercase()} ${node.name} = $value" | ||||
|             } | ||||
|             is PtLabel -> "${node.name}:" | ||||
|             is PtMemMapped -> { | ||||
|                 if(node.type in ArrayDatatypes) { | ||||
|                     val arraysize = if(node.arraySize==null) "" else node.arraySize.toString() | ||||
|                     val eltType = ArrayToElementTypes.getValue(node.type) | ||||
|                     "&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}" | ||||
|                 } else { | ||||
|                     "&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}" | ||||
|                 } | ||||
|             } | ||||
|             is PtSub -> { | ||||
|                 val params = node.parameters.map { "${it.type} ${it.name}" }.joinToString(", ") | ||||
|                 var str = "sub ${node.name}($params) " | ||||
|                 if(node.returntype!=null) | ||||
|                     str += "-> ${node.returntype.name.lowercase()}" | ||||
|                 str | ||||
|             } | ||||
|             is PtVariable -> { | ||||
|                 val split = if(node.type in SplitWordArrayTypes) "@split" else "" | ||||
|                 val str = if(node.arraySize!=null) { | ||||
|                     val eltType = ArrayToElementTypes.getValue(node.type) | ||||
|                     "${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}" | ||||
|                 } | ||||
|                 else if(node.type in ArrayDatatypes) { | ||||
|                     val eltType = ArrayToElementTypes.getValue(node.type) | ||||
|                     "${eltType.name.lowercase()}[] $split ${node.name}" | ||||
|                 } | ||||
|                 else | ||||
|                     "${node.type.name.lowercase()} ${node.name}" | ||||
|                 if(node.value!=null) | ||||
|                     str + " = " + txt(node.value) | ||||
|                 else | ||||
|                     str | ||||
|             } | ||||
|             is PtNodeGroup -> "<group>" | ||||
|             is PtNop -> "nop" | ||||
|             is PtPostIncrDecr -> "<post> ${node.operator}" | ||||
|             is PtProgram -> "PROGRAM ${node.name}" | ||||
|             is PtRepeatLoop -> "repeat" | ||||
|             is PtReturn -> "return" | ||||
|             is PtSubroutineParameter -> "${node.type.name.lowercase()} ${node.name}" | ||||
|             is PtWhen -> "when" | ||||
|             is PtWhenChoice -> { | ||||
|                 if(node.isElse) | ||||
|                     "else" | ||||
|                 else | ||||
|                     "->" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if(root is PtProgram) { | ||||
|         output(txt(root)) | ||||
|         root.children.forEach { | ||||
|             walkAst(it) { node, depth -> | ||||
|                 val txt = txt(node) | ||||
|                 val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true | ||||
|                 if(!library || !skipLibraries) { | ||||
|                     if (txt.isNotEmpty()) | ||||
|                         output("    ".repeat(depth) + txt(node)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         println() | ||||
|     } else { | ||||
|         walkAst(root) { node, depth -> | ||||
|             val txt = txt(node) | ||||
|             val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true | ||||
|             if(!library || !skipLibraries) { | ||||
|                 if (txt.isNotEmpty()) | ||||
|                     output("    ".repeat(depth) + txt(node)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) { | ||||
|     fun recurse(node: PtNode, depth: Int) { | ||||
|         act(node, depth) | ||||
|         node.children.forEach { recurse(it, depth+1) } | ||||
|     } | ||||
|     recurse(root, 0) | ||||
| } | ||||
| @@ -1,171 +0,0 @@ | ||||
| package prog8.code.ast | ||||
|  | ||||
| import prog8.code.core.* | ||||
|  | ||||
|  | ||||
| sealed interface IPtSubroutine { | ||||
|     val name: String | ||||
| } | ||||
|  | ||||
| class PtAsmSub( | ||||
|     name: String, | ||||
|     val address: UInt?, | ||||
|     val clobbers: Set<CpuRegister>, | ||||
|     val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>, | ||||
|     val returns: List<Pair<RegisterOrStatusflag, DataType>>, | ||||
|     val inline: Boolean, | ||||
|     position: Position | ||||
| ) : PtNamedNode(name, position), IPtSubroutine | ||||
|  | ||||
|  | ||||
| class PtSub( | ||||
|     name: String, | ||||
|     val parameters: List<PtSubroutineParameter>, | ||||
|     val returntype: DataType?, | ||||
|     position: Position | ||||
| ) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer { | ||||
|     init { | ||||
|         // params and return value should not be str | ||||
|         if(parameters.any{ it.type !in NumericDatatypes }) | ||||
|             throw AssemblyError("non-numeric parameter") | ||||
|         if(returntype!=null && returntype !in NumericDatatypes) | ||||
|             throw AssemblyError("non-numeric returntype $returntype") | ||||
|         parameters.forEach { it.parent=this } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position) | ||||
|  | ||||
|  | ||||
| sealed interface IPtAssignment { | ||||
|     val children: MutableList<PtNode> | ||||
|     val target: PtAssignTarget | ||||
|         get() = children[0] as PtAssignTarget | ||||
|     val value: PtExpression | ||||
|         get() = children[1] as PtExpression | ||||
| } | ||||
|  | ||||
| class PtAssignment(position: Position) : PtNode(position), IPtAssignment | ||||
|  | ||||
| class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment | ||||
|  | ||||
|  | ||||
| class PtAssignTarget(position: Position) : PtNode(position) { | ||||
|     val identifier: PtIdentifier? | ||||
|         get() = children.single() as? PtIdentifier | ||||
|     val array: PtArrayIndexer? | ||||
|         get() = children.single() as? PtArrayIndexer | ||||
|     val memory: PtMemoryByte? | ||||
|         get() = children.single() as? PtMemoryByte | ||||
|  | ||||
|     val type: DataType | ||||
|         get() { | ||||
|             return when(val tgt = children.single()) { | ||||
|                 is PtIdentifier -> tgt.type | ||||
|                 is PtArrayIndexer -> tgt.type | ||||
|                 is PtMemoryByte -> tgt.type | ||||
|                 else -> throw AssemblyError("weird target $tgt") | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this) | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) { | ||||
|     val trueScope: PtNodeGroup | ||||
|         get() = children[0] as PtNodeGroup | ||||
|     val falseScope: PtNodeGroup | ||||
|         get() = children[1] as PtNodeGroup | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtForLoop(position: Position) : PtNode(position) { | ||||
|     val variable: PtIdentifier | ||||
|         get() = children[0] as PtIdentifier | ||||
|     val iterable: PtExpression | ||||
|         get() = children[1] as PtExpression | ||||
|     val statements: PtNodeGroup | ||||
|         get() = children[2] as PtNodeGroup | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtIfElse(position: Position) : PtNode(position) { | ||||
|     val condition: PtExpression | ||||
|         get() = children[0] as PtExpression | ||||
|     val ifScope: PtNodeGroup | ||||
|         get() = children[1] as PtNodeGroup | ||||
|     val elseScope: PtNodeGroup | ||||
|         get() = children[2] as PtNodeGroup | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtJump(val identifier: PtIdentifier?,     // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position. | ||||
|              val address: UInt?, | ||||
|              position: Position) : PtNode(position) { | ||||
|     init { | ||||
|         identifier?.let {it.parent = this } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) { | ||||
|     val target: PtAssignTarget | ||||
|         get() = children.single() as PtAssignTarget | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtRepeatLoop(position: Position) : PtNode(position) { | ||||
|     val count: PtExpression | ||||
|         get() = children[0] as PtExpression | ||||
|     val statements: PtNodeGroup | ||||
|         get() = children[1] as PtNodeGroup | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtReturn(position: Position) : PtNode(position) { | ||||
|     val hasValue = children.any() | ||||
|     val value: PtExpression? | ||||
|         get() { | ||||
|             return if(children.any()) | ||||
|                 children.single() as PtExpression | ||||
|             else | ||||
|                 null | ||||
|         } | ||||
| } | ||||
|  | ||||
|  | ||||
| sealed interface IPtVariable { | ||||
|     val name: String | ||||
|     val type: DataType | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable { | ||||
|     init { | ||||
|         value?.let {it.parent=this} | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable | ||||
|  | ||||
|  | ||||
| class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable | ||||
|  | ||||
|  | ||||
| class PtWhen(position: Position) : PtNode(position) { | ||||
|     val value: PtExpression | ||||
|         get() = children[0] as PtExpression | ||||
|     val choices: PtNodeGroup | ||||
|         get() = children[1] as PtNodeGroup | ||||
| } | ||||
|  | ||||
|  | ||||
| class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) { | ||||
|     val values: PtNodeGroup | ||||
|         get() = children[0] as PtNodeGroup | ||||
|     val statements: PtNodeGroup | ||||
|         get() = children[1] as PtNodeGroup | ||||
| } | ||||
| @@ -1,7 +1,7 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean) | ||||
| class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean) | ||||
| class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?) | ||||
| class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean) | ||||
| class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) { | ||||
|     override fun toString(): String { | ||||
|         val paramConvs =  params.mapIndexed { index, it -> | ||||
| @@ -13,35 +13,43 @@ class CallConvention(val params: List<ParamConvention>, val returns: ReturnConve | ||||
|         } | ||||
|         val returnConv = | ||||
|             when { | ||||
|                 returns.reg!=null -> returns.reg.toString() | ||||
|                 returns.floatFac1 -> "floatFAC1" | ||||
|                 returns.reg == RegisterOrPair.FAC1 -> "floatFAC1" | ||||
|                 returns.reg != null -> returns.reg.toString() | ||||
|                 else -> "<no returnvalue>" | ||||
|             } | ||||
|         return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]" | ||||
|     } | ||||
| } | ||||
|  | ||||
| class FParam(val name: String, val possibleDatatypes: Array<DataType>) | ||||
| class FParam(val name: String, vararg val possibleDatatypes: BaseDataType) | ||||
|  | ||||
|  | ||||
| private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) | ||||
| private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG) | ||||
| private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) | ||||
|  | ||||
|  | ||||
| class FSignature(val pure: Boolean,      // does it have side effects? | ||||
|                       val parameters: List<FParam>, | ||||
|                       val returnType: DataType?) { | ||||
|                  val returnType: BaseDataType?, | ||||
|                  vararg val parameters: FParam) { | ||||
|  | ||||
|     fun callConvention(actualParamTypes: List<DataType>): CallConvention { | ||||
|     fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention { | ||||
|         val returns: ReturnConvention = when (returnType) { | ||||
|             DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false) | ||||
|             DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false) | ||||
|             DataType.FLOAT -> ReturnConvention(returnType, null, true) | ||||
|             in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false) | ||||
|             null -> ReturnConvention(null, null, false) | ||||
|             BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A) | ||||
|             BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY) | ||||
|             BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15_32) | ||||
|             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()) { | ||||
|                     DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false) | ||||
|                     DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false) | ||||
|                     DataType.FLOAT -> ReturnConvention(paramType, null, true) | ||||
|                     in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false) | ||||
|                     else -> ReturnConvention(paramType, null, false) | ||||
|                     BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A) | ||||
|                     BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY) | ||||
|                     BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15_32) | ||||
|                     BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1) | ||||
|                     in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY) | ||||
|                     else -> ReturnConvention(paramType, null) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -49,16 +57,28 @@ class FSignature(val pure: Boolean,      // does it have side effects? | ||||
|         return when { | ||||
|             actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns) | ||||
|             actualParamTypes.size==1 -> { | ||||
|                 // one parameter goes via register/registerpair | ||||
|                 // One parameter goes via register/registerpair. | ||||
|                 // 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]) { | ||||
|                     DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false) | ||||
|                     DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false) | ||||
|                     DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) | ||||
|                     in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false) | ||||
|                     BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false) | ||||
|                     BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false) | ||||
|                     BaseDataType.LONG -> ParamConvention(paramType, RegisterOrPair.R14R15_32, false) | ||||
|                     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) | ||||
|                 } | ||||
|                 CallConvention(listOf(paramConv), returns) | ||||
|             } | ||||
|             actualParamTypes.size==2 && (actualParamTypes[0].isByte && actualParamTypes[1].isWord) -> { | ||||
|                 TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet") | ||||
|             } | ||||
|             actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> { | ||||
|                 TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet") | ||||
|             } | ||||
|             actualParamTypes.size==3 && actualParamTypes.all { it.isByte } -> { | ||||
|                 TODO("opportunity to pass 3 byte arguments in A,Y and X registers but not implemented yet") | ||||
|             } | ||||
|             else -> { | ||||
|                 // multiple parameters go via variables | ||||
|                 val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) } | ||||
| @@ -69,67 +89,82 @@ class FSignature(val pure: Boolean,      // does it have side effects? | ||||
| } | ||||
|  | ||||
| val BuiltinFunctions: Map<String, FSignature> = mapOf( | ||||
|     // this set of function have no return value and operate in-place: | ||||
|     "setlsb"    to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null), | ||||
|     "setmsb"    to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null), | ||||
|     "rol"       to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||
|     "ror"       to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||
|     "rol2"      to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||
|     "ror2"      to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||
|     "sort"      to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), | ||||
|     "reverse"   to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), | ||||
|     // cmp returns a status in the carry flag, but not a proper return value | ||||
|     "cmp"       to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), | ||||
|     "prog8_lib_stringcompare"     to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), | ||||
|     "prog8_lib_square_byte"       to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE), | ||||
|     "prog8_lib_square_word"       to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD), | ||||
|     "abs"       to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null), | ||||
|     "abs__byte"  to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE), | ||||
|     "abs__word"  to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD), | ||||
|     "abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT), | ||||
|     "len"       to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), | ||||
|     // normal functions follow: | ||||
|     "sizeof"    to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE), | ||||
|     "sgn"       to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE), | ||||
|     "sqrt"        to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null), | ||||
|     "sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), | ||||
|     "sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE), | ||||
|     "sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT), | ||||
|     "divmod"           to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||
|     "divmod__ubyte"    to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), | ||||
|     "divmod__uword"    to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), | ||||
|     "any"       to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), | ||||
|     "all"       to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), | ||||
|     "lsb"       to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), | ||||
|     "msb"       to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), | ||||
|     "mkword"    to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD), | ||||
|     "clamp"     to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null), | ||||
|     "clamp__byte"  to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE), | ||||
|     "clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE), | ||||
|     "clamp__word"  to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD), | ||||
|     "clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||
|     "min"        to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null), | ||||
|     "min__byte"  to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE), | ||||
|     "min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE), | ||||
|     "min__word"  to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD), | ||||
|     "min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||
|     "max"        to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null), | ||||
|     "max__byte"  to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE), | ||||
|     "max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE), | ||||
|     "max__word"  to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD), | ||||
|     "max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||
|     "peek"      to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), | ||||
|     "peekw"     to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||
|     "peekf"     to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT), | ||||
|     "poke"      to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), | ||||
|     "pokew"     to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), | ||||
|     "pokef"     to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null), | ||||
|     "pokemon"   to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), | ||||
|     "rsave"     to FSignature(false, emptyList(), null), | ||||
|     "rrestore"  to FSignature(false, emptyList(), null), | ||||
|     "memory"    to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||
|     "callfar"   to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||
|     "call"      to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||
|     "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, BaseDataType.LONG)), | ||||
|     "ror"     to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)), | ||||
|     "rol2"    to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)), | ||||
|     "ror2"    to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)), | ||||
|     "cmp"     to FSignature(false, null, 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, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)), | ||||
|     "prog8_lib_square_byte"       to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)), | ||||
|     "prog8_lib_square_word"       to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)), | ||||
|     "prog8_lib_structalloc"       to FSignature(true, 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__long"     to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG)), | ||||
|     "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 - BaseDataType.STRUCT_INSTANCE).toTypedArray())), | ||||
|     "offsetof"      to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)), | ||||
|     "sgn"           to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)), | ||||
|     "sqrt"          to FSignature(true, null, FParam("value", *NumericDatatypes)), | ||||
|     "sqrt__ubyte"   to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)), | ||||
|     "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)), | ||||
|     "lsb__long"     to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.LONG)), | ||||
|     "msb"           to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)), | ||||
|     "msb__long"     to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.LONG)), | ||||
|     "lsw"           to FSignature(true, BaseDataType.UWORD, 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)), | ||||
|     "mklong"        to FSignature(true, BaseDataType.LONG, FParam("msb", BaseDataType.UBYTE), FParam("b2", BaseDataType.UBYTE), FParam("b1", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)), | ||||
|     "mklong2"       to FSignature(true, BaseDataType.LONG, FParam("msw", BaseDataType.UWORD), FParam("lsw", BaseDataType.UWORD)), | ||||
|     "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)), | ||||
|     "clamp__long"   to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG), FParam("minimum", BaseDataType.LONG), FParam("maximum", BaseDataType.LONG)), | ||||
|     "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)), | ||||
|     "min__long"     to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)), | ||||
|     "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)), | ||||
|     "max__long"     to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)), | ||||
|     "peek"          to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)), | ||||
|     "peekbool"      to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)), | ||||
|     "peekw"         to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)), | ||||
|     "peekl"         to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)), | ||||
|     "peekf"         to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)), | ||||
|     "poke"          to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE, BaseDataType.BYTE)), | ||||
|     "pokebool"      to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)), | ||||
|     "pokebowl"      to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)), | ||||
|     "pokew"         to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD, BaseDataType.WORD)), | ||||
|     "pokel"         to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)), | ||||
|     "pokef"         to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)), | ||||
|     "pokemon"       to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)), | ||||
|     "rsave"         to FSignature(false, null), | ||||
|     "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)), | ||||
| ) | ||||
|  | ||||
| val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse") | ||||
| val InplaceModifyingBuiltinFunctions = setOf( | ||||
|     "setlsb", "setmsb", | ||||
|     "rol", "ror", "rol2", "ror2", | ||||
|     "divmod", "divmod__ubyte", "divmod__uword" | ||||
| ) | ||||
|   | ||||
| @@ -11,26 +11,32 @@ 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 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 splitWordArrays: Boolean = false, | ||||
|                          var breakpointCpuInstruction: Boolean = false, | ||||
|                          var breakpointCpuInstruction: String? = null, | ||||
|                          var ignoreFootguns: Boolean = false, | ||||
|                          var outputDir: Path = Path(""), | ||||
|                          var quiet: Boolean = false, | ||||
|                          var symbolDefs: Map<String, String> = emptyMap() | ||||
| ) { | ||||
|     init { | ||||
|         compTarget.machine.initializeMemoryAreas(this) | ||||
|         compTarget.initializeMemoryAreas(this) | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|   | ||||
| @@ -1,20 +1,26 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| import kotlin.math.abs | ||||
| import kotlin.math.pow | ||||
|  | ||||
| val powersOfTwoFloat = (0..16).map { (2.0).pow(it) }.toTypedArray() | ||||
| val negativePowersOfTwoFloat = powersOfTwoFloat.map { -it }.toTypedArray() | ||||
| val powersOfTwoInt = (0..16).map { 2.0.pow(it).toInt() }.toTypedArray() | ||||
|  | ||||
| fun Number.toHex(): String { | ||||
|     //  0..15 -> "0".."15" | ||||
|     //  16..255 -> "$10".."$ff" | ||||
|     //  256..65536 -> "$0100".."$ffff" | ||||
|     //  larger -> "$12345678" | ||||
|     // negative values are prefixed with '-'. | ||||
|     val integer = this.toInt() | ||||
|     val integer = this.toLong() | ||||
|     if(integer<0) | ||||
|         return '-' + abs(integer).toHex() | ||||
|     return when (integer) { | ||||
|         in 0 until 16 -> integer.toString() | ||||
|         in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0') | ||||
|         in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0') | ||||
|         else -> throw IllegalArgumentException("number too large for 16 bits $this") | ||||
|         else -> "$"+integer.toString(16).padStart(8,'0') | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -22,11 +28,12 @@ fun UInt.toHex(): String { | ||||
|     //  0..15 -> "0".."15" | ||||
|     //  16..255 -> "$10".."$ff" | ||||
|     //  256..65536 -> "$0100".."$ffff" | ||||
|     //  larger -> "$12345678" | ||||
|     return when (this) { | ||||
|         in 0u until 16u -> this.toString() | ||||
|         in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0') | ||||
|         in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0') | ||||
|         else -> throw IllegalArgumentException("number too large for 16 bits $this") | ||||
|         else -> "$"+this.toString(16).padStart(8,'0') | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -58,6 +65,7 @@ fun String.unescape(): String { | ||||
|                 '\\' -> '\\' | ||||
|                 'n' -> '\n' | ||||
|                 'r' -> '\r' | ||||
|                 't' -> '\t' | ||||
|                 '"' -> '"' | ||||
|                 '\'' -> '\'' | ||||
|                 'u' -> { | ||||
|   | ||||
| @@ -1,63 +1,388 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| enum class DataType { | ||||
| import java.util.* | ||||
|  | ||||
| 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 a 8 bit byte | ||||
|     BOOL,               // pass by value            bit 0 of an 8-bit byte | ||||
|     STR,                // pass by reference | ||||
|     ARRAY_UB,           // pass by reference | ||||
|     ARRAY_B,            // pass by reference | ||||
|     ARRAY_UW,           // pass by reference | ||||
|     ARRAY_UW_SPLIT,     // pass by reference, lo/hi byte split | ||||
|     ARRAY_W,            // pass by reference | ||||
|     ARRAY_W_SPLIT,      // pass by reference, lo/hi byte split | ||||
|     ARRAY_F,            // pass by reference | ||||
|     ARRAY_BOOL,         // pass by reference | ||||
|     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; | ||||
|  | ||||
|     /** | ||||
|      * is the type assignable to the given other type (perhaps via a typecast) without loss of precision? | ||||
|      */ | ||||
|     infix fun isAssignableTo(targetType: DataType) = | ||||
|         when(this) { | ||||
|             BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, LONG, FLOAT) | ||||
|             UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT, BOOL) | ||||
|             BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT) | ||||
|             UWORD -> targetType.oneOf(UWORD, LONG, FLOAT) | ||||
|             WORD -> targetType.oneOf(WORD, LONG, FLOAT) | ||||
|             LONG -> targetType.oneOf(LONG, FLOAT) | ||||
|             FLOAT -> targetType.oneOf(FLOAT) | ||||
|             STR -> targetType.oneOf(STR, UWORD) | ||||
|             in ArrayDatatypes -> targetType == this | ||||
|             else -> false | ||||
|         } | ||||
|  | ||||
|     fun oneOf(vararg types: DataType) = this in types | ||||
|  | ||||
|     infix fun largerThan(other: DataType) = | ||||
|     fun largerSizeThan(other: BaseDataType) = | ||||
|         when { | ||||
|             this == other -> false | ||||
|             this in ByteDatatypes -> false | ||||
|             this in WordDatatypes -> other in ByteDatatypes | ||||
|             this == LONG -> other in ByteDatatypes+WordDatatypes | ||||
|             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 | ||||
|             this.isPointer -> other.isByteOrBool | ||||
|             else -> true | ||||
|         } | ||||
|  | ||||
|     infix fun equalsSize(other: DataType) = | ||||
|     fun equalsSize(other: BaseDataType) = | ||||
|         when { | ||||
|             this == other -> true | ||||
|             this in ByteDatatypes -> other in ByteDatatypes | ||||
|             this in WordDatatypes -> other in WordDatatypes | ||||
|             this== STR && other== UWORD || this== UWORD && other== STR -> true | ||||
|             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 | ||||
|         } | ||||
| } | ||||
|  | ||||
| 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.isLong get() = this == BaseDataType.LONG | ||||
| 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.isWordOrByteOrBool get() = this in arrayOf(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 arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) | ||||
| val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER | ||||
| val BaseDataType.isPointer get() = this == BaseDataType.POINTER | ||||
| val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE | ||||
| val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER | ||||
| val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER       // pointer arrays are also always stored as split uwords | ||||
| val BaseDataType.isIterable get() =  this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER) | ||||
| val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer | ||||
| val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer | ||||
|  | ||||
|  | ||||
| interface ISubType { | ||||
|     val scopedNameString: String | ||||
|     fun memsize(sizer: IMemSizer): Int | ||||
|     fun sameas(other: ISubType): Boolean | ||||
|     fun getFieldType(name: String): DataType? | ||||
| } | ||||
|  | ||||
| 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 { | ||||
|  | ||||
|         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) | ||||
|     } | ||||
|  | ||||
|  | ||||
|     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) | ||||
|         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") | ||||
|     } | ||||
|  | ||||
|     // 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.POINTER, BaseDataType.ARRAY_POINTER) | ||||
|             BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) | ||||
|             BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT) | ||||
|             BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT) | ||||
|             BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE) | ||||
|             BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub | ||||
|             BaseDataType.POINTER -> { | ||||
|                 when { | ||||
|                     targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true | ||||
|                     targetType.isPointer -> this.isUnsignedWord || this == targetType | ||||
|                     else -> false | ||||
|                 } | ||||
|             } | ||||
|             BaseDataType.STRUCT_INSTANCE -> false        // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it) | ||||
|             BaseDataType.ARRAY_POINTER -> false | ||||
|             BaseDataType.UNDEFINED -> false | ||||
|         } | ||||
|  | ||||
|     fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base) | ||||
|     fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base) | ||||
|  | ||||
|     // note: for pointer types, size() doesn't return the size of the pointer itself but the size of the thing it points to | ||||
|     fun size(memsizer: IMemSizer): Int = if(sub!=null) { | ||||
|             memsizer.memorySize(sub) | ||||
|         } else if(subType!=null) { | ||||
|             subType!!.memsize(memsizer) | ||||
|         } else { | ||||
|             memsizer.memorySize(base) | ||||
|         } | ||||
|  | ||||
|     val isBasic = sub==null && subType==null && subTypeFromAntlr==null | ||||
|     val isUndefined = base == BaseDataType.UNDEFINED | ||||
|     val isByte = base.isByte | ||||
|     val isUnsignedByte = base == BaseDataType.UBYTE | ||||
|     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 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 | ||||
| } | ||||
|  | ||||
|  | ||||
| enum class CpuRegister { | ||||
|     A, | ||||
|     X, | ||||
| @@ -75,10 +400,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_32, R2R3_32, R4R5_32, R6R7_32, R8R9_32, R10R11_32, R12R13_32, R14R15_32; | ||||
|  | ||||
|     companion object { | ||||
|         val names by lazy { values().map { it.toString()} } | ||||
|         val names by lazy { entries.map { it.toString()} } | ||||
|         fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair { | ||||
|             return when(cpu) { | ||||
|                 CpuRegister.A -> A | ||||
| @@ -88,6 +415,18 @@ enum class RegisterOrPair { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun startregname() = when(this) { | ||||
|         R0R1_32 -> "r0" | ||||
|         R2R3_32 -> "r2" | ||||
|         R4R5_32 -> "r4" | ||||
|         R6R7_32 -> "r6" | ||||
|         R8R9_32 -> "r8" | ||||
|         R10R11_32 -> "r10" | ||||
|         R12R13_32 -> "r12" | ||||
|         R14R15_32 -> "r14" | ||||
|         else -> throw IllegalArgumentException("must be a combined virtual register $this") | ||||
|     } | ||||
|  | ||||
|     fun asCpuRegister(): CpuRegister = when(this) { | ||||
|         A -> CpuRegister.A | ||||
|         X -> CpuRegister.X | ||||
| @@ -95,6 +434,21 @@ enum class RegisterOrPair { | ||||
|         else -> throw IllegalArgumentException("no cpu hardware register for $this") | ||||
|     } | ||||
|  | ||||
|     fun asScopedNameVirtualReg(type: DataType?): List<String> { | ||||
|         require(this in Cx16VirtualRegisters) | ||||
|         val suffix = when(type?.base) { | ||||
|             BaseDataType.UBYTE, BaseDataType.BOOL -> "L" | ||||
|             BaseDataType.BYTE -> "sL" | ||||
|             BaseDataType.WORD -> "s" | ||||
|             BaseDataType.UWORD, BaseDataType.POINTER, null -> "" | ||||
|             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 { | ||||
| @@ -104,7 +458,7 @@ enum class Statusflag { | ||||
|     Pn;     // don't use | ||||
|  | ||||
|     companion object { | ||||
|         val names by lazy { values().map { it.toString()} } | ||||
|         val names by lazy { entries.map { it.toString()} } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -123,47 +477,6 @@ enum class BranchCondition { | ||||
|     VC | ||||
| } | ||||
|  | ||||
|  | ||||
| val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL) | ||||
| val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD) | ||||
| val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG) | ||||
| val IntegerDatatypes = IntegerDatatypesNoBool + DataType.BOOL | ||||
| val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT) | ||||
| val NumericDatatypes = NumericDatatypesNoBool + DataType.BOOL | ||||
| val SignedDatatypes =  arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT) | ||||
| val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL) | ||||
| val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD) | ||||
| val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT) | ||||
| val IterableDatatypes = arrayOf( | ||||
|     DataType.STR, | ||||
|     DataType.ARRAY_UB, DataType.ARRAY_B, | ||||
|     DataType.ARRAY_UW, DataType.ARRAY_W, | ||||
|     DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT, | ||||
|     DataType.ARRAY_F, DataType.ARRAY_BOOL | ||||
| ) | ||||
| val PassByValueDatatypes = NumericDatatypes | ||||
| val PassByReferenceDatatypes = IterableDatatypes | ||||
| val ArrayToElementTypes = mapOf( | ||||
|     DataType.STR to DataType.UBYTE, | ||||
|     DataType.ARRAY_B to DataType.BYTE, | ||||
|     DataType.ARRAY_UB to DataType.UBYTE, | ||||
|     DataType.ARRAY_W to DataType.WORD, | ||||
|     DataType.ARRAY_UW to DataType.UWORD, | ||||
|     DataType.ARRAY_W_SPLIT to DataType.WORD, | ||||
|     DataType.ARRAY_UW_SPLIT to DataType.UWORD, | ||||
|     DataType.ARRAY_F to DataType.FLOAT, | ||||
|     DataType.ARRAY_BOOL to DataType.BOOL | ||||
| ) | ||||
| val ElementToArrayTypes = mapOf( | ||||
|     DataType.BYTE to DataType.ARRAY_B, | ||||
|     DataType.UBYTE to DataType.ARRAY_UB, | ||||
|     DataType.WORD to DataType.ARRAY_W, | ||||
|     DataType.UWORD to DataType.ARRAY_UW, | ||||
|     DataType.FLOAT to DataType.ARRAY_F, | ||||
|     DataType.BOOL to DataType.ARRAY_BOOL, | ||||
|     DataType.STR to DataType.ARRAY_UW          // array of str is just an array of pointers | ||||
| ) | ||||
|  | ||||
| val Cx16VirtualRegisters = arrayOf( | ||||
|     RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3, | ||||
|     RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7, | ||||
| @@ -171,7 +484,18 @@ val Cx16VirtualRegisters = arrayOf( | ||||
|     RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15 | ||||
| ) | ||||
|  | ||||
| val CpuRegisters = setOf( | ||||
| val combinedLongRegisters = arrayOf( | ||||
|     RegisterOrPair.R0R1_32, | ||||
|     RegisterOrPair.R2R3_32, | ||||
|     RegisterOrPair.R4R5_32, | ||||
|     RegisterOrPair.R6R7_32, | ||||
|     RegisterOrPair.R8R9_32, | ||||
|     RegisterOrPair.R10R11_32, | ||||
|     RegisterOrPair.R12R13_32, | ||||
|     RegisterOrPair.R14R15_32 | ||||
| ) | ||||
|  | ||||
| val CpuRegisters = arrayOf( | ||||
|     RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y, | ||||
|     RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY | ||||
| ) | ||||
| @@ -180,7 +504,8 @@ val CpuRegisters = setOf( | ||||
| enum class OutputType { | ||||
|     RAW, | ||||
|     PRG, | ||||
|     XEX | ||||
|     XEX, | ||||
|     LIBRARY | ||||
| } | ||||
|  | ||||
| enum class CbmPrgLauncherType { | ||||
| @@ -202,3 +527,8 @@ enum class ZeropageWish { | ||||
|     DONTCARE, | ||||
|     NOT_IN_ZEROPAGE | ||||
| } | ||||
|  | ||||
| enum class SplitWish { | ||||
|     DONTCARE, | ||||
|     NOSPLIT | ||||
| } | ||||
| @@ -1,8 +1,43 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| import java.nio.file.Path | ||||
|  | ||||
| enum class CpuType { | ||||
|     CPU6502, | ||||
|     CPU65C02, | ||||
|     VIRTUAL | ||||
| } | ||||
|  | ||||
| interface ICompilationTarget: IStringEncoding, IMemSizer { | ||||
|     val name: String | ||||
|     val machine: IMachineDefinition | ||||
|  | ||||
|     val FLOAT_MAX_NEGATIVE: Double | ||||
|     val FLOAT_MAX_POSITIVE: Double | ||||
|     val FLOAT_MEM_SIZE: Int | ||||
|     val STARTUP_CODE_RESERVED_SIZE: UInt        // this is here, so that certain compiler targets are able to tune this | ||||
|     val PROGRAM_LOAD_ADDRESS : UInt | ||||
|     val PROGRAM_MEMTOP_ADDRESS: UInt | ||||
|     val BSSHIGHRAM_START: UInt | ||||
|     val BSSHIGHRAM_END: UInt | ||||
|     val BSSGOLDENRAM_START: UInt | ||||
|     val BSSGOLDENRAM_END: UInt | ||||
|  | ||||
|     val cpu: CpuType | ||||
|     var zeropage: Zeropage | ||||
|     var golden: GoldenRam | ||||
|     val libraryPath: Path? | ||||
|     val customLauncher: List<String> | ||||
|     val additionalAssemblerOptions: List<String> | ||||
|     val defaultOutputType: OutputType | ||||
|  | ||||
|     fun initializeMemoryAreas(compilerOptions: CompilationOptions) | ||||
|     fun getFloatAsmBytes(num: Number): String | ||||
|  | ||||
|     fun convertFloatToBytes(num: Double): List<UByte> | ||||
|     fun convertBytesToFloat(bytes: List<UByte>): Double | ||||
|  | ||||
|     fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) | ||||
|     fun isIOAddress(address: UInt): Boolean | ||||
|  | ||||
|     override fun encodeString(str: String, encoding: Encoding): List<UByte> | ||||
|     override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String | ||||
|   | ||||
| @@ -11,4 +11,8 @@ interface IErrorReporter { | ||||
|         if(numErrors>0) | ||||
|             throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.") | ||||
|     } | ||||
|  | ||||
|     fun noErrorForLine(position: Position): Boolean | ||||
|  | ||||
|     fun printSingleError(errormessage: String) | ||||
| } | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| enum class CpuType { | ||||
|     CPU6502, | ||||
|     CPU65c02, | ||||
|     VIRTUAL | ||||
| } | ||||
|  | ||||
| interface IMachineDefinition { | ||||
|     val FLOAT_MAX_NEGATIVE: Double | ||||
|     val FLOAT_MAX_POSITIVE: Double | ||||
|     val FLOAT_MEM_SIZE: Int | ||||
|     val PROGRAM_LOAD_ADDRESS : UInt | ||||
|     val BSSHIGHRAM_START: UInt | ||||
|     val BSSHIGHRAM_END: UInt | ||||
|     val BSSGOLDENRAM_START: UInt | ||||
|     val BSSGOLDENRAM_END: UInt | ||||
|  | ||||
|     val cpu: CpuType | ||||
|     var zeropage: Zeropage | ||||
|     var golden: GoldenRam | ||||
|  | ||||
|     fun initializeMemoryAreas(compilerOptions: CompilationOptions) | ||||
|     fun getFloatAsmBytes(num: Number): String | ||||
|  | ||||
|     fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> | ||||
|     fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) | ||||
|     fun isIOAddress(address: UInt): Boolean | ||||
| } | ||||
| @@ -1,6 +1,15 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| interface IMemSizer { | ||||
|     fun memorySize(dt: DataType): Int | ||||
|     fun memorySize(arrayDt: DataType, numElements: Int): Int | ||||
|     fun memorySize(dt: DataType, numElements: Int?): Int | ||||
|  | ||||
|     fun memorySize(dt: BaseDataType): Int { | ||||
|         if(dt.isPassByRef) | ||||
|             return memorySize(DataType.UWORD, null)      // a pointer size | ||||
|         try { | ||||
|             return memorySize(DataType.forDt(dt), null) | ||||
|         } catch (x: NoSuchElementException) { | ||||
|             throw IllegalArgumentException(x.message) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,12 @@ enum class Encoding(val prefix: String) { | ||||
|     PETSCII("petscii"),         // c64/c128/cx16 | ||||
|     SCREENCODES("sc"),          // c64/c128/cx16 | ||||
|     ATASCII("atascii"),         // atari | ||||
|     ISO("iso")                  // cx16 | ||||
|     ISO("iso"),                 // cx16  (iso-8859-15) | ||||
|     ISO5("iso5"),               // cx16  (iso-8859-5, cyrillic) | ||||
|     ISO16("iso16"),             // cx16  (iso-8859-16, eastern european) | ||||
|     CP437("cp437"),             // cx16  (ibm pc, codepage 437) | ||||
|     KATAKANA("kata"),           // cx16  (katakana) | ||||
|     C64OS("c64os")              // c64 (C64 OS) | ||||
| } | ||||
|  | ||||
| interface IStringEncoding { | ||||
|   | ||||
| @@ -22,9 +22,10 @@ abstract class MemoryAllocator(protected val options: CompilationOptions) { | ||||
| abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||
|  | ||||
|     abstract val SCRATCH_B1 : UInt      // temp storage for a single byte | ||||
|     abstract val SCRATCH_REG : UInt     // temp storage for a register, must be B1+1 | ||||
|     abstract val SCRATCH_W1 : UInt      // temp storage 1 for a word  $fb+$fc | ||||
|     abstract val SCRATCH_W2 : UInt      // temp storage 2 for a word  $fb+$fc | ||||
|     abstract val SCRATCH_REG : UInt     // temp storage for a register byte, must be B1+1 | ||||
|     abstract val SCRATCH_W1 : UInt      // temp storage 1 for a word | ||||
|     abstract val SCRATCH_W2 : UInt      // temp storage 2 for a word | ||||
|     abstract val SCRATCH_PTR : UInt     // temp storage for a pointer | ||||
|  | ||||
|  | ||||
|     // the variables allocated into Zeropage. | ||||
| @@ -38,7 +39,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||
|             for (reserved in options.zpReserved) | ||||
|                 reserve(reserved) | ||||
|  | ||||
|             free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u)) | ||||
|             free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -70,9 +71,10 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||
|             return Err(MemAllocationError("zero page usage has been disabled")) | ||||
|  | ||||
|         val size: Int = | ||||
|                 when (datatype) { | ||||
|                     in IntegerDatatypes -> options.compTarget.memorySize(datatype) | ||||
|                     DataType.STR, in ArrayDatatypes  -> { | ||||
|                 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) | ||||
|                             errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position) | ||||
| @@ -80,9 +82,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||
|                             errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY) | ||||
|                         memsize | ||||
|                     } | ||||
|                     DataType.FLOAT -> { | ||||
|                     datatype.isFloat -> { | ||||
|                         if (options.floats) { | ||||
|                             val memsize = options.compTarget.memorySize(DataType.FLOAT) | ||||
|                             val memsize = options.compTarget.memorySize(DataType.FLOAT, null) | ||||
|                             if(position!=null) | ||||
|                                 errors.warn("allocating a large value in zeropage; float $memsize bytes", position) | ||||
|                             else | ||||
| @@ -94,7 +96,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||
|                 } | ||||
|  | ||||
|         synchronized(this) { | ||||
|             if(free.size > 0) { | ||||
|             if(free.isNotEmpty()) { | ||||
|                 if(size==1) { | ||||
|                     for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) { | ||||
|                         if(oneSeparateByteFree(candidate)) | ||||
| @@ -118,10 +120,11 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||
|         require(size>=0) | ||||
|         free.removeAll(address until address+size.toUInt()) | ||||
|         if(name.isNotEmpty()) { | ||||
|             allocatedVariables[name] = when(datatype) { | ||||
|                 in NumericDatatypes -> VarAllocation(address, datatype, size)        // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments | ||||
|                 DataType.STR -> VarAllocation(address, datatype, size) | ||||
|                 in ArrayDatatypes -> VarAllocation(address, datatype, size) | ||||
|             allocatedVariables[name] = when { | ||||
|                 datatype.isNumericOrBool -> VarAllocation(address, datatype, size)        // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments | ||||
|                 datatype.isString -> VarAllocation(address, datatype, size) | ||||
|                 datatype.isArray -> VarAllocation(address, datatype, size) | ||||
|                 datatype.isPointer -> VarAllocation(address, datatype, size) | ||||
|                 else -> throw AssemblyError("invalid dt") | ||||
|             } | ||||
|         } | ||||
| @@ -133,8 +136,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||
|         require(size>0) | ||||
|         return free.containsAll((address until address+size.toUInt()).toList()) | ||||
|     } | ||||
|  | ||||
|     abstract fun allocateCx16VirtualRegisters() | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -150,14 +151,13 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc | ||||
|         errors: IErrorReporter): Result<VarAllocation, MemAllocationError> { | ||||
|  | ||||
|         val size: Int = | ||||
|             when (datatype) { | ||||
|                 in IntegerDatatypes -> options.compTarget.memorySize(datatype) | ||||
|                 DataType.STR, in ArrayDatatypes  -> { | ||||
|                     options.compTarget.memorySize(datatype, numElements!!) | ||||
|                 } | ||||
|                 DataType.FLOAT -> { | ||||
|             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.FLOAT) | ||||
|                         options.compTarget.memorySize(DataType.FLOAT, null) | ||||
|                     } else return Err(MemAllocationError("floating point option not enabled")) | ||||
|                 } | ||||
|                 else -> throw MemAllocationError("weird dt") | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor") | ||||
| val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") | ||||
| val LogicalOperators = setOf("and", "or", "xor", "not") | ||||
| val BitwiseOperators = setOf("&", "|", "^", "~") | ||||
| val PrefixOperators = setOf("+", "-", "~", "not") | ||||
| 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") | ||||
|  | ||||
| fun invertedComparisonOperator(operator: String) = | ||||
|     when (operator) { | ||||
|   | ||||
| @@ -1,21 +1,21 @@ | ||||
| package prog8.code.core | ||||
|  | ||||
| import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX | ||||
| 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}]" | ||||
|     fun toClickableStr(): String { | ||||
|         if(this===DUMMY) | ||||
|             return "" | ||||
|         if(file.startsWith(LIBRARYFILEPREFIX)) | ||||
|         if(SourceCode.isLibraryResource(file)) | ||||
|             return "$file:$line:$startCol:" | ||||
|         return try { | ||||
|             val path = Path(file).absolute().normalize().toString() | ||||
|             val path = Path(file).sanitize().toString() | ||||
|             "file://$path:$line:$startCol:" | ||||
|         } catch(x: InvalidPathException) { | ||||
|         } catch(_: InvalidPathException) { | ||||
|             // this can occur on Windows when the source origin contains "invalid" characters such as ':' | ||||
|             "file://$file:$line:$startCol:" | ||||
|         } | ||||
|   | ||||
| @@ -1,127 +0,0 @@ | ||||
| package prog8.code.optimize | ||||
|  | ||||
| import prog8.code.ast.* | ||||
| import prog8.code.core.* | ||||
|  | ||||
|  | ||||
| fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, errors: IErrorReporter) { | ||||
|     if (!options.optimize) | ||||
|         return | ||||
|     while(errors.noErrors() && optimizeCommonSubExpressions(program, errors)>0) { | ||||
|         // keep rolling | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) { | ||||
|     fun recurse(node: PtNode, depth: Int) { | ||||
|         if(act(node, depth)) | ||||
|             node.children.forEach { recurse(it, depth+1) } | ||||
|     } | ||||
|     recurse(root, 0) | ||||
| } | ||||
|  | ||||
|  | ||||
| private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int { | ||||
|  | ||||
|     fun extractableSubExpr(expr: PtExpression): Boolean { | ||||
|         val result = if(expr is PtBinaryExpression) | ||||
|             expr.type !in ByteDatatypes || | ||||
|                     !expr.left.isSimple() || | ||||
|                     !expr.right.isSimple() || | ||||
|                     (expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators) | ||||
|         else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes) | ||||
|             true | ||||
|         else | ||||
|             !expr.isSimple() | ||||
|         return result | ||||
|     } | ||||
|  | ||||
|     // for each Binaryexpression, recurse to find a common subexpression pair therein. | ||||
|     val commons = mutableMapOf<PtBinaryExpression, Pair<PtExpression, PtExpression>>() | ||||
|     walkAst(program) { node: PtNode, depth: Int -> | ||||
|         if(node is PtBinaryExpression) { | ||||
|             val subExpressions = mutableListOf<PtExpression>() | ||||
|             walkAst(node.left) { subNode: PtNode, subDepth: Int -> | ||||
|                 if (subNode is PtExpression) { | ||||
|                     if(extractableSubExpr(subNode)) subExpressions.add(subNode) | ||||
|                     true | ||||
|                 } else false | ||||
|             } | ||||
|             walkAst(node.right) { subNode: PtNode, subDepth: Int -> | ||||
|                 if (subNode is PtExpression) { | ||||
|                     if(extractableSubExpr(subNode)) subExpressions.add(subNode) | ||||
|                     true | ||||
|                 } else false | ||||
|             } | ||||
|  | ||||
|             outer@for (first in subExpressions) { | ||||
|                 for (second in subExpressions) { | ||||
|                     if (first!==second && first isSameAs second) { | ||||
|                         commons[node] = first to second | ||||
|                         break@outer     // do only 1 replacement at a time per binaryexpression | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             false | ||||
|         } else true | ||||
|     } | ||||
|  | ||||
|     // replace common subexpressions by a temp variable that is assigned only once. | ||||
|     // TODO: check for commonalities across multiple separate expressions in the current scope, not only inside a single line | ||||
|     commons.forEach { binexpr, (occurrence1, occurrence2) -> | ||||
|         val (stmtContainer, stmt) = findContainingStatements(binexpr) | ||||
|         val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1) | ||||
|         val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2) | ||||
|         val containerScopedName = findScopeName(stmtContainer) | ||||
|         val tempvarName = "subexprvar_line${binexpr.position.line}_${binexpr.hashCode().toUInt()}" | ||||
|         // TODO: some tempvars could be reused, if they are from different lines | ||||
|  | ||||
|         val datatype = occurrence1.type | ||||
|         val singleReplacement1 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence1.position) | ||||
|         val singleReplacement2 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence2.position) | ||||
|         occurrence1.parent.children[occurrence1idx] = singleReplacement1 | ||||
|         singleReplacement1.parent = occurrence1.parent | ||||
|         occurrence2.parent.children[occurrence2idx] = singleReplacement2 | ||||
|         singleReplacement2.parent = occurrence2.parent | ||||
|  | ||||
|         val tempassign = PtAssignment(binexpr.position).also { assign -> | ||||
|             assign.add(PtAssignTarget(binexpr.position).also { tgt-> | ||||
|                 tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position)) | ||||
|             }) | ||||
|             assign.add(occurrence1) | ||||
|             occurrence1.parent = assign | ||||
|         } | ||||
|         stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign) | ||||
|         tempassign.parent = stmtContainer | ||||
|  | ||||
|         val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.DONTCARE, null, null, binexpr.position) | ||||
|         stmtContainer.add(0, tempvar) | ||||
|         tempvar.parent = stmtContainer | ||||
|  | ||||
|         // errors.info("common subexpressions replaced by a tempvar, maybe simplify the expression manually", binexpr.position) | ||||
|     } | ||||
|  | ||||
|     return commons.size | ||||
| } | ||||
|  | ||||
|  | ||||
| internal fun findScopeName(node: PtNode): String { | ||||
|     var parent=node | ||||
|     while(parent !is PtNamedNode) | ||||
|         parent = parent.parent | ||||
|     return parent.scopedName | ||||
| } | ||||
|  | ||||
|  | ||||
| internal fun findContainingStatements(node: PtNode): Pair<PtNode, PtNode> {      // returns (parentstatementcontainer, childstatement) | ||||
|     var parent = node.parent | ||||
|     var child = node | ||||
|     while(true) { | ||||
|         if(parent is IPtStatementContainer) { | ||||
|             return parent to child | ||||
|         } | ||||
|         child=parent | ||||
|         parent=parent.parent | ||||
|     } | ||||
| } | ||||
							
								
								
									
										74
									
								
								codeCore/src/prog8/code/source/ImportFileSystem.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								codeCore/src/prog8/code/source/ImportFileSystem.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| package prog8.code.source | ||||
|  | ||||
| import prog8.code.core.Position | ||||
| import prog8.code.sanitize | ||||
| import java.nio.file.Path | ||||
| import java.util.* | ||||
| import kotlin.io.path.Path | ||||
|  | ||||
|  | ||||
| // Resource caching "filesystem". | ||||
| // Note that it leaves the decision to load a resource or an actual disk file to the caller. | ||||
|  | ||||
| object ImportFileSystem { | ||||
|  | ||||
|     fun expandTilde(path: String): String = if (path.startsWith("~")) { | ||||
|         val userHome = System.getProperty("user.home") | ||||
|         userHome + path.drop(1) | ||||
|     } else { | ||||
|         path | ||||
|     } | ||||
|  | ||||
|     fun expandTilde(path: Path): Path = Path(expandTilde(path.toString())) | ||||
|  | ||||
|     fun getFile(path: Path, isLibrary: Boolean=false): SourceCode { | ||||
|         val normalized = path.sanitize() | ||||
|         val cached = cache[normalized.toString()] | ||||
|         if (cached != null) | ||||
|             return cached | ||||
|         val file = SourceCode.File(normalized, isLibrary) | ||||
|         cache[normalized.toString()] = file | ||||
|         return file | ||||
|     } | ||||
|  | ||||
|     fun getResource(name: String): SourceCode { | ||||
|         val cached = cache[name] | ||||
|         if (cached != null) return cached | ||||
|         val resource = SourceCode.Resource(name) | ||||
|         cache[name] = resource | ||||
|         return resource | ||||
|     } | ||||
|  | ||||
|     fun retrieveSourceLine(position: Position): String { | ||||
|         if(SourceCode.isLibraryResource(position.file)) { | ||||
|             val cached = cache[SourceCode.withoutPrefix(position.file)] | ||||
|             if(cached != null) | ||||
|                 return getLine(cached, position.line) | ||||
|         } | ||||
|         val cached = cache[position.file] | ||||
|         if(cached != null) | ||||
|             return getLine(cached, position.line) | ||||
|         val path = Path(position.file).sanitize() | ||||
|         val cached2 = cache[path.toString()] | ||||
|         if(cached2 != null) | ||||
|             return getLine(cached2, position.line) | ||||
|         throw NoSuchElementException("cannot get source line $position, with path $path") | ||||
|     } | ||||
|  | ||||
|     private fun getLine(code: SourceCode, lineIndex: Int): String { | ||||
|         var spans = lineSpanCache[code] | ||||
|         if(spans==null) { | ||||
|             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 | ||||
|         } | ||||
|         val span = spans[lineIndex - 1] | ||||
|         return code.text.substring(span.start, span.end).trim() | ||||
|     } | ||||
|  | ||||
|     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>>() | ||||
| } | ||||
| @@ -1,16 +1,13 @@ | ||||
| package prog8.code.core | ||||
| package prog8.code.source | ||||
| 
 | ||||
| import java.io.File | ||||
| import prog8.code.sanitize | ||||
| import java.io.IOException | ||||
| import java.nio.file.Path | ||||
| import java.text.Normalizer | ||||
| import kotlin.io.path.Path | ||||
| import kotlin.io.path.absolute | ||||
| import kotlin.io.path.readText | ||||
| 
 | ||||
| 
 | ||||
| const val internedStringsModuleName = "prog8_interned_strings" | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Encapsulates - and ties together - actual source code (=text) and its [origin]. | ||||
|  */ | ||||
| @@ -26,6 +23,11 @@ sealed class SourceCode { | ||||
|      */ | ||||
|     abstract val isFromFilesystem: Boolean | ||||
| 
 | ||||
|     /** | ||||
|      * Whether this [SourceCode] instance was created from a library module file | ||||
|      */ | ||||
|     abstract val isFromLibrary: Boolean | ||||
| 
 | ||||
|     /** | ||||
|      * The logical name of the source code unit. Usually the module's name. | ||||
|      */ | ||||
| @@ -55,23 +57,32 @@ sealed class SourceCode { | ||||
|         /** | ||||
|          * filename prefix to designate library files that will be retreived from internal resources rather than disk | ||||
|          */ | ||||
|         const val LIBRARYFILEPREFIX = "library:" | ||||
|         const val STRINGSOURCEPREFIX = "string:" | ||||
|         val curdir: Path = Path(".").toAbsolutePath() | ||||
|         fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath()) | ||||
|         fun isRegularFilesystemPath(pathString: String) = | ||||
|             !(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX)) | ||||
| 
 | ||||
|         private const val LIBRARYFILEPREFIX = "library:" | ||||
|         private const val STRINGSOURCEPREFIX = "string:" | ||||
|         val curdir: Path = Path(".").absolute() | ||||
|         fun relative(path: Path): Path = curdir.relativize(path.sanitize()) | ||||
|         fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString) | ||||
|         fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX) | ||||
|         fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX) | ||||
|         fun withoutPrefix(path: String): String { | ||||
|             return if(isLibraryResource(path)) | ||||
|                 path.removePrefix(LIBRARYFILEPREFIX) | ||||
|             else if(isStringResource(path)) | ||||
|                 path.removePrefix(STRINGSOURCEPREFIX) | ||||
|             else | ||||
|                 path | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Turn a plain String into a [SourceCode] object. | ||||
|      * [origin] will be something like `string:44c56085`. | ||||
|      */ | ||||
|     class Text(override val text: String): SourceCode() { | ||||
|     class Text(origText: String): SourceCode() { | ||||
|         override val text = origText.replace("\\R".toRegex(), "\n")      // normalize line endings | ||||
|         override val isFromResources = false | ||||
|         override val isFromFilesystem = false | ||||
|         override val isFromLibrary = false | ||||
|         override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}" | ||||
|         override val name = "<unnamed-text>" | ||||
|     } | ||||
| @@ -79,12 +90,13 @@ sealed class SourceCode { | ||||
|     /** | ||||
|      * Get [SourceCode] from the file represented by the specified Path. | ||||
|      * This immediately reads the file fully into memory. | ||||
|      * You can only get an instance of this via the ImportFileSystem object. | ||||
|      * | ||||
|      * [origin] will be the given path in absolute and normalized form. | ||||
|      * @throws NoSuchFileException if the file does not exist | ||||
|      * @throws FileSystemException if the file cannot be read | ||||
|      */ | ||||
|     class File(path: Path): SourceCode() { | ||||
|     internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() { | ||||
|         override val text: String | ||||
|         override val origin: String | ||||
|         override val name: String | ||||
| @@ -95,7 +107,8 @@ sealed class SourceCode { | ||||
|             val normalized = path.normalize() | ||||
|             origin = relative(normalized).toString() | ||||
|             try { | ||||
|                 text = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC) | ||||
|                 val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC) | ||||
|                 text = contents.replace("\\R".toRegex(), "\n")      // normalize line endings | ||||
|                 name = normalized.toFile().nameWithoutExtension | ||||
|             } catch (nfx: java.nio.file.NoSuchFileException) { | ||||
|                 throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) } | ||||
| @@ -107,12 +120,14 @@ sealed class SourceCode { | ||||
| 
 | ||||
|     /** | ||||
|      * [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8" | ||||
|      * You can only get an instance of this via the ImportFileSystem object. | ||||
|      */ | ||||
|     class Resource(pathString: String): SourceCode() { | ||||
|     internal class Resource(pathString: String): SourceCode() { | ||||
|         private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/") | ||||
| 
 | ||||
|         override val isFromResources = true | ||||
|         override val isFromFilesystem = false | ||||
|         override val isFromLibrary = true | ||||
|         override val origin = "$LIBRARYFILEPREFIX$normalized" | ||||
|         override val text: String | ||||
|         override val name: String | ||||
| @@ -122,12 +137,13 @@ sealed class SourceCode { | ||||
|             if (rscURL == null) { | ||||
|                 val rscRoot = object {}.javaClass.getResource("/") | ||||
|                 throw NoSuchFileException( | ||||
|                     File(normalized), | ||||
|                     java.io.File(normalized), | ||||
|                     reason = "looked in resources rooted at $rscRoot" | ||||
|                 ) | ||||
|             } | ||||
|             val stream = object {}.javaClass.getResourceAsStream(normalized) | ||||
|             text = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) } | ||||
|             val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) } | ||||
|             text = contents.replace("\\R".toRegex(), "\n")      // normalize line endings | ||||
|             name = Path(pathString).toFile().nameWithoutExtension | ||||
|         } | ||||
|     } | ||||
| @@ -138,37 +154,8 @@ sealed class SourceCode { | ||||
|     class Generated(override val name: String) : SourceCode() { | ||||
|         override val isFromResources: Boolean = false | ||||
|         override val isFromFilesystem: Boolean = false | ||||
|         override val isFromLibrary: Boolean = false | ||||
|         override val origin: String = name | ||||
|         override val text: String = "<generated code node, no text representation>" | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| object SourceLineCache { | ||||
|     private val cache = mutableMapOf<String, List<String>>() | ||||
| 
 | ||||
|     private fun getCachedFile(file: String): List<String> { | ||||
|         val existing = cache[file] | ||||
|         if(existing!=null) | ||||
|             return existing | ||||
|         if (SourceCode.isRegularFilesystemPath(file)) { | ||||
|             val source = SourceCode.File(Path(file)) | ||||
|             cache[file] = source.text.split('\n', '\r').map { it.trim() } | ||||
|             return cache.getValue(file) | ||||
|         } else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) { | ||||
|             val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length)) | ||||
|             cache[file] = source.text.split('\n', '\r').map { it.trim()} | ||||
|             return cache.getValue(file) | ||||
|         } | ||||
|         return emptyList() | ||||
|     } | ||||
| 
 | ||||
|     fun retrieveLine(position: Position): String? { | ||||
|         if (position.line>0) { | ||||
|             val lines = getCachedFile(position.file) | ||||
|             if(lines.isNotEmpty()) | ||||
|                 return lines[position.line-1] | ||||
|         } | ||||
|         return null | ||||
|     } | ||||
| } | ||||
| @@ -1,27 +0,0 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.atari.AtariMachineDefinition | ||||
|  | ||||
|  | ||||
| class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer { | ||||
|     override val name = NAME | ||||
|     override val machine = AtariMachineDefinition() | ||||
|     override val defaultEncoding = Encoding.ATASCII | ||||
|  | ||||
|     companion object { | ||||
|         const val NAME = "atari" | ||||
|     } | ||||
|  | ||||
|     override fun memorySize(dt: DataType): Int { | ||||
|         return when(dt) { | ||||
|             in ByteDatatypes -> 1 | ||||
|             in WordDatatypes, in PassByReferenceDatatypes -> 2 | ||||
|             DataType.FLOAT -> machine.FLOAT_MEM_SIZE | ||||
|             else -> throw IllegalArgumentException("invalid datatype") | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun memorySize(arrayDt: DataType, numElements: Int) = | ||||
|         memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements | ||||
| } | ||||
| @@ -1,19 +1,81 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.Encoding | ||||
| import prog8.code.core.ICompilationTarget | ||||
| import prog8.code.core.IMemSizer | ||||
| import prog8.code.core.IStringEncoding | ||||
| import prog8.code.target.c128.C128MachineDefinition | ||||
| import prog8.code.target.cbm.CbmMemorySizer | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.encodings.Encoder | ||||
| import prog8.code.target.zp.C128Zeropage | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { | ||||
| class C128Target: ICompilationTarget, | ||||
|     IStringEncoding by Encoder(true), | ||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { | ||||
|  | ||||
|     override val name = NAME | ||||
|     override val machine = C128MachineDefinition() | ||||
|     override val defaultEncoding = Encoding.PETSCII | ||||
|     override val libraryPath = null | ||||
|     override val customLauncher = emptyList<String>() | ||||
|     override val additionalAssemblerOptions = emptyList<String>() | ||||
|     override val defaultOutputType = OutputType.PRG | ||||
|  | ||||
|     companion object { | ||||
|         const val NAME = "c128" | ||||
|     } | ||||
|  | ||||
|  | ||||
|     override val cpu = CpuType.CPU6502 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x1c01u | ||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0xc000u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0u    // TODO address? | ||||
|     override val BSSHIGHRAM_END = 0u      // TODO address? | ||||
|     override val BSSGOLDENRAM_START = 0u  // TODO address? | ||||
|     override val BSSGOLDENRAM_END = 0u    // TODO address? | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|  | ||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { | ||||
|         val m5 = Mflpt5.fromNumber(num) | ||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) | ||||
|     } | ||||
|  | ||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { | ||||
|         require(bytes.size==5) { "need 5 bytes" } | ||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) | ||||
|         return m5.toDouble() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { | ||||
|         if(selectedEmulator!=1) { | ||||
|             System.err.println("The c128 target only supports the main emulator (Vice).") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         if(!quiet) | ||||
|             println("\nStarting C-128 emulator x128...") | ||||
|  | ||||
|         val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) | ||||
|         val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist, | ||||
|             "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||
|         val processb = ProcessBuilder(cmdline) | ||||
|         if(!quiet) | ||||
|             processb.inheritIO() | ||||
|         val process: Process = processb.start() | ||||
|         process.waitFor() | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = C128Zeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY)    // TODO does the c128 have some of this somewhere? | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,21 +1,111 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.Encoding | ||||
| import prog8.code.core.ICompilationTarget | ||||
| import prog8.code.core.IMemSizer | ||||
| import prog8.code.core.IStringEncoding | ||||
| import prog8.code.target.c64.C64MachineDefinition | ||||
| import prog8.code.target.cbm.CbmMemorySizer | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.encodings.Encoder | ||||
| import prog8.code.target.zp.C64Zeropage | ||||
| import java.io.IOException | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { | ||||
| class C64Target: ICompilationTarget, | ||||
|     IStringEncoding by Encoder(true), | ||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { | ||||
|  | ||||
|     override val name = NAME | ||||
|     override val machine = C64MachineDefinition() | ||||
|     override val defaultEncoding = Encoding.PETSCII | ||||
|     override val libraryPath = null | ||||
|     override val customLauncher = emptyList<String>() | ||||
|     override val additionalAssemblerOptions = emptyList<String>() | ||||
|     override val defaultOutputType = OutputType.PRG | ||||
|  | ||||
|     companion object { | ||||
|         const val NAME = "c64" | ||||
|  | ||||
|         fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list" | ||||
|     } | ||||
|  | ||||
|  | ||||
|     override val cpu = CpuType.CPU6502 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0801u | ||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u      // $a000  if floats are used | ||||
|     // note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15 | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0xc000u | ||||
|     override val BSSHIGHRAM_END = 0xcfdfu | ||||
|     override val BSSGOLDENRAM_START = 0u        // no golden ram on C64 | ||||
|     override val BSSGOLDENRAM_END = 0u | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|  | ||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { | ||||
|         val m5 = Mflpt5.fromNumber(num) | ||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) | ||||
|     } | ||||
|  | ||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { | ||||
|         require(bytes.size==5) { "need 5 bytes" } | ||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) | ||||
|         return m5.toDouble() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { | ||||
|         if(selectedEmulator!=1) { | ||||
|             System.err.println("The c64 target only supports the main emulator (Vice).") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         for(emulator in listOf("x64sc", "x64")) { | ||||
|             if(!quiet) | ||||
|                 println("\nStarting C-64 emulator $emulator...") | ||||
|  | ||||
|             val viceMonlist = viceMonListName(programNameWithPath.toString()) | ||||
|             val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist, | ||||
|                 "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||
|             val processb = ProcessBuilder(cmdline) | ||||
|             if(!quiet) | ||||
|                 processb.inheritIO() | ||||
|             val process: Process | ||||
|             try { | ||||
|                 process=processb.start() | ||||
|             } catch(_: IOException) { | ||||
|                 continue  // try the next emulator executable | ||||
|             } | ||||
|             process.waitFor() | ||||
|             break | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = C64Zeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| val CompilationTargets = listOf( | ||||
|     C64Target.NAME, | ||||
|     C128Target.NAME, | ||||
|     Cx16Target.NAME, | ||||
|     PETTarget.NAME, | ||||
|     VMTarget.NAME | ||||
| ) | ||||
|  | ||||
| fun getCompilationTargetByName(name: String) = when(name.lowercase()) { | ||||
|     C64Target.NAME -> C64Target() | ||||
|     C128Target.NAME -> C128Target() | ||||
|     Cx16Target.NAME -> Cx16Target() | ||||
|     PETTarget.NAME -> PETTarget() | ||||
|     VMTarget.NAME -> VMTarget() | ||||
|     else -> throw IllegalArgumentException("invalid compilation target") | ||||
| } | ||||
|   | ||||
							
								
								
									
										173
									
								
								codeCore/src/prog8/code/target/ConfigFileTarget.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								codeCore/src/prog8/code/target/ConfigFileTarget.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import prog8.code.source.ImportFileSystem.expandTilde | ||||
| import prog8.code.target.encodings.Encoder | ||||
| import prog8.code.target.zp.ConfigurableZeropage | ||||
| import java.io.IOException | ||||
| import java.nio.file.Path | ||||
| import java.util.* | ||||
| import kotlin.io.path.Path | ||||
| import kotlin.io.path.inputStream | ||||
| import kotlin.io.path.isDirectory | ||||
| import kotlin.io.path.nameWithoutExtension | ||||
|  | ||||
|  | ||||
| class ConfigFileTarget( | ||||
|     override val name: String, | ||||
|     override val defaultEncoding: Encoding, | ||||
|     override val cpu: CpuType, | ||||
|     override val PROGRAM_LOAD_ADDRESS: UInt, | ||||
|     override val PROGRAM_MEMTOP_ADDRESS: UInt, | ||||
|     override val STARTUP_CODE_RESERVED_SIZE: UInt, | ||||
|     override val BSSHIGHRAM_START: UInt, | ||||
|     override val BSSHIGHRAM_END: UInt, | ||||
|     override val BSSGOLDENRAM_START: UInt, | ||||
|     override val BSSGOLDENRAM_END: UInt, | ||||
|     override val defaultOutputType: OutputType, | ||||
|     override val libraryPath: Path, | ||||
|     override val customLauncher: List<String>, | ||||
|     override val additionalAssemblerOptions: List<String>, | ||||
|     val ioAddresses: List<UIntRange>, | ||||
|     val zpScratchB1: UInt, | ||||
|     val zpScratchReg: UInt, | ||||
|     val zpScratchW1: UInt, | ||||
|     val zpScratchW2: UInt, | ||||
|     val zpScratchPtr: UInt, | ||||
|     val virtualregistersStart: UInt, | ||||
|     val zpFullsafe: List<UIntRange>, | ||||
|     val zpKernalsafe: List<UIntRange>, | ||||
|     val zpBasicsafe: List<UIntRange> | ||||
| ): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) { | ||||
|  | ||||
|     companion object { | ||||
|  | ||||
|         private fun Properties.getString(property: String): String { | ||||
|             val value = this.getProperty(property, null) | ||||
|             if(value!=null) | ||||
|                 return value | ||||
|             throw NoSuchElementException("string property '$property' not found in config file") | ||||
|         } | ||||
|  | ||||
|         private fun Properties.getInteger(property: String): UInt { | ||||
|             val value = this.getProperty(property, null) | ||||
|             if(value!=null) return parseInt(value) | ||||
|             throw NoSuchElementException("integer property '$property' not found in config file") | ||||
|         } | ||||
|  | ||||
|         private fun parseInt(value: String): UInt { | ||||
|             if(value.startsWith("0x")) | ||||
|                 return value.drop(2).toUInt(16) | ||||
|             if(value.startsWith("$")) | ||||
|                 return value.drop(1).toUInt(16) | ||||
|             if(value.startsWith("%")) | ||||
|                 return value.drop(1).toUInt(2) | ||||
|             return value.toUInt() | ||||
|         } | ||||
|  | ||||
|         private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> { | ||||
|             val rangesStr = props.getString(key) | ||||
|             if(rangesStr.isBlank()) | ||||
|                 return emptyList() | ||||
|             val result = mutableListOf<UIntRange>() | ||||
|             val ranges = rangesStr.split(",").map { it.trim() } | ||||
|             for(r in ranges) { | ||||
|                 if ('-' in r) { | ||||
|                     val (fromStr, toStr) = r.split("-") | ||||
|                     val from = parseInt(fromStr.trim()) | ||||
|                     val to = parseInt(toStr.trim()) | ||||
|                     result.add(from..to) | ||||
|                 } else { | ||||
|                     val address = parseInt(r) | ||||
|                     result.add(address..address) | ||||
|                 } | ||||
|             } | ||||
|             return result | ||||
|         } | ||||
|  | ||||
|         fun fromConfigFile(configfile: Path): ConfigFileTarget { | ||||
|             val props = Properties() | ||||
|             props.load(configfile.inputStream()) | ||||
|  | ||||
|             val cpuString = props.getString("cpu").uppercase() | ||||
|             val cpuType = try { | ||||
|                 CpuType.valueOf(cpuString) | ||||
|             } catch (_: IllegalArgumentException) { | ||||
|                 CpuType.valueOf("CPU$cpuString") | ||||
|             } | ||||
|             val ioAddresses = parseAddressRanges("io_regions", props) | ||||
|             val zpFullsafe = parseAddressRanges("zp_fullsafe", props) | ||||
|             val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props) | ||||
|             val zpBasicsafe = parseAddressRanges("zp_basicsafe", props) | ||||
|  | ||||
|             val libraryPath = expandTilde(Path(props.getString("library"))) | ||||
|             if(!libraryPath.isDirectory()) | ||||
|                 throw IOException("invalid library path: $libraryPath") | ||||
|  | ||||
|             val customLauncherStr = props.getProperty("custom_launcher_code", null) | ||||
|             val customLauncher = | ||||
|                 if(customLauncherStr?.isNotBlank()==true) | ||||
|                     (customLauncherStr+"\n").lines().map { it.trimEnd() } | ||||
|                 else emptyList() | ||||
|             val assemblerOptionsStr = props.getProperty("assembler_options", "").trim() | ||||
|             val outputTypeString = props.getProperty("output_type", "PRG") | ||||
|             val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase()) | ||||
|  | ||||
|             return ConfigFileTarget( | ||||
|                 configfile.nameWithoutExtension, | ||||
|                 Encoding.entries.first { it.prefix==props.getString("encoding") }, | ||||
|                 cpuType, | ||||
|                 props.getInteger("load_address"), | ||||
|                 props.getInteger("memtop"), | ||||
|                 0u,         // used only in a very specific error condition check in a certain scenario... | ||||
|                 props.getInteger("bss_highram_start"), | ||||
|                 props.getInteger("bss_highram_end"), | ||||
|                 props.getInteger("bss_goldenram_start"), | ||||
|                 props.getInteger("bss_goldenram_end"), | ||||
|                 defaultOutputType, | ||||
|                 libraryPath, | ||||
|                 customLauncher, | ||||
|                 if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "), | ||||
|                 ioAddresses, | ||||
|                 props.getInteger("zp_scratch_b1"), | ||||
|                 props.getInteger("zp_scratch_reg"), | ||||
|                 props.getInteger("zp_scratch_w1"), | ||||
|                 props.getInteger("zp_scratch_w2"), | ||||
|                 props.getInteger("zp_scratch_ptr"), | ||||
|                 props.getInteger("virtual_registers"), | ||||
|                 zpFullsafe, | ||||
|                 zpKernalsafe, | ||||
|                 zpBasicsafe, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // TODO floats are not yet supported here, just enter some values | ||||
|     override val FLOAT_MAX_POSITIVE = 9.999999999e97 | ||||
|     override val FLOAT_MAX_NEGATIVE = -9.999999999e97 | ||||
|     override val FLOAT_MEM_SIZE = 8 | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam     // TODO this is not yet used | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = TODO("floats") | ||||
|     override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats") | ||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats") | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { | ||||
|         throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.") | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it } | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = ConfigurableZeropage( | ||||
|             zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr, | ||||
|             virtualregistersStart, | ||||
|             zpBasicsafe, | ||||
|             zpKernalsafe, | ||||
|             zpFullsafe, | ||||
|             compilerOptions | ||||
|         ) | ||||
|         // note: there's no golden ram yet | ||||
|     } | ||||
| } | ||||
| @@ -1,19 +1,94 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.Encoding | ||||
| import prog8.code.core.ICompilationTarget | ||||
| import prog8.code.core.IMemSizer | ||||
| import prog8.code.core.IStringEncoding | ||||
| import prog8.code.target.cbm.CbmMemorySizer | ||||
| import prog8.code.target.cx16.CX16MachineDefinition | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.encodings.Encoder | ||||
| import prog8.code.target.zp.CX16Zeropage | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { | ||||
| class Cx16Target: ICompilationTarget, | ||||
|     IStringEncoding by Encoder(true), | ||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { | ||||
|  | ||||
|     override val name = NAME | ||||
|     override val machine = CX16MachineDefinition() | ||||
|     override val defaultEncoding = Encoding.PETSCII | ||||
|     override val libraryPath = null | ||||
|     override val customLauncher = emptyList<String>() | ||||
|     override val additionalAssemblerOptions = emptyList<String>() | ||||
|     override val defaultOutputType = OutputType.PRG | ||||
|  | ||||
|     companion object { | ||||
|         const val NAME = "cx16" | ||||
|     } | ||||
|  | ||||
|  | ||||
|     override val cpu = CpuType.CPU65C02 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0801u | ||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0xa000u     // hiram bank 1, 8Kb, assumed to be active | ||||
|     override val BSSHIGHRAM_END = 0xbfffu       // Rom starts at $c000 | ||||
|     override val BSSGOLDENRAM_START = 0x0400u | ||||
|     override val BSSGOLDENRAM_END = 0x07ffu | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|  | ||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { | ||||
|         val m5 = Mflpt5.fromNumber(num) | ||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) | ||||
|     } | ||||
|  | ||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { | ||||
|         require(bytes.size==5) { "need 5 bytes" } | ||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) | ||||
|         return m5.toDouble() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { | ||||
|         val emulator: String | ||||
|         val extraArgs: List<String> | ||||
|  | ||||
|         when(selectedEmulator) { | ||||
|             1 -> { | ||||
|                 emulator = "x16emu" | ||||
|                 extraArgs = listOf("-debug") | ||||
|             } | ||||
|             2 -> { | ||||
|                 emulator = "box16" | ||||
|                 extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString())) | ||||
|             } | ||||
|             else -> { | ||||
|                 System.err.println("Cx16 target only supports x16emu and box16 emulators.") | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if(!quiet) | ||||
|             println("\nStarting Commander X16 emulator $emulator...") | ||||
|  | ||||
|         val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs | ||||
|         val processb = ProcessBuilder(cmdline) | ||||
|         if(!quiet) | ||||
|             processb.inheritIO() | ||||
|         processb.environment()["PULSE_LATENCY_MSEC"] = "10" | ||||
|         val process: Process = processb.start() | ||||
|         process.waitFor() | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = CX16Zeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END) | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,9 @@ | ||||
| package prog8.code.target.cbm | ||||
| package prog8.code.target | ||||
| 
 | ||||
| import prog8.code.core.InternalCompilerException | ||||
| import kotlin.math.absoluteValue | ||||
| import kotlin.math.pow | ||||
| 
 | ||||
| 
 | ||||
| data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) { | ||||
| 
 | ||||
|     companion object { | ||||
| @@ -19,7 +18,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va | ||||
|             // and https://en.wikipedia.org/wiki/IEEE_754-1985 | ||||
| 
 | ||||
|             val flt = num.toDouble() | ||||
|             if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE) | ||||
|             if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE) | ||||
|                 throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this") | ||||
|             if (flt == 0.0) | ||||
|                 return zero | ||||
							
								
								
									
										39
									
								
								codeCore/src/prog8/code/target/NormalMemSizer.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								codeCore/src/prog8/code/target/NormalMemSizer.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.BaseDataType | ||||
| import prog8.code.core.DataType | ||||
| import prog8.code.core.IMemSizer | ||||
|  | ||||
| internal class NormalMemSizer(val floatsize: Int): IMemSizer { | ||||
|  | ||||
|     override fun memorySize(dt: DataType, numElements: Int?): Int { | ||||
|         if(dt.isPointerArray) | ||||
|             return 2 * numElements!!        // array of pointers is just array of uwords | ||||
|         else if(dt.isArray) { | ||||
|             if(numElements==null) return 2      // treat it as a pointer size | ||||
|             return when(dt.sub) { | ||||
|                 BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements | ||||
|                 BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2 | ||||
|                 BaseDataType.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) { | ||||
|             return numElements        // treat it as the size of the given string with the length | ||||
|                 ?: 2    // treat it as the size to store a string pointer | ||||
|         } | ||||
|  | ||||
|         return when { | ||||
|             dt.isByteOrBool -> 1 * (numElements ?: 1) | ||||
|             dt.isFloat -> floatsize * (numElements ?: 1) | ||||
|             dt.isLong -> 4 * (numElements ?: 1) | ||||
|             dt.isPointer -> 2  // pointer is just a uword | ||||
|             dt.isStructInstance -> dt.subType!!.memsize(this) | ||||
|             dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size") | ||||
|             else -> 2 * (numElements ?: 1) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,19 +1,80 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.Encoding | ||||
| import prog8.code.core.ICompilationTarget | ||||
| import prog8.code.core.IMemSizer | ||||
| import prog8.code.core.IStringEncoding | ||||
| import prog8.code.target.cbm.CbmMemorySizer | ||||
| import prog8.code.target.pet.PETMachineDefinition | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.encodings.Encoder | ||||
| import prog8.code.target.zp.PETZeropage | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { | ||||
| class PETTarget: ICompilationTarget, | ||||
|     IStringEncoding by Encoder(true), | ||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { | ||||
|  | ||||
|     override val name = NAME | ||||
|     override val machine = PETMachineDefinition() | ||||
|     override val defaultEncoding = Encoding.PETSCII | ||||
|     override val libraryPath = null | ||||
|     override val customLauncher = emptyList<String>() | ||||
|     override val additionalAssemblerOptions = emptyList<String>() | ||||
|     override val defaultOutputType = OutputType.PRG | ||||
|  | ||||
|     companion object { | ||||
|         const val NAME = "pet32" | ||||
|     } | ||||
|  | ||||
|     override val cpu = CpuType.CPU6502 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0401u | ||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0x8000u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0u | ||||
|     override val BSSHIGHRAM_END = 0u | ||||
|     override val BSSGOLDENRAM_START = 0u | ||||
|     override val BSSGOLDENRAM_END = 0u | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|  | ||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { | ||||
|         val m5 = Mflpt5.fromNumber(num) | ||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) | ||||
|     } | ||||
|  | ||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { | ||||
|         require(bytes.size==5) { "need 5 bytes" } | ||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) | ||||
|         return m5.toDouble() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { | ||||
|         if(selectedEmulator!=1) { | ||||
|             System.err.println("The pet target only supports the main emulator (Vice).") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         if(!quiet) | ||||
|             println("\nStarting PET emulator...") | ||||
|  | ||||
|         val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) | ||||
|         val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist, | ||||
|             "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||
|         val processb = ProcessBuilder(cmdline) | ||||
|         if(!quiet) | ||||
|             processb.inheritIO() | ||||
|         val process=processb.start() | ||||
|         process.waitFor() | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = PETZeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,26 +1,109 @@ | ||||
| package prog8.code.target | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.virtual.VirtualMachineDefinition | ||||
| import prog8.code.target.encodings.Encoder | ||||
| import java.nio.file.Path | ||||
| import kotlin.io.path.extension | ||||
| import kotlin.io.path.isReadable | ||||
| import kotlin.io.path.name | ||||
| import kotlin.io.path.readText | ||||
|  | ||||
| class VMTarget: ICompilationTarget, | ||||
|     IStringEncoding by Encoder(false), | ||||
|     IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) { | ||||
|  | ||||
| class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer { | ||||
|     override val name = NAME | ||||
|     override val machine = VirtualMachineDefinition() | ||||
|     override val defaultEncoding = Encoding.ISO | ||||
|     override val libraryPath = null | ||||
|     override val customLauncher = emptyList<String>() | ||||
|     override val additionalAssemblerOptions = emptyList<String>() | ||||
|     override val defaultOutputType = OutputType.PRG | ||||
|  | ||||
|     companion object { | ||||
|         const val NAME = "virtual" | ||||
|         const val FLOAT_MEM_SIZE = 8             // 64-bits double | ||||
|     } | ||||
|  | ||||
|     override fun memorySize(dt: DataType): Int { | ||||
|         return when(dt) { | ||||
|             in ByteDatatypes -> 1 | ||||
|             in WordDatatypes, in PassByReferenceDatatypes -> 2 | ||||
|             DataType.FLOAT -> machine.FLOAT_MEM_SIZE | ||||
|             else -> throw IllegalArgumentException("invalid datatype") | ||||
|         } | ||||
|     override val cpu = CpuType.VIRTUAL | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE | ||||
|     override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE | ||||
|     override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE | ||||
|     override val STARTUP_CODE_RESERVED_SIZE = 0u  // not actually used | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0u      // not actually used | ||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0xffffu  // not actually used | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0u          // not actually used | ||||
|     override val BSSHIGHRAM_END = 0u            // not actually used | ||||
|     override val BSSGOLDENRAM_START = 0u        // not actually used | ||||
|     override val BSSGOLDENRAM_END = 0u          // not actually used | ||||
|     override lateinit var zeropage: Zeropage    // not actually used | ||||
|     override lateinit var golden: GoldenRam     // not actually used | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number): String { | ||||
|         // little endian binary representation | ||||
|         val bits = num.toDouble().toBits().toULong() | ||||
|         val hexStr = bits.toString(16).padStart(16, '0') | ||||
|         val parts = hexStr.chunked(2).map { "$$it" } | ||||
|         return parts.joinToString(", ") | ||||
|     } | ||||
|  | ||||
|     override fun memorySize(arrayDt: DataType, numElements: Int) = | ||||
|         memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements | ||||
| } | ||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { | ||||
|         val bits = num.toBits().toULong() | ||||
|         val hexStr = bits.toString(16).padStart(16, '0') | ||||
|         val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() } | ||||
|         return parts | ||||
|     } | ||||
|  | ||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { | ||||
|         require(bytes.size==8) { "need 8 bytes" } | ||||
|         val b0 = bytes[0].toLong() shl (8*7) | ||||
|         val b1 = bytes[1].toLong() shl (8*6) | ||||
|         val b2 = bytes[2].toLong() shl (8*5) | ||||
|         val b3 = bytes[3].toLong() shl (8*4) | ||||
|         val b4 = bytes[4].toLong() shl (8*3) | ||||
|         val b5 = bytes[5].toLong() shl (8*2) | ||||
|         val b6 = bytes[6].toLong() shl (8*1) | ||||
|         val b7 = bytes[7].toLong() shl (8*0) | ||||
|         return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7) | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { | ||||
|         if(!quiet) | ||||
|             println("\nStarting Virtual Machine...") | ||||
|  | ||||
|         // to not have external module dependencies in our own module, we launch the virtual machine via reflection | ||||
|         val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner | ||||
|         val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir") | ||||
|         if(withExt.isReadable()) | ||||
|             vm.runProgram(withExt.readText(), quiet) | ||||
|         else | ||||
|             throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file") | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = false | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = VirtualZeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| interface IVirtualMachineRunner { | ||||
|     fun runProgram(irSource: String, quiet: Boolean) | ||||
| } | ||||
|  | ||||
| private class VirtualZeropage(options: CompilationOptions): Zeropage(options) { | ||||
|     override val SCRATCH_B1: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|     override val SCRATCH_REG: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|     override val SCRATCH_W1: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|     override val SCRATCH_W2: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|     override val SCRATCH_PTR: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
| } | ||||
|   | ||||
| @@ -1,65 +0,0 @@ | ||||
| package prog8.code.target.atari | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class AtariMachineDefinition: IMachineDefinition { | ||||
|  | ||||
|     override val cpu = CpuType.CPU6502 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = 9.999999999e97 | ||||
|     override val FLOAT_MAX_NEGATIVE = -9.999999999e97 | ||||
|     override val FLOAT_MEM_SIZE = 6 | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x2000u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0u    // TODO | ||||
|     override val BSSHIGHRAM_END = 0u      // TODO | ||||
|     override val BSSGOLDENRAM_START = 0u  // TODO | ||||
|     override val BSSGOLDENRAM_END = 0u    // TODO | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number") | ||||
|  | ||||
|     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||
|         return if (compilerOptions.output == OutputType.XEX) | ||||
|             listOf("syslib") | ||||
|         else | ||||
|             emptyList() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||
|         val emulatorName: String | ||||
|         val cmdline: List<String> | ||||
|         when(selectedEmulator) { | ||||
|             1 -> { | ||||
|                 emulatorName = "atari800" | ||||
|                 cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex") | ||||
|             } | ||||
|             2 -> { | ||||
|                 emulatorName = "altirra" | ||||
|                 cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex") | ||||
|             } | ||||
|             else -> { | ||||
|                 System.err.println("Atari target only supports atari800 and altirra emulators.") | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // TODO monlist? | ||||
|  | ||||
|         println("\nStarting Atari800XL emulator $emulatorName...") | ||||
|         val processb = ProcessBuilder(cmdline).inheritIO() | ||||
|         val process: Process = processb.start() | ||||
|         process.waitFor() | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu        // TODO | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = AtariZeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) | ||||
|     } | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| package prog8.code.target.atari | ||||
|  | ||||
| import prog8.code.core.CompilationOptions | ||||
| import prog8.code.core.InternalCompilerException | ||||
| import prog8.code.core.Zeropage | ||||
| import prog8.code.core.ZeropageType | ||||
|  | ||||
| class AtariZeropage(options: CompilationOptions) : Zeropage(options) { | ||||
|  | ||||
|     override val SCRATCH_B1 = 0xcbu      // temp storage for a single byte | ||||
|     override val SCRATCH_REG = 0xccu     // temp storage for a register, must be B1+1 | ||||
|     override val SCRATCH_W1 = 0xcdu      // temp storage 1 for a word  $cd+$ce | ||||
|     override val SCRATCH_W2 = 0xcfu      // temp storage 2 for a word  $cf+$d0        TODO is $d0 okay to use? | ||||
|  | ||||
|     init { | ||||
|         if (options.floats) { | ||||
|             throw InternalCompilerException("Atari target doesn't yet support floating point routines") | ||||
|         } | ||||
|  | ||||
|         if (options.floats && options.zeropage !in arrayOf( | ||||
|                 ZeropageType.FLOATSAFE, | ||||
|                 ZeropageType.BASICSAFE, | ||||
|                 ZeropageType.DONTUSE | ||||
|             )) | ||||
|             throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'") | ||||
|  | ||||
|         when (options.zeropage) { | ||||
|             ZeropageType.FULL -> { | ||||
|                 // TODO all atari usable zero page locations, except the ones used by the system's IRQ routine | ||||
|                 free.addAll(0x00u..0xffu) | ||||
|                 // TODO atari  free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u))        // these are updated by IRQ | ||||
|             } | ||||
|             ZeropageType.KERNALSAFE -> { | ||||
|                 free.addAll(0x80u..0xffu)       // TODO | ||||
|             } | ||||
|             ZeropageType.BASICSAFE, | ||||
|             ZeropageType.FLOATSAFE -> { | ||||
|                 free.addAll(0x80u..0xffu)       // TODO | ||||
|                 free.removeAll(0xd4u .. 0xefu)      // floating point storage | ||||
|             } | ||||
|             ZeropageType.DONTUSE -> { | ||||
|                 free.clear()  // don't use zeropage at all | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         val distinctFree = free.distinct() | ||||
|         free.clear() | ||||
|         free.addAll(distinctFree) | ||||
|  | ||||
|         removeReservedFromFreePool() | ||||
|         retainAllowed() | ||||
|     } | ||||
|  | ||||
|     override fun allocateCx16VirtualRegisters() { | ||||
|         TODO("Not known if atari can put the virtual regs in ZP") | ||||
|     } | ||||
| } | ||||
| @@ -1,56 +0,0 @@ | ||||
| package prog8.code.target.c128 | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.C64Target | ||||
| import prog8.code.target.cbm.Mflpt5 | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class C128MachineDefinition: IMachineDefinition { | ||||
|  | ||||
|     override val cpu = CpuType.CPU6502 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x1c01u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0u    // TODO | ||||
|     override val BSSHIGHRAM_END = 0u      // TODO | ||||
|     override val BSSGOLDENRAM_START = 0u  // TODO | ||||
|     override val BSSGOLDENRAM_END = 0u    // TODO | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|  | ||||
|     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||
|         return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) | ||||
|             listOf("syslib") | ||||
|         else | ||||
|             emptyList() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||
|         if(selectedEmulator!=1) { | ||||
|             System.err.println("The c128 target only supports the main emulator (Vice).") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         println("\nStarting C-128 emulator x128...") | ||||
|         val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) | ||||
|         val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist, | ||||
|                 "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||
|         val processb = ProcessBuilder(cmdline).inheritIO() | ||||
|         val process: Process = processb.start() | ||||
|         process.waitFor() | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = C128Zeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY)    // TODO does the c128 have some of this somewhere? | ||||
|     } | ||||
| } | ||||
| @@ -1,66 +0,0 @@ | ||||
| package prog8.code.target.c64 | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.C64Target | ||||
| import prog8.code.target.cbm.Mflpt5 | ||||
| import java.io.IOException | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class C64MachineDefinition: IMachineDefinition { | ||||
|  | ||||
|     override val cpu = CpuType.CPU6502 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0801u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0xc000u | ||||
|     override val BSSHIGHRAM_END = 0xcfffu | ||||
|     override val BSSGOLDENRAM_START = 0u | ||||
|     override val BSSGOLDENRAM_END = 0u | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|  | ||||
|     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||
|         return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) | ||||
|             listOf("syslib") | ||||
|         else | ||||
|             emptyList() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||
|         if(selectedEmulator!=1) { | ||||
|             System.err.println("The c64 target only supports the main emulator (Vice).") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         for(emulator in listOf("x64sc", "x64")) { | ||||
|             println("\nStarting C-64 emulator $emulator...") | ||||
|             val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) | ||||
|             val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist, | ||||
|                     "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||
|             val processb = ProcessBuilder(cmdline).inheritIO() | ||||
|             val process: Process | ||||
|             try { | ||||
|                 process=processb.start() | ||||
|             } catch(x: IOException) { | ||||
|                 continue  // try the next emulator executable | ||||
|             } | ||||
|             process.waitFor() | ||||
|             break | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = C64Zeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u) | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,18 +0,0 @@ | ||||
| package prog8.code.target.cbm | ||||
|  | ||||
| import prog8.code.core.* | ||||
|  | ||||
|  | ||||
| internal object CbmMemorySizer: IMemSizer { | ||||
|     override fun memorySize(dt: DataType): Int { | ||||
|         return when(dt) { | ||||
|             in ByteDatatypes -> 1 | ||||
|             in WordDatatypes, in PassByReferenceDatatypes -> 2 | ||||
|             DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE | ||||
|             else -> throw IllegalArgumentException("invalid datatype") | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun memorySize(arrayDt: DataType, numElements: Int) = | ||||
|         memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| package prog8.code.target.cx16 | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.C64Target | ||||
| import prog8.code.target.cbm.Mflpt5 | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class CX16MachineDefinition: IMachineDefinition { | ||||
|  | ||||
|     override val cpu = CpuType.CPU65c02 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0801u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0xa000u     // hiram bank 1, 8Kb, assumed to be active | ||||
|     override val BSSHIGHRAM_END = 0xbfffu       // Rom starts at $c000 | ||||
|     override val BSSGOLDENRAM_START = 0x0400u | ||||
|     override val BSSGOLDENRAM_END = 0x07ffu | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||
|         return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) | ||||
|             listOf("syslib") | ||||
|         else | ||||
|             emptyList() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||
|         val emulator: String | ||||
|         val extraArgs: List<String> | ||||
|  | ||||
|         when(selectedEmulator) { | ||||
|             1 -> { | ||||
|                 emulator = "x16emu" | ||||
|                 extraArgs = listOf("-debug") | ||||
|             } | ||||
|             2 -> { | ||||
|                 emulator = "box16" | ||||
|                 extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString())) | ||||
|             } | ||||
|             else -> { | ||||
|                 System.err.println("Cx16 target only supports x16emu and box16 emulators.") | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         println("\nStarting Commander X16 emulator $emulator...") | ||||
|         val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs | ||||
|         val processb = ProcessBuilder(cmdline).inheritIO() | ||||
|         processb.environment()["PULSE_LATENCY_MSEC"] = "10" | ||||
|         val process: Process = processb.start() | ||||
|         process.waitFor() | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = CX16Zeropage(compilerOptions) | ||||
|         golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u) | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package prog8.code.target.cbm | ||||
| package prog8.code.target.encodings | ||||
| 
 | ||||
| import com.github.michaelbull.result.Ok | ||||
| import com.github.michaelbull.result.Result | ||||
| @@ -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 | ||||
							
								
								
									
										327
									
								
								codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								codeCore/src/prog8/code/target/encodings/C64osEncoding.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,327 @@ | ||||
| package prog8.code.target.encodings | ||||
|  | ||||
| import com.github.michaelbull.result.Err | ||||
| import com.github.michaelbull.result.Ok | ||||
| import com.github.michaelbull.result.Result | ||||
| import java.io.CharConversionException | ||||
|  | ||||
| object C64osEncoding { | ||||
|  | ||||
|     // decoding:  from C64 OS Screencodes (0-255) to unicode | ||||
|     // character table from: | ||||
|     // https://www.c64os.com/c64os/usersguide/appendices#charactersets | ||||
|  | ||||
|     private val decodingC64os = charArrayOf( | ||||
|         '@'     ,    //  @    0x00 -> COMMERCIAL AT | ||||
|         'a'     ,    //  a    0x01 -> LATIN SMALL LETTER A | ||||
|         'b'     ,    //  b    0x02 -> LATIN SMALL LETTER B | ||||
|         'c'     ,    //  c    0x03 -> LATIN SMALL LETTER C | ||||
|         'd'     ,    //  d    0x04 -> LATIN SMALL LETTER D | ||||
|         'e'     ,    //  e    0x05 -> LATIN SMALL LETTER E | ||||
|         'f'     ,    //  f    0x06 -> LATIN SMALL LETTER F | ||||
|         'g'     ,    //  g    0x07 -> LATIN SMALL LETTER G | ||||
|         'h'     ,    //  h    0x08 -> LATIN SMALL LETTER H | ||||
|         'i'     ,    //  i    0x09 -> LATIN SMALL LETTER I | ||||
|         'j'     ,    //  j    0x0A -> LATIN SMALL LETTER J | ||||
|         'k'     ,    //  k    0x0B -> LATIN SMALL LETTER K | ||||
|         'l'     ,    //  l    0x0C -> LATIN SMALL LETTER L | ||||
|         'm'     ,    //  m    0x0D -> LATIN SMALL LETTER M | ||||
|         'n'     ,    //  n    0x0E -> LATIN SMALL LETTER N | ||||
|         'o'     ,    //  o    0x0F -> LATIN SMALL LETTER O | ||||
|         'p'     ,    //  p    0x10 -> LATIN SMALL LETTER P | ||||
|         'q'     ,    //  q    0x11 -> LATIN SMALL LETTER Q | ||||
|         'r'     ,    //  r    0x12 -> LATIN SMALL LETTER R | ||||
|         's'     ,    //  s    0x13 -> LATIN SMALL LETTER S | ||||
|         't'     ,    //  t    0x14 -> LATIN SMALL LETTER T | ||||
|         'u'     ,    //  u    0x15 -> LATIN SMALL LETTER U | ||||
|         'v'     ,    //  v    0x16 -> LATIN SMALL LETTER V | ||||
|         'w'     ,    //  w    0x17 -> LATIN SMALL LETTER W | ||||
|         'x'     ,    //  x    0x18 -> LATIN SMALL LETTER X | ||||
|         'y'     ,    //  y    0x19 -> LATIN SMALL LETTER Y | ||||
|         'z'     ,    //  z    0x1A -> LATIN SMALL LETTER Z | ||||
|         '['     ,    //  [    0x1B -> LEFT SQUARE BRACKET | ||||
|         '\\'    ,    //  \    0x1C -> REVERSE SOLIDUS | ||||
|         ']'     ,    //  ]    0x1D -> RIGHT SQUARE BRACKET | ||||
|         '^'     ,    //  ^    0x1E -> CIRCUMFLEX | ||||
|         '_'     ,    //  _    0x1F -> UNDERSCORE | ||||
|         ' '     ,    //       0x20 -> SPACE | ||||
|         '!'     ,    //  !    0x21 -> EXCLAMATION MARK | ||||
|         '"'     ,    //  "    0x22 -> QUOTATION MARK | ||||
|         '#'     ,    //  #    0x23 -> NUMBER SIGN | ||||
|         '$'     ,    //  $    0x24 -> DOLLAR SIGN | ||||
|         '%'     ,    //  %    0x25 -> PERCENT SIGN | ||||
|         '&'     ,    //  &    0x26 -> AMPERSAND | ||||
|         '\''    ,    //  '    0x27 -> APOSTROPHE | ||||
|         '('     ,    //  (    0x28 -> LEFT PARENTHESIS | ||||
|         ')'     ,    //  )    0x29 -> RIGHT PARENTHESIS | ||||
|         '*'     ,    //  *    0x2A -> ASTERISK | ||||
|         '+'     ,    //  +    0x2B -> PLUS SIGN | ||||
|         ','     ,    //  ,    0x2C -> COMMA | ||||
|         '-'     ,    //  -    0x2D -> HYPHEN-MINUS | ||||
|         '.'     ,    //  .    0x2E -> FULL STOP | ||||
|         '/'     ,    //  /    0x2F -> SOLIDUS | ||||
|         '0'     ,    //  0    0x30 -> DIGIT ZERO | ||||
|         '1'     ,    //  1    0x31 -> DIGIT ONE | ||||
|         '2'     ,    //  2    0x32 -> DIGIT TWO | ||||
|         '3'     ,    //  3    0x33 -> DIGIT THREE | ||||
|         '4'     ,    //  4    0x34 -> DIGIT FOUR | ||||
|         '5'     ,    //  5    0x35 -> DIGIT FIVE | ||||
|         '6'     ,    //  6    0x36 -> DIGIT SIX | ||||
|         '7'     ,    //  7    0x37 -> DIGIT SEVEN | ||||
|         '8'     ,    //  8    0x38 -> DIGIT EIGHT | ||||
|         '9'     ,    //  9    0x39 -> DIGIT NINE | ||||
|         ':'     ,    //  :    0x3A -> COLON | ||||
|         ';'     ,    //  ;    0x3B -> SEMICOLON | ||||
|         '<'     ,    //  <    0x3C -> LESS-THAN SIGN | ||||
|         '='     ,    //  =    0x3D -> EQUALS SIGN | ||||
|         '>'     ,    //  >    0x3E -> GREATER-THAN SIGN | ||||
|         '?'     ,    //  ?    0x3F -> QUESTION MARK | ||||
|         '`'     ,    //  `    0x40 -> GRAVE ACCENT | ||||
|         'A'     ,    //  A    0x41 -> LATIN CAPITAL LETTER A | ||||
|         'B'     ,    //  B    0x42 -> LATIN CAPITAL LETTER B | ||||
|         'C'     ,    //  C    0x43 -> LATIN CAPITAL LETTER C | ||||
|         'D'     ,    //  D    0x44 -> LATIN CAPITAL LETTER D | ||||
|         'E'     ,    //  E    0x45 -> LATIN CAPITAL LETTER E | ||||
|         'F'     ,    //  F    0x46 -> LATIN CAPITAL LETTER F | ||||
|         'G'     ,    //  G    0x47 -> LATIN CAPITAL LETTER G | ||||
|         'H'     ,    //  H    0x48 -> LATIN CAPITAL LETTER H | ||||
|         'I'     ,    //  I    0x49 -> LATIN CAPITAL LETTER I | ||||
|         'J'     ,    //  J    0x4A -> LATIN CAPITAL LETTER J | ||||
|         'K'     ,    //  K    0x4B -> LATIN CAPITAL LETTER K | ||||
|         'L'     ,    //  L    0x4C -> LATIN CAPITAL LETTER L | ||||
|         'M'     ,    //  M    0x4D -> LATIN CAPITAL LETTER M | ||||
|         'N'     ,    //  N    0x4E -> LATIN CAPITAL LETTER N | ||||
|         'O'     ,    //  O    0x4F -> LATIN CAPITAL LETTER O | ||||
|         'P'     ,    //  P    0x50 -> LATIN CAPITAL LETTER P | ||||
|         'Q'     ,    //  Q    0x51 -> LATIN CAPITAL LETTER Q | ||||
|         'R'     ,    //  R    0x52 -> LATIN CAPITAL LETTER R | ||||
|         'S'     ,    //  S    0x53 -> LATIN CAPITAL LETTER S | ||||
|         'T'     ,    //  T    0x54 -> LATIN CAPITAL LETTER T | ||||
|         'U'     ,    //  U    0x55 -> LATIN CAPITAL LETTER U | ||||
|         'V'     ,    //  V    0x56 -> LATIN CAPITAL LETTER V | ||||
|         'W'     ,    //  W    0x57 -> LATIN CAPITAL LETTER W | ||||
|         'X'     ,    //  X    0x58 -> LATIN CAPITAL LETTER X | ||||
|         'Y'     ,    //  Y    0x59 -> LATIN CAPITAL LETTER Y | ||||
|         'Z'     ,    //  Z    0x5A -> LATIN CAPITAL LETTER Z | ||||
|         '{'     ,    //  {    0x5B -> LEFT BRACE | ||||
|         '|'     ,    //  |    0x5C -> VERTICAL BAR | ||||
|         '}'     ,    //  }    0x5D -> RIGHT BRACE | ||||
|         '~'     ,    //  ~    0x5E -> TILDE | ||||
|         '\ufffe',    //       0x5F -> RESERVED | ||||
|         '\u00a0',    //       0x60 -> NO-BREAK SPACE (TRANSPARENT) | ||||
|         '\ufffe',    //       0x61 -> COMMODORE SYMBOL | ||||
|         '\u2191',    //  ↑    0x62 -> UP ARROW | ||||
|         '\u2193',    //  ↓    0x63 -> DOWN ARROW | ||||
|         '\u2190',    //  ←    0x64 -> LEFT ARROW | ||||
|         '\u2192',    //  →    0x65 -> RIGHT ARROW | ||||
|         '\u231A',    //  ⌚   0x66 -> WATCH (ANALOG CLOCKFACE) | ||||
|         '\u21BB',    //  ↻    0x67 -> CYCLE ARROWS | ||||
|         '\u2026',    //  …    0x68 -> ELLIPSIS | ||||
|         '\u25a7',    //  ▧    0x69 -> DIAGNONAL STRIPES | ||||
|         '\u2610',    //  ☐    0x6A -> CHECKBOX UNCHECKED | ||||
|         '\u2611',    //  ☑    0x6B -> CHECKBOX CHECKED | ||||
|         '\ufffe',    //       0x6C -> RADIO BUTTON UNSELECTED | ||||
|         '\ufffe',    //       0x6D -> RADIO BUTTON SELECTED | ||||
|         '\ufffe',    //       0x6E -> UTILITY CLOSE BUTTON | ||||
|         '\ufffe',    //       0x6F -> UTILITY TITLE BAR | ||||
|         '\u00a9',    //  ©    0x70 -> COPYRIGHT | ||||
|         '\u2713',    //  ✓    0x71 -> CHECKMARK | ||||
|         '\u2261',    //  ≡    0x72 -> THREE HORIZONTAL STRIPES | ||||
|         '\ufffe',    //       0x73 -> TICK TRACK | ||||
|         '\ufffe',    //       0x74 -> TICK TRACK NUB | ||||
|         '\ufffe',    //       0x75 -> TAB CORNER | ||||
|         '\u2980',    //  ⦀    0x76 -> THREE VERTICAL STRIPES | ||||
|         '\ufffe',    //       0x77 -> CUSTOM 1 | ||||
|         '\ufffe',    //       0x78 -> CUSTOM 2 | ||||
|         '\ufffe',    //       0x79 -> CUSTOM 3 | ||||
|         '\ufffe',    //       0x7A -> CUSTOM 4 | ||||
|         '\ufffe',    //       0x7B -> CUSTOM 5 | ||||
|         '\ufffe',    //       0x7C -> CUSTOM 6 | ||||
|         '\ufffe',    //       0x7D -> CUSTOM 7 | ||||
|         '\ufffe',    //       0x7E -> CUSTOM 8 | ||||
|         '\ufffe',    //       0x7F -> CUSTOM 9 | ||||
|         '\ufffe',    //       0x80 -> REVERSED COMMERCIAL AT | ||||
|         '\ufffe',    //       0x81 -> REVERSED LATIN SMALL LETTER A | ||||
|         '\ufffe',    //       0x82 -> REVERSED LATIN SMALL LETTER B | ||||
|         '\ufffe',    //       0x83 -> REVERSED LATIN SMALL LETTER C | ||||
|         '\ufffe',    //       0x84 -> REVERSED LATIN SMALL LETTER D | ||||
|         '\ufffe',    //       0x85 -> REVERSED LATIN SMALL LETTER E | ||||
|         '\ufffe',    //       0x86 -> REVERSED LATIN SMALL LETTER F | ||||
|         '\ufffe',    //       0x87 -> REVERSED LATIN SMALL LETTER G | ||||
|         '\ufffe',    //       0x88 -> REVERSED LATIN SMALL LETTER H | ||||
|         '\ufffe',    //       0x89 -> REVERSED LATIN SMALL LETTER I | ||||
|         '\ufffe',    //       0x8A -> REVERSED LATIN SMALL LETTER J | ||||
|         '\ufffe',    //       0x8B -> REVERSED LATIN SMALL LETTER K | ||||
|         '\ufffe',    //       0x8C -> REVERSED LATIN SMALL LETTER L | ||||
|         '\ufffe',    //       0x8D -> REVERSED LATIN SMALL LETTER M | ||||
|         '\ufffe',    //       0x8E -> REVERSED LATIN SMALL LETTER N | ||||
|         '\ufffe',    //       0x8F -> REVERSED LATIN SMALL LETTER O | ||||
|         '\ufffe',    //       0x90 -> REVERSED LATIN SMALL LETTER P | ||||
|         '\ufffe',    //       0x91 -> REVERSED LATIN SMALL LETTER Q | ||||
|         '\ufffe',    //       0x92 -> REVERSED LATIN SMALL LETTER R | ||||
|         '\ufffe',    //       0x93 -> REVERSED LATIN SMALL LETTER S | ||||
|         '\ufffe',    //       0x94 -> REVERSED LATIN SMALL LETTER T | ||||
|         '\ufffe',    //       0x95 -> REVERSED LATIN SMALL LETTER U | ||||
|         '\ufffe',    //       0x96 -> REVERSED LATIN SMALL LETTER V | ||||
|         '\ufffe',    //       0x97 -> REVERSED LATIN SMALL LETTER W | ||||
|         '\ufffe',    //       0x98 -> REVERSED LATIN SMALL LETTER X | ||||
|         '\ufffe',    //       0x99 -> REVERSED LATIN SMALL LETTER Y | ||||
|         '\ufffe',    //       0x9A -> REVERSED LATIN SMALL LETTER Z | ||||
|         '\ufffe',    //       0x9B -> REVERSED LEFT SQUARE BRACKET | ||||
|         '\ufffe',    //       0x9C -> REVERSED REVERSE SOLIDUS | ||||
|         '\ufffe',    //       0x9D -> REVERSED RIGHT SQUARE BRACKET | ||||
|         '\ufffe',    //       0x9E -> REVERSED CIRCUMFLEX | ||||
|         '\ufffe',    //       0x9F -> REVERSED UNDERSCORE | ||||
|         '\ufffe',    //       0xA0 -> REVERSED SPACE | ||||
|         '\ufffe',    //       0xA1 -> REVERSED EXCLAMATION MARK | ||||
|         '\ufffe',    //       0xA2 -> REVERSED QUOTATION MARK | ||||
|         '\ufffe',    //       0xA3 -> REVERSED NUMBER SIGN | ||||
|         '\ufffe',    //       0xA4 -> REVERSED DOLLAR SIGN | ||||
|         '\ufffe',    //       0xA5 -> REVERSED PERCENT SIGN | ||||
|         '\ufffe',    //       0xA6 -> REVERSED AMPERSAND | ||||
|         '\ufffe',    //       0xA7 -> REVERSED APOSTROPHE | ||||
|         '\ufffe',    //       0xA8 -> REVERSED LEFT PARENTHESIS | ||||
|         '\ufffe',    //       0xA9 -> REVERSED RIGHT PARENTHESIS | ||||
|         '\ufffe',    //       0xAA -> REVERSED ASTERISK | ||||
|         '\ufffe',    //       0xAB -> REVERSED PLUS SIGN | ||||
|         '\ufffe',    //       0xAC -> REVERSED COMMA | ||||
|         '\ufffe',    //       0xAD -> REVERSED HYPHEN-MINUS | ||||
|         '\ufffe',    //       0xAE -> REVERSED FULL STOP | ||||
|         '\ufffe',    //       0xAF -> REVERSED SOLIDUS | ||||
|         '\ufffe',    //       0xB0 -> REVERSED DIGIT ZERO | ||||
|         '\ufffe',    //       0xB1 -> REVERSED DIGIT ONE | ||||
|         '\ufffe',    //       0xB2 -> REVERSED DIGIT TWO | ||||
|         '\ufffe',    //       0xB3 -> REVERSED DIGIT THREE | ||||
|         '\ufffe',    //       0xB4 -> REVERSED DIGIT FOUR | ||||
|         '\ufffe',    //       0xB5 -> REVERSED DIGIT FIVE | ||||
|         '\ufffe',    //       0xB6 -> REVERSED DIGIT SIX | ||||
|         '\ufffe',    //       0xB7 -> REVERSED DIGIT SEVEN | ||||
|         '\ufffe',    //       0xB8 -> REVERSED DIGIT EIGHT | ||||
|         '\ufffe',    //       0xB9 -> REVERSED DIGIT NINE | ||||
|         '\ufffe',    //       0xBA -> REVERSED COLON | ||||
|         '\ufffe',    //       0xBB -> REVERSED SEMICOLON | ||||
|         '\ufffe',    //       0xBC -> REVERSED LESS-THAN SIGN | ||||
|         '\ufffe',    //       0xBD -> REVERSED EQUALS SIGN | ||||
|         '\ufffe',    //       0xBE -> REVERSED GREATER-THAN SIGN | ||||
|         '\ufffe',    //       0xBF -> REVERSED QUESTION MARK | ||||
|         '\ufffe',    //       0xC0 -> REVERSED GRAVE ACCENT | ||||
|         '\ufffe',    //       0xC1 -> REVERSED LATIN CAPITAL LETTER A | ||||
|         '\ufffe',    //       0xC2 -> REVERSED LATIN CAPITAL LETTER B | ||||
|         '\ufffe',    //       0xC3 -> REVERSED LATIN CAPITAL LETTER C | ||||
|         '\ufffe',    //       0xC4 -> REVERSED LATIN CAPITAL LETTER D | ||||
|         '\ufffe',    //       0xC5 -> REVERSED LATIN CAPITAL LETTER E | ||||
|         '\ufffe',    //       0xC6 -> REVERSED LATIN CAPITAL LETTER F | ||||
|         '\ufffe',    //       0xC7 -> REVERSED LATIN CAPITAL LETTER G | ||||
|         '\ufffe',    //       0xC8 -> REVERSED LATIN CAPITAL LETTER H | ||||
|         '\ufffe',    //       0xC9 -> REVERSED LATIN CAPITAL LETTER I | ||||
|         '\ufffe',    //       0xCA -> REVERSED LATIN CAPITAL LETTER J | ||||
|         '\ufffe',    //       0xCB -> REVERSED LATIN CAPITAL LETTER K | ||||
|         '\ufffe',    //       0xCC -> REVERSED LATIN CAPITAL LETTER L | ||||
|         '\ufffe',    //       0xCD -> REVERSED LATIN CAPITAL LETTER M | ||||
|         '\ufffe',    //       0xCE -> REVERSED LATIN CAPITAL LETTER N | ||||
|         '\ufffe',    //       0xCF -> REVERSED LATIN CAPITAL LETTER O | ||||
|         '\ufffe',    //       0xD0 -> REVERSED LATIN CAPITAL LETTER P | ||||
|         '\ufffe',    //       0xD1 -> REVERSED LATIN CAPITAL LETTER Q | ||||
|         '\ufffe',    //       0xD2 -> REVERSED LATIN CAPITAL LETTER R | ||||
|         '\ufffe',    //       0xD3 -> REVERSED LATIN CAPITAL LETTER S | ||||
|         '\ufffe',    //       0xD4 -> REVERSED LATIN CAPITAL LETTER T | ||||
|         '\ufffe',    //       0xD5 -> REVERSED LATIN CAPITAL LETTER U | ||||
|         '\ufffe',    //       0xD6 -> REVERSED LATIN CAPITAL LETTER V | ||||
|         '\ufffe',    //       0xD7 -> REVERSED LATIN CAPITAL LETTER W | ||||
|         '\ufffe',    //       0xD8 -> REVERSED LATIN CAPITAL LETTER X | ||||
|         '\ufffe',    //       0xD9 -> REVERSED LATIN CAPITAL LETTER Y | ||||
|         '\ufffe',    //       0xDA -> REVERSED LATIN CAPITAL LETTER Z | ||||
|         '\ufffe',    //       0xDB -> REVERSED LEFT BRACE | ||||
|         '\ufffe',    //       0xDC -> REVERSED VERTICAL BAR | ||||
|         '\ufffe',    //       0xDD -> REVERSED RIGHT BRACE | ||||
|         '\ufffe',    //       0xDE -> REVERSED TILDE | ||||
|         '\ufffe',    //       0xDF -> RESERVED | ||||
|         '\ufffe',    //       0xE0 -> RESERVED | ||||
|         '\ufffe',    //       0xE1 -> REVERSED COMMODORE SYMBOL | ||||
|         '\ufffe',    //       0xE2 -> REVERSED UP ARROW | ||||
|         '\ufffe',    //       0xE3 -> REVERSED DOWN ARROW | ||||
|         '\ufffe',    //       0xE4 -> REVERSED LEFT ARROW | ||||
|         '\ufffe',    //       0xE5 -> REVERSED RIGHT ARROW | ||||
|         '\ufffe',    //       0xE6 -> REVERSED ANALOG CLOCKFACE | ||||
|         '\ufffe',    //       0xE7 -> REVERSED CYCLE ARROWS | ||||
|         '\ufffe',    //       0xE8 -> REVERSED ELLIPSIS | ||||
|         '\ufffe',    //       0xE9 -> REVERSED DIAGONAL STRIPES | ||||
|         '\ufffe',    //       0xEA -> REVERSED CHECKBOX UNCHECKED | ||||
|         '\ufffe',    //       0xEB -> REVERSED CHECKBOX CHECKED | ||||
|         '\ufffe',    //       0xEC -> REVERSED RADIO BUTTON UNSELECTED | ||||
|         '\ufffe',    //       0xED -> REVERSED RADIO BUTTON SELECTED | ||||
|         '\ufffe',    //       0xEE -> MEMORY CHIP ICON | ||||
|         '\u21e7',    //  ⇧    0xEF -> SHIFT SYMBOL | ||||
|         '\ufffe',    //       0xF0 -> REVERSED COPYRIGHT SYMBOL | ||||
|         '\ufffe',    //       0xF1 -> REVERSED CHECKMARK | ||||
|         '\ufffe',    //       0xF2 -> REVERSED THREE HORIZONTAL STRIPES | ||||
|         '\ufffe',    //       0xF3 -> REVERSED TICK TRACK | ||||
|         '\ufffe',    //       0xF4 -> REVERSED TICK TRACK NUB | ||||
|         '\ufffe',    //       0xF5 -> REVERSED TAB CORNER | ||||
|         '\ufffe',    //       0xF6 -> REVERSED THREE VERTICAL STRIPES | ||||
|         '\ufffe',    //       0xF7 -> CUSTOM 10 | ||||
|         '\ufffe',    //       0xF8 -> CUSTOM 11 | ||||
|         '\ufffe',    //       0xF9 -> CUSTOM 12 | ||||
|         '\ufffe',    //       0xFA -> CUSTOM 13 | ||||
|         '\ufffe',    //       0xFB -> CUSTOM 14 | ||||
|         '\ufffe',    //       0xFC -> CUSTOM 15 | ||||
|         '\ufffe',    //       0xFD -> CUSTOM 16 | ||||
|         '\ufffe',    //       0xFE -> CUSTOM 17 | ||||
|         '\ufffe'     //       0xFF -> CUSTOM 18 | ||||
|     ) | ||||
|  | ||||
|     // encoding:  from unicode to C64 OS Screencodes (0-255) | ||||
|     private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index} | ||||
|  | ||||
|     private fun replaceSpecial(chr: Char): Char = | ||||
|         when(chr) { | ||||
|             '\r' -> '\n'        // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d) | ||||
|             else -> chr | ||||
|         } | ||||
|      | ||||
|     fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> { | ||||
|         fun encodeChar(chr3: Char, lowercase: Boolean): UByte { | ||||
|             val chr = replaceSpecial(chr3) | ||||
|             val screencode = encodingC64os[chr] | ||||
|             return screencode?.toUByte() ?: when (chr) { | ||||
|                 '\u0000' -> 0u | ||||
|                 '\n' -> 13u | ||||
|                 in '\u8000'..'\u80ff' -> { | ||||
|                     // special case: take the lower 8 bit hex value directly | ||||
|                     (chr.code - 0x8000).toUByte() | ||||
|                 } | ||||
|                 else -> { | ||||
|                     if(chr.isISOControl()) | ||||
|                         throw CharConversionException("no c64os character for char #${chr.code}") | ||||
|                     else | ||||
|                         throw CharConversionException("no c64os character for char #${chr.code} '${chr}'") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return try { | ||||
|             Ok(text.map { | ||||
|                 try { | ||||
|                     encodeChar(it, lowercase) | ||||
|                 } catch (x: CharConversionException) { | ||||
|                     encodeChar(it, !lowercase) | ||||
|                 } | ||||
|             }) | ||||
|         } catch(cx: CharConversionException) { | ||||
|             Err(cx) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> { | ||||
|         return try { | ||||
|             Ok(screencode.map { | ||||
|                 val code = it.toInt() | ||||
|                 if(code<0 || code>= decodingC64os.size) | ||||
|                     throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}") | ||||
|                 decodingC64os[code] | ||||
|             }.joinToString("")) | ||||
|         } catch(ce: CharConversionException) { | ||||
|             Err(ce) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								codeCore/src/prog8/code/target/encodings/Cp437Encoding.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| package prog8.code.target.encodings | ||||
|  | ||||
| import com.github.michaelbull.result.Err | ||||
| import com.github.michaelbull.result.Ok | ||||
| import com.github.michaelbull.result.Result | ||||
| import java.io.CharConversionException | ||||
| import java.nio.charset.Charset | ||||
|  | ||||
| object Cp437Encoding { | ||||
|     val charset: Charset = Charset.forName("IBM437") | ||||
|  | ||||
|     fun encode(str: String): Result<List<UByte>, CharConversionException> { | ||||
|         return try { | ||||
|             val mapped = str.map { chr -> | ||||
|                 when (chr) { | ||||
|                     '\u0000' -> 0u | ||||
|                     '\u00a0' -> 255u | ||||
|                     '☺' -> 1u | ||||
|                     '☻' -> 2u | ||||
|                     '♥' -> 3u | ||||
|                     '♦' -> 4u | ||||
|                     '♣' -> 5u | ||||
|                     '♠' -> 6u | ||||
|                     '•' -> 7u | ||||
|                     '◘' -> 8u | ||||
|                     '○' -> 9u | ||||
|                     '◙' -> 10u | ||||
|                     '♂' -> 11u | ||||
|                     '♀' -> 12u | ||||
|                     '♪' -> 13u | ||||
|                     '♫' -> 14u | ||||
|                     '☼' -> 15u | ||||
|                     '►' -> 16u | ||||
|                     '◄' -> 17u | ||||
|                     '↕' -> 18u | ||||
|                     '‼' -> 19u | ||||
|                     '¶' -> 20u | ||||
|                     '§' -> 21u | ||||
|                     '▬' -> 22u | ||||
|                     '↨' -> 23u | ||||
|                     '↑' -> 24u | ||||
|                     '↓' -> 25u | ||||
|                     '→' -> 26u | ||||
|                     '←' -> 27u | ||||
|                     '∟' -> 28u | ||||
|                     '↔' -> 29u | ||||
|                     '▲' -> 30u | ||||
|                     '▼' -> 31u | ||||
|                     in '\u8000'..'\u80ff' -> { | ||||
|                         // special case: take the lower 8 bit hex value directly | ||||
|                         (chr.code - 0x8000).toUByte() | ||||
|                     } | ||||
|                     else -> charset.encode(chr.toString())[0].toUByte() | ||||
|                 } | ||||
|             } | ||||
|             Ok(mapped) | ||||
|         } catch (ce: CharConversionException) { | ||||
|             Err(ce) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> { | ||||
|         return try { | ||||
|             Ok(String(bytes.map { it.toByte() }.toByteArray(), charset)) | ||||
|         } catch (ce: CharConversionException) { | ||||
|             Err(ce) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,23 +1,24 @@ | ||||
| package prog8.code.target | ||||
| package prog8.code.target.encodings | ||||
| 
 | ||||
| import com.github.michaelbull.result.fold | ||||
| import prog8.code.core.Encoding | ||||
| import prog8.code.core.IStringEncoding | ||||
| import prog8.code.core.InternalCompilerException | ||||
| import prog8.code.target.cbm.AtasciiEncoding | ||||
| import prog8.code.target.cbm.IsoEncoding | ||||
| import prog8.code.target.cbm.PetsciiEncoding | ||||
| 
 | ||||
| 
 | ||||
| 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.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, newlineToCarriageReturn) | ||||
|             Encoding.ATASCII -> AtasciiEncoding.encode(str) | ||||
|             Encoding.C64OS -> C64osEncoding.encode(str) | ||||
|             else -> throw InternalCompilerException("unsupported encoding $encoding") | ||||
|         } | ||||
|         return coded.fold( | ||||
| @@ -29,8 +30,13 @@ object Encoder: IStringEncoding { | ||||
|         val decoded = when(encoding) { | ||||
|             Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true) | ||||
|             Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true) | ||||
|             Encoding.ISO -> IsoEncoding.decode(bytes) | ||||
|             Encoding.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, newlineToCarriageReturn) | ||||
|             Encoding.ATASCII -> AtasciiEncoding.decode(bytes) | ||||
|             Encoding.C64OS -> C64osEncoding.decode(bytes) | ||||
|             else -> throw InternalCompilerException("unsupported encoding $encoding") | ||||
|         } | ||||
|         return decoded.fold( | ||||
| @@ -38,4 +44,4 @@ object Encoder: IStringEncoding { | ||||
|             success = { it } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package prog8.code.target.cbm | ||||
| package prog8.code.target.encodings | ||||
| 
 | ||||
| import com.github.michaelbull.result.Err | ||||
| import com.github.michaelbull.result.Ok | ||||
| @@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result | ||||
| import java.io.CharConversionException | ||||
| import java.nio.charset.Charset | ||||
| 
 | ||||
| object IsoEncoding { | ||||
|     val charset: Charset = Charset.forName("ISO-8859-15") | ||||
| 
 | ||||
|     fun encode(str: String): Result<List<UByte>, CharConversionException> { | ||||
| open class IsoEncodingBase(charsetName: String) { | ||||
|     val charset: Charset = Charset.forName(charsetName) | ||||
| 
 | ||||
|     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,11 +29,21 @@ object IsoEncoding { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| object IsoEncoding: IsoEncodingBase("ISO-8859-15") | ||||
| object IsoCyrillicEncoding: IsoEncodingBase("ISO-8859-5") | ||||
| object IsoEasternEncoding: IsoEncodingBase("ISO-8859-16") | ||||
							
								
								
									
										128
									
								
								codeCore/src/prog8/code/target/encodings/KatakanaEncoding.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								codeCore/src/prog8/code/target/encodings/KatakanaEncoding.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| package prog8.code.target.encodings | ||||
|  | ||||
| import com.github.michaelbull.result.Err | ||||
| import com.github.michaelbull.result.Ok | ||||
| import com.github.michaelbull.result.Result | ||||
| import java.io.CharConversionException | ||||
| import java.nio.charset.Charset | ||||
|  | ||||
|  | ||||
| object JapaneseCharacterConverter { | ||||
|     // adapted from https://github.com/raminduw/Japanese-Character-Converter | ||||
|  | ||||
|     private val ZENKAKU_KATAKANA = charArrayOf( | ||||
|         'ァ', 'ア', 'ィ', 'イ', 'ゥ', | ||||
|         'ウ', 'ェ', 'エ', 'ォ', 'オ', 'カ', 'ガ', 'キ', 'ギ', 'ク', 'グ', 'ケ', 'ゲ', | ||||
|         'コ', 'ゴ', 'サ', 'ザ', 'シ', 'ジ', 'ス', 'ズ', 'セ', 'ゼ', 'ソ', 'ゾ', 'タ', | ||||
|         'ダ', 'チ', 'ヂ', 'ッ', 'ツ', 'ヅ', 'テ', 'デ', 'ト', 'ド', 'ナ', 'ニ', 'ヌ', | ||||
|         'ネ', 'ノ', 'ハ', 'バ', 'パ', 'ヒ', 'ビ', 'ピ', 'フ', 'ブ', 'プ', 'ヘ', 'ベ', | ||||
|         'ペ', 'ホ', 'ボ', 'ポ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ャ', 'ヤ', 'ュ', 'ユ', | ||||
|         'ョ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ヮ', 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン', | ||||
|         'ヴ', 'ヵ', 'ヶ' | ||||
|     ) | ||||
|  | ||||
|     private val HANKAKU_HIRAGANA = charArrayOf( | ||||
|         'ぁ', 'あ', 'ぃ', 'い', 'ぅ', 'う', 'ぇ', 'え', | ||||
|         'ぉ', 'お', 'か', 'が', 'き', 'ぎ', 'く', 'ぐ', | ||||
|         'け', 'げ', 'こ', 'ご', 'さ', 'ざ', 'し', 'じ', | ||||
|         'す', 'ず', 'せ', 'ぜ', 'そ', 'ぞ', 'た', 'だ', | ||||
|         'ち', 'ぢ', 'っ', 'つ', 'づ', 'て', 'で', 'と', | ||||
|         'ど', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ば', | ||||
|         'ぱ', 'ひ', 'び', 'ぴ', 'ふ', 'ぶ', 'ぷ', 'へ', | ||||
|         'べ', 'ぺ', 'ほ', 'ぼ', 'ぽ', 'ま', 'み', 'む', | ||||
|         'め', 'も', 'ゃ', 'や', 'ゅ', 'ゆ', 'ょ', 'よ', | ||||
|         'ら', 'り', 'る', 'れ', 'ろ', 'ゎ', 'わ', 'ゐ', | ||||
|         'ゑ', 'を', 'ん', 'ゔ', 'ゕ', 'ゖ' | ||||
|     ) | ||||
|  | ||||
|     private val HANKAKU_KATAKANA = arrayOf( | ||||
|         "ァ", "ア", "ィ", "イ", "ゥ", | ||||
|         "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ", | ||||
|         "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", | ||||
|         "ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", | ||||
|         "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ", | ||||
|         "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ", "ム", "メ", | ||||
|         "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", | ||||
|         "ワ", "イ", "エ", "ヲ", "ン", "ヴ", "カ", "ケ" | ||||
|     ) | ||||
|  | ||||
|     private val ZENKAKU_KATAKANA_FIRST_CHAR_CODE = ZENKAKU_KATAKANA.first().code | ||||
|     private val HANKAKU_HIRAGANA_FIRST_CHAR_CODE = HANKAKU_HIRAGANA.first().code | ||||
|  | ||||
|     private fun zenkakuKatakanaToHankakuKatakana(c: Char): String = if (c in ZENKAKU_KATAKANA) HANKAKU_KATAKANA[c.code - ZENKAKU_KATAKANA_FIRST_CHAR_CODE] else c.toString() | ||||
|     private fun hankakuKatakanaToZenkakuKatakana(c: Char): Char = if (c in HANKAKU_HIRAGANA) ZENKAKU_KATAKANA[c.code - HANKAKU_HIRAGANA_FIRST_CHAR_CODE] else c | ||||
|  | ||||
|     fun zenkakuKatakanaToHankakuKatakana(s: String): String = buildString { | ||||
|         for (element in s) { | ||||
|             val converted = hankakuKatakanaToZenkakuKatakana(element) | ||||
|             val convertedChar = zenkakuKatakanaToHankakuKatakana(converted) | ||||
|             append(convertedChar) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| object KatakanaEncoding { | ||||
|     val charset: Charset = Charset.forName("JIS_X0201") | ||||
|  | ||||
|     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 | ||||
|  | ||||
|                     '♥' -> 0xe3u | ||||
|                     '♦' -> 0xe4u | ||||
|                     '♣' -> 0xe5u | ||||
|                     '♠' -> 0xe6u | ||||
|  | ||||
|                     '大' -> 0xeau | ||||
|                     '中' -> 0xebu | ||||
|                     '小' -> 0xecu | ||||
|                     '百' -> 0xedu | ||||
|                     '千' -> 0xeeu | ||||
|                     '万' -> 0xefu | ||||
|                     '♪' -> 0xf0u | ||||
|                     '土' -> 0xf1u | ||||
|                     '金' -> 0xf2u | ||||
|                     '木' -> 0xf3u | ||||
|                     '水' -> 0xf4u | ||||
|                     '火' -> 0xf5u | ||||
|                     '月' -> 0xf6u | ||||
|                     '日' -> 0xf7u | ||||
|                     '時' -> 0xf8u | ||||
|                     '分' -> 0xf9u | ||||
|                     '秒' -> 0xfau | ||||
|                     '年' -> 0xfbu | ||||
|                     '円' -> 0xfcu | ||||
|                     '人' -> 0xfdu | ||||
|                     '生' -> 0xfeu | ||||
|                     '〒' -> 0xffu | ||||
|                     in '\u8000'..'\u80ff' -> { | ||||
|                         // special case: take the lower 8 bit hex value directly | ||||
|                         (chr.code - 0x8000).toUByte() | ||||
|                     } | ||||
|                     else -> charset.encode(chr.toString())[0].toUByte() | ||||
|                 } | ||||
|             } | ||||
|             Ok(mapped) | ||||
|         } catch (ce: CharConversionException) { | ||||
|             Err(ce) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> { | ||||
|         return try { | ||||
|             Ok(String(bytes.map { | ||||
|                 when(it) { | ||||
|                     13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13 | ||||
|                     else -> it.toByte() | ||||
|                 } | ||||
|             }.toByteArray(), charset)) | ||||
|         } catch (ce: CharConversionException) { | ||||
|             Err(ce) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package prog8.code.target.cbm | ||||
| package prog8.code.target.encodings | ||||
| 
 | ||||
| import com.github.michaelbull.result.Err | ||||
| import com.github.michaelbull.result.Ok | ||||
| @@ -7,7 +7,7 @@ import java.io.CharConversionException | ||||
| 
 | ||||
| object PetsciiEncoding { | ||||
| 
 | ||||
|     // decoding:  from Petscii/Screencodes (0-255) to unicode | ||||
|     // decoding:  from Petscii/Screencodes (0-255) to Unicode | ||||
|     // character tables used from https://github.com/irmen/cbmcodecs2 | ||||
| 
 | ||||
|     private val decodingPetsciiLowercase = charArrayOf( | ||||
| @@ -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) | ||||
| @@ -1089,7 +1089,7 @@ object PetsciiEncoding { | ||||
|             Ok(text.map { | ||||
|                 try { | ||||
|                     encodeChar(it, lowercase) | ||||
|                 } catch (x: CharConversionException) { | ||||
|                 } catch (_: CharConversionException) { | ||||
|                     encodeChar(it, !lowercase) | ||||
|                 } | ||||
|             }) | ||||
| @@ -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() | ||||
| @@ -1135,7 +1137,7 @@ object PetsciiEncoding { | ||||
|             Ok(text.map { | ||||
|                 try { | ||||
|                     encodeChar(it, lowercase) | ||||
|                 } catch (x: CharConversionException) { | ||||
|                 } catch (_: CharConversionException) { | ||||
|                     encodeChar(it, !lowercase) | ||||
|                 } | ||||
|             }) | ||||
| @@ -1157,16 +1159,16 @@ object PetsciiEncoding { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> { | ||||
|     fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> { | ||||
|         val code: UInt = when { | ||||
|             petscii_code <= 0x1fu -> petscii_code + 128u | ||||
|             petscii_code <= 0x3fu -> petscii_code.toUInt() | ||||
|             petscii_code <= 0x5fu -> petscii_code - 64u | ||||
|             petscii_code <= 0x7fu -> petscii_code - 32u | ||||
|             petscii_code <= 0x9fu -> petscii_code + 64u | ||||
|             petscii_code <= 0xbfu -> petscii_code - 64u | ||||
|             petscii_code <= 0xfeu -> petscii_code - 128u | ||||
|             petscii_code == 255.toUByte() -> 95u | ||||
|             petsciicode <= 0x1fu -> petsciicode + 128u | ||||
|             petsciicode <= 0x3fu -> petsciicode.toUInt() | ||||
|             petsciicode <= 0x5fu -> petsciicode - 64u | ||||
|             petsciicode <= 0x7fu -> petsciicode - 32u | ||||
|             petsciicode <= 0x9fu -> petsciicode + 64u | ||||
|             petsciicode <= 0xbfu -> petsciicode - 64u | ||||
|             petsciicode <= 0xfeu -> petsciicode - 128u | ||||
|             petsciicode == 255.toUByte() -> 95u | ||||
|             else -> return Err(CharConversionException("petscii code out of range")) | ||||
|         } | ||||
|         if(inverseVideo) { | ||||
| @@ -1,57 +0,0 @@ | ||||
| package prog8.code.target.pet | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import prog8.code.target.C64Target | ||||
| import prog8.code.target.cbm.Mflpt5 | ||||
| import java.nio.file.Path | ||||
|  | ||||
|  | ||||
| class PETMachineDefinition: IMachineDefinition { | ||||
|  | ||||
|     override val cpu = CpuType.CPU6502 | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0401u | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0u | ||||
|     override val BSSHIGHRAM_END = 0u | ||||
|     override val BSSGOLDENRAM_START = 0u | ||||
|     override val BSSGOLDENRAM_END = 0u | ||||
|  | ||||
|     override lateinit var zeropage: Zeropage | ||||
|     override lateinit var golden: GoldenRam | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||
|  | ||||
|     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||
|         return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) | ||||
|             listOf("syslib") | ||||
|         else | ||||
|             emptyList() | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||
|         if(selectedEmulator!=1) { | ||||
|             System.err.println("The pet target only supports the main emulator (Vice).") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         println("\nStarting PET emulator...") | ||||
|         val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) | ||||
|         val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist, | ||||
|                 "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||
|         val processb = ProcessBuilder(cmdline).inheritIO() | ||||
|         val process=processb.start() | ||||
|         process.waitFor() | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = PETZeropage(compilerOptions) | ||||
|         // there's no golden ram. | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,75 +0,0 @@ | ||||
| package prog8.code.target.virtual | ||||
|  | ||||
| import prog8.code.core.* | ||||
| import java.nio.file.Path | ||||
| import kotlin.io.path.isReadable | ||||
| import kotlin.io.path.name | ||||
| import kotlin.io.path.readText | ||||
|  | ||||
| class VirtualMachineDefinition: IMachineDefinition { | ||||
|  | ||||
|     override val cpu = CpuType.VIRTUAL | ||||
|  | ||||
|     override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble() | ||||
|     override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble() | ||||
|     override val FLOAT_MEM_SIZE = 8             // 64-bits double | ||||
|     override val PROGRAM_LOAD_ADDRESS = 0u      // not actually used | ||||
|  | ||||
|     override val BSSHIGHRAM_START = 0u          // not actually used | ||||
|     override val BSSHIGHRAM_END = 0u            // not actually used | ||||
|     override val BSSGOLDENRAM_START = 0u        // not actually used | ||||
|     override val BSSGOLDENRAM_END = 0u          // not actually used | ||||
|     override lateinit var zeropage: Zeropage    // not actually used | ||||
|     override lateinit var golden: GoldenRam     // not actually used | ||||
|  | ||||
|     override fun getFloatAsmBytes(num: Number): String { | ||||
|         // little endian binary representation | ||||
|         val bits = num.toDouble().toBits().toULong() | ||||
|         val hexStr = bits.toString(16).padStart(16, '0') | ||||
|         val parts = hexStr.chunked(2).map { "\$" + it } | ||||
|         return parts.joinToString(", ") | ||||
|     } | ||||
|  | ||||
|     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||
|         return listOf("syslib") | ||||
|     } | ||||
|  | ||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||
|         println("\nStarting Virtual Machine...") | ||||
|         // to not have external module dependencies in our own module, we launch the virtual machine via reflection | ||||
|         val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner | ||||
|         val filename = programNameWithPath.name | ||||
|         if(programNameWithPath.isReadable()) { | ||||
|             vm.runProgram(programNameWithPath.readText()) | ||||
|         } else { | ||||
|             val withExt = programNameWithPath.resolveSibling("$filename.p8ir") | ||||
|             if(withExt.isReadable()) | ||||
|                 vm.runProgram(withExt.readText()) | ||||
|             else | ||||
|                 throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file") | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun isIOAddress(address: UInt): Boolean = false | ||||
|  | ||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||
|         zeropage = VirtualZeropage(compilerOptions) | ||||
|     } | ||||
| } | ||||
|  | ||||
| interface IVirtualMachineRunner { | ||||
|     fun runProgram(irSource: String) | ||||
| } | ||||
|  | ||||
| private class VirtualZeropage(options: CompilationOptions): Zeropage(options) { | ||||
|     override val SCRATCH_B1: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|     override val SCRATCH_REG: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|     override val SCRATCH_W1: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|     override val SCRATCH_W2: UInt | ||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") | ||||
|  | ||||
|     override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package prog8.code.target.c128 | ||||
| package prog8.code.target.zp | ||||
| 
 | ||||
| import prog8.code.core.CompilationOptions | ||||
| import prog8.code.core.InternalCompilerException | ||||
| @@ -6,22 +6,17 @@ import prog8.code.core.Zeropage | ||||
| import prog8.code.core.ZeropageType | ||||
| 
 | ||||
| 
 | ||||
| // reference: "Mapping the C128" zero page chapter. | ||||
| // reference: "Mapping the C128" zeropage chapter. | ||||
| 
 | ||||
| class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | ||||
| 
 | ||||
|     override val SCRATCH_B1 = 0x74u      // temp storage for a single byte | ||||
|     override val SCRATCH_REG = 0x75u     // temp storage for a register, must be B1+1 | ||||
|     override val SCRATCH_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, | ||||
| @@ -33,18 +28,18 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | ||||
|             ZeropageType.FULL -> { | ||||
|                 // $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such | ||||
|                 free.addAll(0x0au..0xffu) | ||||
|                 free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u))        // these are updated/used by IRQ | ||||
|                 free.removeAll(arrayOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u))        // these are updated/used by IRQ | ||||
|             } | ||||
|             ZeropageType.KERNALSAFE -> { | ||||
|                 free.addAll(0x0au..0x8fu)       // BASIC variables | ||||
|                 free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, | ||||
|                 free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, | ||||
|                     0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u)) | ||||
|             } | ||||
|             ZeropageType.FLOATSAFE, | ||||
|             ZeropageType.BASICSAFE -> { | ||||
|                 free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au)) | ||||
|                 free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au)) | ||||
|                 free.addAll(0x1bu..0x23u) | ||||
|                 free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu, | ||||
|                 free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu, | ||||
|                     0x55u, 0x56u, 0x57u, 0x58u, | ||||
|                     0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, | ||||
|                     0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, | ||||
| @@ -53,7 +48,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | ||||
| 
 | ||||
|                 // if(options.zeropage==ZeropageType.BASICSAFE) { | ||||
|                     // can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0 | ||||
|                     free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu, | ||||
|                     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)) | ||||
| @@ -71,8 +66,4 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | ||||
|         removeReservedFromFreePool() | ||||
|         retainAllowed() | ||||
|     } | ||||
| 
 | ||||
|     override fun allocateCx16VirtualRegisters() { | ||||
|         TODO("Not known if C128 can put the virtual regs in ZP") | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user