mirror of
				https://github.com/irmen/prog8.git
				synced 2025-11-03 19:16:13 +00:00 
			
		
		
		
	Compare commits
	
		
			483 Commits
		
	
	
		
			v11.3.1
			...
			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 | 
							
								
								
									
										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/
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/all-ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/all-ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -20,10 +20,10 @@ jobs:
 | 
			
		||||
          make -j4
 | 
			
		||||
          sudo make install          
 | 
			
		||||
 | 
			
		||||
      - name: Set up JDK 11
 | 
			
		||||
      - name: Set up JDK 17
 | 
			
		||||
        uses: actions/setup-java@v4
 | 
			
		||||
        with:
 | 
			
		||||
          java-version: 11
 | 
			
		||||
          java-version: 17
 | 
			
		||||
          distribution: temurin
 | 
			
		||||
 | 
			
		||||
      - name: Build and test with Gradle
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,10 +7,13 @@ build/
 | 
			
		||||
dist/
 | 
			
		||||
output/
 | 
			
		||||
out/
 | 
			
		||||
out-new/
 | 
			
		||||
out-old/
 | 
			
		||||
.*cache/
 | 
			
		||||
*.directory
 | 
			
		||||
*.prg
 | 
			
		||||
*.bin
 | 
			
		||||
*.p8ir
 | 
			
		||||
*.labels.txt
 | 
			
		||||
*.vm.txt
 | 
			
		||||
*.vice-mon-list
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1288
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1288
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
								
							@@ -7,13 +7,13 @@
 | 
			
		||||
    <option name="jvmTarget" value="11" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="KotlinCommonCompilerArguments">
 | 
			
		||||
    <option name="apiVersion" value="2.1" />
 | 
			
		||||
    <option name="languageVersion" value="2.1" />
 | 
			
		||||
    <option name="apiVersion" value="2.2" />
 | 
			
		||||
    <option name="languageVersion" value="2.2" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="KotlinCompilerSettings">
 | 
			
		||||
    <option name="additionalArguments" value="-Xwhen-guards" />
 | 
			
		||||
    <option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="KotlinJpsPluginSettings">
 | 
			
		||||
    <option name="version" value="2.1.10" />
 | 
			
		||||
    <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:2.1.20" />
 | 
			
		||||
    <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" />
 | 
			
		||||
    <CLASSES>
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" />
 | 
			
		||||
    </CLASSES>
 | 
			
		||||
    <JAVADOC>
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" />
 | 
			
		||||
    </JAVADOC>
 | 
			
		||||
    <SOURCES>
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-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>
 | 
			
		||||
							
								
								
									
										4
									
								
								.idea/libraries/eclipse_lsp4j.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.idea/libraries/eclipse_lsp4j.xml
									
									
									
										generated
									
									
									
								
							@@ -4,8 +4,8 @@
 | 
			
		||||
    <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.12.1/gson-2.12.1.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.13.1/gson-2.13.1.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.38.0/error_prone_annotations-2.38.0.jar!/" />
 | 
			
		||||
    </CLASSES>
 | 
			
		||||
    <JAVADOC />
 | 
			
		||||
    <SOURCES />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								.idea/libraries/io_kotest_assertions_core_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								.idea/libraries/io_kotest_assertions_core_jvm.xml
									
									
									
										generated
									
									
									
								
							@@ -1,20 +0,0 @@
 | 
			
		||||
<component name="libraryTable">
 | 
			
		||||
  <library name="io.kotest.assertions.core.jvm" type="repository">
 | 
			
		||||
    <properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
 | 
			
		||||
    <CLASSES>
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
 | 
			
		||||
    </CLASSES>
 | 
			
		||||
    <JAVADOC />
 | 
			
		||||
    <SOURCES />
 | 
			
		||||
  </library>
 | 
			
		||||
</component>
 | 
			
		||||
							
								
								
									
										14
									
								
								.idea/libraries/michael_bull_kotlin_result_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								.idea/libraries/michael_bull_kotlin_result_jvm.xml
									
									
									
										generated
									
									
									
								
							@@ -1,19 +1,19 @@
 | 
			
		||||
<component name="libraryTable">
 | 
			
		||||
  <library name="michael.bull.kotlin.result.jvm" type="repository">
 | 
			
		||||
    <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
 | 
			
		||||
    <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" />
 | 
			
		||||
    <CLASSES>
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
 | 
			
		||||
    </CLASSES>
 | 
			
		||||
    <JAVADOC>
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
 | 
			
		||||
    </JAVADOC>
 | 
			
		||||
    <SOURCES>
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
 | 
			
		||||
      <root url="jar://$MAVEN_REPOSITORY$/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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							@@ -12,6 +12,7 @@
 | 
			
		||||
          <option name="pkg" value="" />
 | 
			
		||||
          <option name="language" value="Java" />
 | 
			
		||||
          <option name="generateListener" value="false" />
 | 
			
		||||
          <option name="generateVisitor" value="true" />
 | 
			
		||||
        </PerGrammarGenerationSettings>
 | 
			
		||||
      </list>
 | 
			
		||||
    </option>
 | 
			
		||||
@@ -25,4 +26,7 @@
 | 
			
		||||
  <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>
 | 
			
		||||
							
								
								
									
										1
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							@@ -15,6 +15,7 @@
 | 
			
		||||
      <module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
 | 
			
		||||
      <module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
 | 
			
		||||
      <module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
 | 
			
		||||
      <module fileurl="file://$PROJECT_DIR$/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" />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							@@ -61,6 +61,7 @@ What does Prog8 provide?
 | 
			
		||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
 | 
			
		||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
 | 
			
		||||
- various data types other than just bytes (16-bit words, floats, strings)
 | 
			
		||||
- Structs and typed pointers
 | 
			
		||||
- floating point math is supported on certain targets
 | 
			
		||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
 | 
			
		||||
- tight control over Zeropage usage
 | 
			
		||||
@@ -71,6 +72,7 @@ What does Prog8 provide?
 | 
			
		||||
- high-level program optimizations
 | 
			
		||||
- conditional branches that map 1:1 to cpu status flags
 | 
			
		||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
 | 
			
		||||
- ``on .. goto`` statement for fast jump tables
 | 
			
		||||
- ``in`` expression for concise and efficient multi-value/containment check 
 | 
			
		||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
 | 
			
		||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
 | 
			
		||||
@@ -95,7 +97,7 @@ What does Prog8 provide?
 | 
			
		||||
- "c64": Commodore-64  (6502 like CPU)
 | 
			
		||||
- "c128": Commodore-128  (6502 like CPU - the Z80 cpu mode is not supported)
 | 
			
		||||
- "pet32": Commodore PET (limited support)
 | 
			
		||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
 | 
			
		||||
- 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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -191,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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -16,6 +16,7 @@
 | 
			
		||||
%import b_textelite
 | 
			
		||||
%import b_maze
 | 
			
		||||
%import b_sprites
 | 
			
		||||
%import b_btree
 | 
			
		||||
 | 
			
		||||
%zeropage basicsafe
 | 
			
		||||
%option no_sysinit
 | 
			
		||||
@@ -34,7 +35,7 @@ main {
 | 
			
		||||
        txt.color2(1, 6)
 | 
			
		||||
        txt.clear_screen()
 | 
			
		||||
 | 
			
		||||
        txt.print("\n\n\n  prog8 compiler benchmark tests.\n")
 | 
			
		||||
        txt.print("\n\n\n  prog8 compiler benchmark tests.\n\n  benchmark version v12\n\n")
 | 
			
		||||
        sys.wait(60)
 | 
			
		||||
 | 
			
		||||
        benchmark_number = 0
 | 
			
		||||
@@ -64,13 +65,9 @@ main {
 | 
			
		||||
        benchmark_number++
 | 
			
		||||
 | 
			
		||||
        announce_benchmark("circles with gfx_lores")
 | 
			
		||||
        benchmark_score[benchmark_number]  = circles.draw(false, 300)
 | 
			
		||||
        benchmark_score[benchmark_number]  = circles.draw(false, 400)
 | 
			
		||||
        benchmark_number++
 | 
			
		||||
 | 
			
		||||
;        announce_benchmark("circles with kernal")
 | 
			
		||||
;        benchmark_score[benchmark_number]  = circles.draw(true, 300)
 | 
			
		||||
;        benchmark_number++
 | 
			
		||||
 | 
			
		||||
        announce_benchmark("text-elite")
 | 
			
		||||
        benchmark_score[benchmark_number]  = textelite.bench(120)
 | 
			
		||||
        benchmark_number++
 | 
			
		||||
@@ -79,28 +76,32 @@ main {
 | 
			
		||||
        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 final_score
 | 
			
		||||
        uword total_score
 | 
			
		||||
        benchmark_number = 0
 | 
			
		||||
        txt.print("\nscore benchmark\n\n")
 | 
			
		||||
        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])
 | 
			
		||||
            final_score += benchmark_score[benchmark_number]
 | 
			
		||||
            total_score += benchmark_score[benchmark_number]
 | 
			
		||||
            txt.nl()
 | 
			
		||||
            benchmark_number++
 | 
			
		||||
        } until benchmark_names[benchmark_number]==0
 | 
			
		||||
 | 
			
		||||
        txt.print("\n\nfinal score : ")
 | 
			
		||||
        txt.print_uw(final_score)
 | 
			
		||||
        txt.nl()
 | 
			
		||||
        txt.print("\n\ntotal score : ")
 | 
			
		||||
        txt.print_uw(total_score)
 | 
			
		||||
        txt.print(" (higher=better)\n")
 | 
			
		||||
 | 
			
		||||
        sub announce_benchmark(str name) {
 | 
			
		||||
            benchmark_names[benchmark_number] = name
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
 | 
			
		||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm") version "2.1.20"
 | 
			
		||||
    kotlin("jvm") version "2.2.20"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
allprojects {
 | 
			
		||||
@@ -18,6 +20,8 @@ allprojects {
 | 
			
		||||
        compilerOptions {
 | 
			
		||||
            freeCompilerArgs = listOf("-Xwhen-guards")
 | 
			
		||||
            jvmTarget = JvmTarget.JVM_11
 | 
			
		||||
            jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
 | 
			
		||||
            // languageVersion.set(KotlinVersion.KOTLIN_2_3)
 | 
			
		||||
        }
 | 
			
		||||
        sourceSets.all {
 | 
			
		||||
            languageSettings {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +5,7 @@ plugins {
 | 
			
		||||
dependencies {
 | 
			
		||||
    // should have no dependencies to other modules
 | 
			
		||||
    // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sourceSets {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,10 @@ import java.nio.file.Path
 | 
			
		||||
import kotlin.io.path.absolute
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// the automatically generated module where all string literals are interned to:
 | 
			
		||||
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_"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ class FSignature(val pure: Boolean,      // does it have side effects?
 | 
			
		||||
        val returns: ReturnConvention = when (returnType) {
 | 
			
		||||
            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)
 | 
			
		||||
@@ -45,6 +46,7 @@ class FSignature(val pure: Boolean,      // does it have side effects?
 | 
			
		||||
                when (val paramType = actualParamTypes.first()) {
 | 
			
		||||
                    BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
 | 
			
		||||
                    BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
 | 
			
		||||
                    BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15_32)
 | 
			
		||||
                    BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
 | 
			
		||||
                    in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
 | 
			
		||||
                    else -> ReturnConvention(paramType, null)
 | 
			
		||||
@@ -61,6 +63,7 @@ class FSignature(val pure: Boolean,      // does it have side effects?
 | 
			
		||||
                val paramConv = when(val paramType = actualParamTypes[0]) {
 | 
			
		||||
                    BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
 | 
			
		||||
                    BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
 | 
			
		||||
                    BaseDataType.LONG -> ParamConvention(paramType, 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)
 | 
			
		||||
@@ -88,20 +91,23 @@ class FSignature(val pure: Boolean,      // does it have side effects?
 | 
			
		||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
 | 
			
		||||
    "setlsb"  to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
 | 
			
		||||
    "setmsb"  to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
 | 
			
		||||
    "rol"     to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
 | 
			
		||||
    "ror"     to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
 | 
			
		||||
    "rol2"    to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
 | 
			
		||||
    "ror2"    to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
 | 
			
		||||
    "cmp"     to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)),  // cmp returns a status in the carry flag, but not a proper return value
 | 
			
		||||
    "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.toTypedArray())),
 | 
			
		||||
    "sizeof"        to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
 | 
			
		||||
    "offsetof"      to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)),
 | 
			
		||||
    "sgn"           to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
 | 
			
		||||
    "sqrt"          to FSignature(true, null, FParam("value", *NumericDatatypes)),
 | 
			
		||||
    "sqrt__ubyte"   to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
 | 
			
		||||
@@ -111,30 +117,42 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
 | 
			
		||||
    "divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
 | 
			
		||||
    "divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
 | 
			
		||||
    "lsb"           to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
 | 
			
		||||
    "lsw"           to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
 | 
			
		||||
    "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)),
 | 
			
		||||
    "pokew"         to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", 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),
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ class CompilationOptions(val output: OutputType,
 | 
			
		||||
                         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,
 | 
			
		||||
@@ -28,7 +29,6 @@ class CompilationOptions(val output: OutputType,
 | 
			
		||||
                         var varsGolden: Boolean = false,
 | 
			
		||||
                         var slabsHighBank: Int? = null,
 | 
			
		||||
                         var slabsGolden: Boolean = false,
 | 
			
		||||
                         var dontSplitWordArrays: Boolean = false,
 | 
			
		||||
                         var breakpointCpuInstruction: String? = null,
 | 
			
		||||
                         var ignoreFootguns: Boolean = false,
 | 
			
		||||
                         var outputDir: Path = Path(""),
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ fun Number.toHex(): String {
 | 
			
		||||
    //  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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,9 @@ enum class BaseDataType {
 | 
			
		||||
    STR,                // pass by reference
 | 
			
		||||
    ARRAY,              // pass by reference, subtype is the element type
 | 
			
		||||
    ARRAY_SPLITW,       // pass by reference, split word layout, subtype is the element type (restricted to word types)
 | 
			
		||||
    POINTER,            // typed pointer, subtype is whatever type is pointed to
 | 
			
		||||
    STRUCT_INSTANCE,    // the actual instance of a struct (not directly supported in the language yet, but we need its type)
 | 
			
		||||
    ARRAY_POINTER,      // array of pointers (uwords), subtype is whatever type each element points to
 | 
			
		||||
    UNDEFINED;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -26,6 +29,7 @@ enum class BaseDataType {
 | 
			
		||||
            this.isArray && other.isArray -> false
 | 
			
		||||
            this.isArray -> other != FLOAT
 | 
			
		||||
            this == STR -> other != FLOAT
 | 
			
		||||
            this.isPointer -> other.isByteOrBool
 | 
			
		||||
            else -> true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -34,7 +38,8 @@ enum class BaseDataType {
 | 
			
		||||
            this == other -> true
 | 
			
		||||
            this.isArray && other.isArray -> true
 | 
			
		||||
            this.isByteOrBool -> other.isByteOrBool
 | 
			
		||||
            this.isWord -> other.isWord
 | 
			
		||||
            this.isWord -> other.isWord || other.isPointer
 | 
			
		||||
            this.isPointer -> other.isWord
 | 
			
		||||
            this == STR && other== UWORD || this== UWORD && other== STR -> true
 | 
			
		||||
            this == STR && other.isArray -> true
 | 
			
		||||
            this.isArray && other == STR -> true
 | 
			
		||||
@@ -45,89 +50,170 @@ enum class BaseDataType {
 | 
			
		||||
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
 | 
			
		||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
 | 
			
		||||
val BaseDataType.isIterable get() =  this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
 | 
			
		||||
val BaseDataType.isPassByRef get() = this.isIterable
 | 
			
		||||
val BaseDataType.isPassByValue get() = !this.isIterable
 | 
			
		||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
 | 
			
		||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
 | 
			
		||||
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
 | 
			
		||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
 | 
			
		||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER       // pointer arrays are also always stored as split uwords
 | 
			
		||||
val BaseDataType.isIterable get() =  this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
 | 
			
		||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
 | 
			
		||||
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
 | 
			
		||||
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 {
 | 
			
		||||
        if(base.isArray) {
 | 
			
		||||
            require(sub != null)
 | 
			
		||||
        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)
 | 
			
		||||
            }
 | 
			
		||||
        else if(base==BaseDataType.STR)
 | 
			
		||||
            require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
 | 
			
		||||
        else
 | 
			
		||||
            require(sub == null) { "only string and array base types can have a subtype"}
 | 
			
		||||
            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
 | 
			
		||||
        return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int = Objects.hash(base, sub)
 | 
			
		||||
    override fun hashCode(): Int = Objects.hash(base, sub, subType)
 | 
			
		||||
 | 
			
		||||
    fun setActualSubType(actualSubType: ISubType) {
 | 
			
		||||
        subType = actualSubType
 | 
			
		||||
        subTypeFromAntlr = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        val UBYTE = DataType(BaseDataType.UBYTE, null)
 | 
			
		||||
        val BYTE = DataType(BaseDataType.BYTE, null)
 | 
			
		||||
        val UWORD = DataType(BaseDataType.UWORD, null)
 | 
			
		||||
        val WORD = DataType(BaseDataType.WORD, null)
 | 
			
		||||
        val LONG = DataType(BaseDataType.LONG, null)
 | 
			
		||||
        val FLOAT = DataType(BaseDataType.FLOAT, null)
 | 
			
		||||
        val BOOL = DataType(BaseDataType.BOOL, null)
 | 
			
		||||
        val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
 | 
			
		||||
        val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
 | 
			
		||||
        val UBYTE = DataType(BaseDataType.UBYTE, null, null)
 | 
			
		||||
        val BYTE = DataType(BaseDataType.BYTE, null, null)
 | 
			
		||||
        val UWORD = DataType(BaseDataType.UWORD, null, null)
 | 
			
		||||
        val WORD = DataType(BaseDataType.WORD, null, null)
 | 
			
		||||
        val LONG = DataType(BaseDataType.LONG, null, null)
 | 
			
		||||
        val FLOAT = DataType(BaseDataType.FLOAT, null, null)
 | 
			
		||||
        val BOOL = DataType(BaseDataType.BOOL, null, null)
 | 
			
		||||
        val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
 | 
			
		||||
        val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
 | 
			
		||||
 | 
			
		||||
        private val simpletypes = mapOf(
 | 
			
		||||
            BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
 | 
			
		||||
            BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
 | 
			
		||||
            BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
 | 
			
		||||
            BaseDataType.WORD to DataType(BaseDataType.WORD, null),
 | 
			
		||||
            BaseDataType.LONG to DataType(BaseDataType.LONG, null),
 | 
			
		||||
            BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
 | 
			
		||||
            BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
 | 
			
		||||
            BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
 | 
			
		||||
            BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
 | 
			
		||||
            BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
 | 
			
		||||
            BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
 | 
			
		||||
            BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
 | 
			
		||||
            BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
 | 
			
		||||
            BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
 | 
			
		||||
            BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
 | 
			
		||||
            BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
 | 
			
		||||
            BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
 | 
			
		||||
            BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
 | 
			
		||||
        fun forDt(dt: BaseDataType): DataType {
 | 
			
		||||
            if(dt.isStructInstance)
 | 
			
		||||
                TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
 | 
			
		||||
            return simpletypes.getValue(dt)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
 | 
			
		||||
            require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
 | 
			
		||||
            val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt      // array of strings is actually just an array of UWORD pointers
 | 
			
		||||
            return if(splitwordarray && actualElementDt.isWord)
 | 
			
		||||
                DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
 | 
			
		||||
                DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
 | 
			
		||||
            else {
 | 
			
		||||
                if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
 | 
			
		||||
                    DataType(BaseDataType.ARRAY, actualElementDt)
 | 
			
		||||
                if(actualElementDt.isNumericOrBool)
 | 
			
		||||
                    DataType(BaseDataType.ARRAY, actualElementDt, null)
 | 
			
		||||
                else
 | 
			
		||||
                    throw NoSuchElementException("invalid element dt $elementDt")
 | 
			
		||||
                    throw NoSuchElementException("invalid basic element dt $elementDt")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
 | 
			
		||||
        fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
 | 
			
		||||
        fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
 | 
			
		||||
            DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
 | 
			
		||||
 | 
			
		||||
        fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
 | 
			
		||||
        fun pointer(dt: DataType): DataType = if(dt.isBasic)
 | 
			
		||||
                DataType(BaseDataType.POINTER, dt.base, null)
 | 
			
		||||
            else
 | 
			
		||||
                DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
 | 
			
		||||
        fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType)
 | 
			
		||||
        fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
 | 
			
		||||
        fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
 | 
			
		||||
        fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun elementToArray(splitwords: Boolean = true): DataType {
 | 
			
		||||
        return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
 | 
			
		||||
        else arrayFor(base, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun elementType(): DataType =
 | 
			
		||||
        if(base.isArray || base==BaseDataType.STR)
 | 
			
		||||
            forDt(sub!!)
 | 
			
		||||
        else
 | 
			
		||||
            throw IllegalArgumentException("not an array")
 | 
			
		||||
        when {
 | 
			
		||||
            isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
 | 
			
		||||
            base.isArray || base==BaseDataType.STR -> forDt(sub!!)
 | 
			
		||||
            else -> throw IllegalArgumentException("not an array")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    fun typeForAddressOf(msb: Boolean): DataType {
 | 
			
		||||
        if (isUndefined)
 | 
			
		||||
            return if(msb) pointer(BaseDataType.UBYTE) else UWORD
 | 
			
		||||
        else {
 | 
			
		||||
            if (isBasic)
 | 
			
		||||
                return pointer(base)
 | 
			
		||||
            if (isString)
 | 
			
		||||
                return pointer(BaseDataType.UBYTE)
 | 
			
		||||
            if (isPointer)
 | 
			
		||||
                return UWORD
 | 
			
		||||
            if (isArray) {
 | 
			
		||||
                if (msb || isSplitWordArray)
 | 
			
		||||
                    return pointer(BaseDataType.UBYTE)
 | 
			
		||||
                val elementDt = elementType()
 | 
			
		||||
                require(elementDt.isBasic)
 | 
			
		||||
                return pointer(elementDt)
 | 
			
		||||
            }
 | 
			
		||||
            if (subType != null)
 | 
			
		||||
                return pointer(this)
 | 
			
		||||
            return UWORD
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun dereference(): DataType {
 | 
			
		||||
        require(isPointer || isUnsignedWord)
 | 
			
		||||
        return when {
 | 
			
		||||
            isUnsignedWord -> forDt(BaseDataType.UBYTE)
 | 
			
		||||
            sub!=null -> forDt(sub)
 | 
			
		||||
            subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
 | 
			
		||||
            subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
 | 
			
		||||
            else -> throw IllegalArgumentException("cannot dereference this pointer type")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = when(base) {
 | 
			
		||||
        BaseDataType.ARRAY -> {
 | 
			
		||||
@@ -138,6 +224,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
 | 
			
		||||
                BaseDataType.WORD -> "word[]"
 | 
			
		||||
                BaseDataType.UBYTE -> "ubyte[]"
 | 
			
		||||
                BaseDataType.UWORD -> "uword[]"
 | 
			
		||||
                BaseDataType.LONG -> "long[]"
 | 
			
		||||
                else -> throw IllegalArgumentException("invalid sub type")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -148,6 +235,15 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
 | 
			
		||||
                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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -160,6 +256,30 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
 | 
			
		||||
        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["
 | 
			
		||||
@@ -167,6 +287,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
 | 
			
		||||
                BaseDataType.BOOL -> "bool["
 | 
			
		||||
                BaseDataType.BYTE -> "byte["
 | 
			
		||||
                BaseDataType.WORD -> "@nosplit word["
 | 
			
		||||
                BaseDataType.LONG -> "long["
 | 
			
		||||
                BaseDataType.FLOAT -> "float["
 | 
			
		||||
                else -> throw IllegalArgumentException("invalid sub type")
 | 
			
		||||
            }
 | 
			
		||||
@@ -187,18 +308,37 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
 | 
			
		||||
            BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
 | 
			
		||||
            BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
 | 
			
		||||
            BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
 | 
			
		||||
            BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
 | 
			
		||||
            BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
 | 
			
		||||
            BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
 | 
			
		||||
            BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
 | 
			
		||||
            BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
 | 
			
		||||
            BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
 | 
			
		||||
            BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
 | 
			
		||||
            BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
 | 
			
		||||
            BaseDataType.POINTER -> {
 | 
			
		||||
                when {
 | 
			
		||||
                    targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
 | 
			
		||||
                    targetType.isPointer -> this.isUnsignedWord || this == targetType
 | 
			
		||||
                    else -> false
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.STRUCT_INSTANCE -> false        // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
 | 
			
		||||
            BaseDataType.ARRAY_POINTER -> false
 | 
			
		||||
            BaseDataType.UNDEFINED -> false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    fun largerSizeThan(other: DataType): Boolean = 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
 | 
			
		||||
@@ -208,28 +348,35 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
 | 
			
		||||
    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 isBoolArray = base.isArray && sub == BaseDataType.BOOL
 | 
			
		||||
    val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
 | 
			
		||||
    val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
 | 
			
		||||
    val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
 | 
			
		||||
    val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
 | 
			
		||||
    val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
 | 
			
		||||
    val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
 | 
			
		||||
    val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
 | 
			
		||||
    val 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 && sub == BaseDataType.UWORD
 | 
			
		||||
    val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
 | 
			
		||||
    val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
 | 
			
		||||
    val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
 | 
			
		||||
    val isIterable =  base.isIterable
 | 
			
		||||
    val isPassByRef = base.isPassByRef
 | 
			
		||||
    val isPassByValue = base.isPassByValue
 | 
			
		||||
@@ -253,7 +400,9 @@ 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 { entries.map { it.toString()} }
 | 
			
		||||
@@ -266,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
 | 
			
		||||
@@ -279,11 +440,15 @@ enum class RegisterOrPair {
 | 
			
		||||
            BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
 | 
			
		||||
            BaseDataType.BYTE -> "sL"
 | 
			
		||||
            BaseDataType.WORD -> "s"
 | 
			
		||||
            BaseDataType.UWORD, null -> ""
 | 
			
		||||
            else -> throw kotlin.IllegalArgumentException("invalid register param type")
 | 
			
		||||
            BaseDataType.UWORD, BaseDataType.POINTER, null -> ""
 | 
			
		||||
            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 {
 | 
			
		||||
@@ -319,6 +484,17 @@ val Cx16VirtualRegisters = arrayOf(
 | 
			
		||||
    RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -354,6 +530,5 @@ enum class ZeropageWish {
 | 
			
		||||
 | 
			
		||||
enum class SplitWish {
 | 
			
		||||
    DONTCARE,
 | 
			
		||||
    SPLIT,
 | 
			
		||||
    NOSPLIT
 | 
			
		||||
}
 | 
			
		||||
@@ -27,7 +27,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
 | 
			
		||||
    var golden: GoldenRam
 | 
			
		||||
    val libraryPath: Path?
 | 
			
		||||
    val customLauncher: List<String>
 | 
			
		||||
    val additionalAssemblerOptions: String?
 | 
			
		||||
    val additionalAssemblerOptions: List<String>
 | 
			
		||||
    val defaultOutputType: OutputType
 | 
			
		||||
 | 
			
		||||
    fun initializeMemoryAreas(compilerOptions: CompilationOptions)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
 | 
			
		||||
 | 
			
		||||
    abstract val SCRATCH_B1 : UInt      // temp storage for a single byte
 | 
			
		||||
    abstract val SCRATCH_REG : UInt     // temp storage for a register byte, must be B1+1
 | 
			
		||||
    abstract val SCRATCH_W1 : UInt      // temp storage 1 for a word  $fb+$fc
 | 
			
		||||
    abstract val SCRATCH_W2 : UInt      // temp storage 2 for a word  $fb+$fc
 | 
			
		||||
    abstract val SCRATCH_W1 : UInt      // temp storage 1 for a word
 | 
			
		||||
    abstract val SCRATCH_W2 : UInt      // temp storage 2 for a word
 | 
			
		||||
    abstract val SCRATCH_PTR : UInt     // temp storage for a pointer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // the variables allocated into Zeropage.
 | 
			
		||||
@@ -38,7 +39,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
 | 
			
		||||
            for (reserved in options.zpReserved)
 | 
			
		||||
                reserve(reserved)
 | 
			
		||||
 | 
			
		||||
            free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
 | 
			
		||||
            free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -72,6 +73,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
 | 
			
		||||
        val size: Int =
 | 
			
		||||
                when {
 | 
			
		||||
                    datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
 | 
			
		||||
                    datatype.isPointer -> options.compTarget.memorySize(datatype, null)
 | 
			
		||||
                    datatype.isString || datatype.isArray -> {
 | 
			
		||||
                        val memsize = options.compTarget.memorySize(datatype, numElements!!)
 | 
			
		||||
                        if(position!=null)
 | 
			
		||||
@@ -122,6 +124,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
 | 
			
		||||
                datatype.isNumericOrBool -> VarAllocation(address, datatype, size)        // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
 | 
			
		||||
                datatype.isString -> VarAllocation(address, datatype, size)
 | 
			
		||||
                datatype.isArray -> VarAllocation(address, datatype, size)
 | 
			
		||||
                datatype.isPointer -> VarAllocation(address, datatype, size)
 | 
			
		||||
                else -> throw AssemblyError("invalid dt")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,15 @@ import prog8.code.target.zp.C128Zeropage
 | 
			
		||||
import java.nio.file.Path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
 | 
			
		||||
class C128Target: ICompilationTarget,
 | 
			
		||||
    IStringEncoding by Encoder(true),
 | 
			
		||||
    IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
 | 
			
		||||
 | 
			
		||||
    override val name = NAME
 | 
			
		||||
    override val defaultEncoding = Encoding.PETSCII
 | 
			
		||||
    override val libraryPath = null
 | 
			
		||||
    override val customLauncher: List<String> = emptyList()
 | 
			
		||||
    override val additionalAssemblerOptions = null
 | 
			
		||||
    override val customLauncher = emptyList<String>()
 | 
			
		||||
    override val additionalAssemblerOptions = emptyList<String>()
 | 
			
		||||
    override val defaultOutputType = OutputType.PRG
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
@@ -28,10 +31,10 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
 | 
			
		||||
    override val PROGRAM_LOAD_ADDRESS = 0x1c01u
 | 
			
		||||
    override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
 | 
			
		||||
 | 
			
		||||
    override val BSSHIGHRAM_START = 0u    // TODO
 | 
			
		||||
    override val BSSHIGHRAM_END = 0u      // TODO
 | 
			
		||||
    override val BSSGOLDENRAM_START = 0u  // TODO
 | 
			
		||||
    override val BSSGOLDENRAM_END = 0u    // TODO
 | 
			
		||||
    override val BSSHIGHRAM_START = 0u    // 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
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,15 @@ import java.io.IOException
 | 
			
		||||
import java.nio.file.Path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
			
		||||
class C64Target: ICompilationTarget,
 | 
			
		||||
    IStringEncoding by Encoder(true),
 | 
			
		||||
    IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
 | 
			
		||||
 | 
			
		||||
    override val name = NAME
 | 
			
		||||
    override val defaultEncoding = Encoding.PETSCII
 | 
			
		||||
    override val libraryPath = null
 | 
			
		||||
    override val customLauncher: List<String> = emptyList()
 | 
			
		||||
    override val additionalAssemblerOptions = null
 | 
			
		||||
    override val customLauncher = emptyList<String>()
 | 
			
		||||
    override val additionalAssemblerOptions = emptyList<String>()
 | 
			
		||||
    override val defaultOutputType = OutputType.PRG
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,17 +27,18 @@ class ConfigFileTarget(
 | 
			
		||||
    override val defaultOutputType: OutputType,
 | 
			
		||||
    override val libraryPath: Path,
 | 
			
		||||
    override val customLauncher: List<String>,
 | 
			
		||||
    override val additionalAssemblerOptions: String?,
 | 
			
		||||
    override val additionalAssemblerOptions: List<String>,
 | 
			
		||||
    val ioAddresses: List<UIntRange>,
 | 
			
		||||
    val zpScratchB1: UInt,
 | 
			
		||||
    val zpScratchReg: UInt,
 | 
			
		||||
    val zpScratchW1: UInt,
 | 
			
		||||
    val zpScratchW2: UInt,
 | 
			
		||||
    val zpScratchPtr: UInt,
 | 
			
		||||
    val virtualregistersStart: UInt,
 | 
			
		||||
    val zpFullsafe: List<UIntRange>,
 | 
			
		||||
    val zpKernalsafe: List<UIntRange>,
 | 
			
		||||
    val zpBasicsafe: List<UIntRange>
 | 
			
		||||
): ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(8) {
 | 
			
		||||
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
@@ -109,8 +110,6 @@ class ConfigFileTarget(
 | 
			
		||||
                    (customLauncherStr+"\n").lines().map { it.trimEnd() }
 | 
			
		||||
                else emptyList()
 | 
			
		||||
            val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
 | 
			
		||||
            val assemblerOptions = assemblerOptionsStr.ifBlank { null }
 | 
			
		||||
 | 
			
		||||
            val outputTypeString = props.getProperty("output_type", "PRG")
 | 
			
		||||
            val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
 | 
			
		||||
 | 
			
		||||
@@ -128,12 +127,13 @@ class ConfigFileTarget(
 | 
			
		||||
                defaultOutputType,
 | 
			
		||||
                libraryPath,
 | 
			
		||||
                customLauncher,
 | 
			
		||||
                assemblerOptions,
 | 
			
		||||
                if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "),
 | 
			
		||||
                ioAddresses,
 | 
			
		||||
                props.getInteger("zp_scratch_b1"),
 | 
			
		||||
                props.getInteger("zp_scratch_reg"),
 | 
			
		||||
                props.getInteger("zp_scratch_w1"),
 | 
			
		||||
                props.getInteger("zp_scratch_w2"),
 | 
			
		||||
                props.getInteger("zp_scratch_ptr"),
 | 
			
		||||
                props.getInteger("virtual_registers"),
 | 
			
		||||
                zpFullsafe,
 | 
			
		||||
                zpKernalsafe,
 | 
			
		||||
@@ -161,7 +161,7 @@ class ConfigFileTarget(
 | 
			
		||||
 | 
			
		||||
    override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
 | 
			
		||||
        zeropage = ConfigurableZeropage(
 | 
			
		||||
            zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
 | 
			
		||||
            zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr,
 | 
			
		||||
            virtualregistersStart,
 | 
			
		||||
            zpBasicsafe,
 | 
			
		||||
            zpKernalsafe,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,15 @@ import prog8.code.target.zp.CX16Zeropage
 | 
			
		||||
import java.nio.file.Path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
			
		||||
class Cx16Target: ICompilationTarget,
 | 
			
		||||
    IStringEncoding by Encoder(true),
 | 
			
		||||
    IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
 | 
			
		||||
 | 
			
		||||
    override val name = NAME
 | 
			
		||||
    override val defaultEncoding = Encoding.PETSCII
 | 
			
		||||
    override val libraryPath = null
 | 
			
		||||
    override val customLauncher: List<String> = emptyList()
 | 
			
		||||
    override val additionalAssemblerOptions = null
 | 
			
		||||
    override val customLauncher = emptyList<String>()
 | 
			
		||||
    override val additionalAssemblerOptions = emptyList<String>()
 | 
			
		||||
    override val defaultOutputType = OutputType.PRG
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,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
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,14 @@ import prog8.code.core.IMemSizer
 | 
			
		||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
 | 
			
		||||
 | 
			
		||||
    override fun memorySize(dt: DataType, numElements: Int?): Int {
 | 
			
		||||
        if(dt.isArray) {
 | 
			
		||||
        if(dt.isPointerArray)
 | 
			
		||||
            return 2 * numElements!!        // array of pointers is just array of uwords
 | 
			
		||||
        else if(dt.isArray) {
 | 
			
		||||
            if(numElements==null) return 2      // treat it as a pointer size
 | 
			
		||||
            return when(dt.sub) {
 | 
			
		||||
                BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
 | 
			
		||||
                BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
 | 
			
		||||
                BaseDataType.LONG -> numElements * 4
 | 
			
		||||
                BaseDataType.FLOAT-> numElements * floatsize
 | 
			
		||||
                BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
 | 
			
		||||
                else -> throw IllegalArgumentException("invalid sub type")
 | 
			
		||||
@@ -25,7 +28,9 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
 | 
			
		||||
        return when {
 | 
			
		||||
            dt.isByteOrBool -> 1 * (numElements ?: 1)
 | 
			
		||||
            dt.isFloat -> floatsize * (numElements ?: 1)
 | 
			
		||||
            dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
 | 
			
		||||
            dt.isLong -> 4 * (numElements ?: 1)
 | 
			
		||||
            dt.isPointer -> 2  // pointer is just a uword
 | 
			
		||||
            dt.isStructInstance -> dt.subType!!.memsize(this)
 | 
			
		||||
            dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
 | 
			
		||||
            else -> 2 * (numElements ?: 1)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,15 @@ import prog8.code.target.zp.PETZeropage
 | 
			
		||||
import java.nio.file.Path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
			
		||||
class PETTarget: ICompilationTarget,
 | 
			
		||||
    IStringEncoding by Encoder(true),
 | 
			
		||||
    IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
 | 
			
		||||
 | 
			
		||||
    override val name = NAME
 | 
			
		||||
    override val defaultEncoding = Encoding.PETSCII
 | 
			
		||||
    override val libraryPath = null
 | 
			
		||||
    override val customLauncher: List<String> = emptyList()
 | 
			
		||||
    override val additionalAssemblerOptions = null
 | 
			
		||||
    override val customLauncher = emptyList<String>()
 | 
			
		||||
    override val additionalAssemblerOptions = emptyList<String>()
 | 
			
		||||
    override val defaultOutputType = OutputType.PRG
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,16 +3,20 @@ package prog8.code.target
 | 
			
		||||
import prog8.code.core.*
 | 
			
		||||
import prog8.code.target.encodings.Encoder
 | 
			
		||||
import java.nio.file.Path
 | 
			
		||||
import kotlin.io.path.extension
 | 
			
		||||
import kotlin.io.path.isReadable
 | 
			
		||||
import kotlin.io.path.name
 | 
			
		||||
import kotlin.io.path.readText
 | 
			
		||||
 | 
			
		||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
 | 
			
		||||
class VMTarget: ICompilationTarget,
 | 
			
		||||
    IStringEncoding by Encoder(false),
 | 
			
		||||
    IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
 | 
			
		||||
 | 
			
		||||
    override val name = NAME
 | 
			
		||||
    override val defaultEncoding = Encoding.ISO
 | 
			
		||||
    override val libraryPath = null
 | 
			
		||||
    override val customLauncher: List<String> = emptyList()
 | 
			
		||||
    override val additionalAssemblerOptions = null
 | 
			
		||||
    override val customLauncher = emptyList<String>()
 | 
			
		||||
    override val additionalAssemblerOptions = emptyList<String>()
 | 
			
		||||
    override val defaultOutputType = OutputType.PRG
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
@@ -70,17 +74,12 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
 | 
			
		||||
 | 
			
		||||
        // 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(), quiet)
 | 
			
		||||
        } else {
 | 
			
		||||
            val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
@@ -88,31 +87,6 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
 | 
			
		||||
        zeropage = VirtualZeropage(compilerOptions)
 | 
			
		||||
        golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun memorySize(dt: DataType, numElements: Int?): Int {
 | 
			
		||||
        if(dt.isArray) {
 | 
			
		||||
            if(numElements==null) return 2      // treat it as a pointer size
 | 
			
		||||
            return when(dt.sub) {
 | 
			
		||||
                BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
 | 
			
		||||
                BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
 | 
			
		||||
                BaseDataType.FLOAT-> numElements * FLOAT_MEM_SIZE
 | 
			
		||||
                BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
 | 
			
		||||
                else -> throw IllegalArgumentException("invalid sub type")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (dt.isString) {
 | 
			
		||||
            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 -> FLOAT_MEM_SIZE * (numElements ?: 1)
 | 
			
		||||
            dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
 | 
			
		||||
            dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
 | 
			
		||||
            else -> 2 * (numElements ?: 1)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -130,4 +104,6 @@ private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
 | 
			
		||||
        get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
 | 
			
		||||
    override val SCRATCH_W2: UInt
 | 
			
		||||
        get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
 | 
			
		||||
    override val SCRATCH_PTR: UInt
 | 
			
		||||
        get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -197,6 +197,7 @@ object AtasciiEncoding {
 | 
			
		||||
    fun encode(str: String): Result<List<UByte>, CharConversionException> {
 | 
			
		||||
        val mapped = str.map { chr ->
 | 
			
		||||
            when (chr) {
 | 
			
		||||
                '\r' -> 0x9bu
 | 
			
		||||
                '\u0000' -> 0u
 | 
			
		||||
                in '\u8000'..'\u80ff' -> {
 | 
			
		||||
                    // special case: take the lower 8 bit hex value directly
 | 
			
		||||
 
 | 
			
		||||
@@ -285,6 +285,7 @@ object C64osEncoding {
 | 
			
		||||
            val screencode = encodingC64os[chr]
 | 
			
		||||
            return screencode?.toUByte() ?: when (chr) {
 | 
			
		||||
                '\u0000' -> 0u
 | 
			
		||||
                '\n' -> 13u
 | 
			
		||||
                in '\u8000'..'\u80ff' -> {
 | 
			
		||||
                    // special case: take the lower 8 bit hex value directly
 | 
			
		||||
                    (chr.code - 0x8000).toUByte()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,19 +5,19 @@ import prog8.code.core.Encoding
 | 
			
		||||
import prog8.code.core.IStringEncoding
 | 
			
		||||
import prog8.code.core.InternalCompilerException
 | 
			
		||||
 | 
			
		||||
object Encoder: IStringEncoding {
 | 
			
		||||
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
 | 
			
		||||
    override val defaultEncoding: Encoding = Encoding.ISO
 | 
			
		||||
 | 
			
		||||
    override fun encodeString(str: String, encoding: Encoding): List<UByte> {
 | 
			
		||||
        val coded = when(encoding) {
 | 
			
		||||
            Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
 | 
			
		||||
            Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
 | 
			
		||||
            Encoding.ISO -> IsoEncoding.encode(str)
 | 
			
		||||
            Encoding.ATASCII -> AtasciiEncoding.encode(str)
 | 
			
		||||
            Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
 | 
			
		||||
            Encoding.ISO16 -> IsoEasternEncoding.encode(str)
 | 
			
		||||
            Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.CP437 -> Cp437Encoding.encode(str)
 | 
			
		||||
            Encoding.KATAKANA -> KatakanaEncoding.encode(str)
 | 
			
		||||
            Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.ATASCII -> AtasciiEncoding.encode(str)
 | 
			
		||||
            Encoding.C64OS -> C64osEncoding.encode(str)
 | 
			
		||||
            else -> throw InternalCompilerException("unsupported encoding $encoding")
 | 
			
		||||
        }
 | 
			
		||||
@@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
 | 
			
		||||
        val decoded = when(encoding) {
 | 
			
		||||
            Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
 | 
			
		||||
            Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
 | 
			
		||||
            Encoding.ISO -> IsoEncoding.decode(bytes)
 | 
			
		||||
            Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
 | 
			
		||||
            Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
 | 
			
		||||
            Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
 | 
			
		||||
            Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.CP437 -> Cp437Encoding.decode(bytes)
 | 
			
		||||
            Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
 | 
			
		||||
            Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
 | 
			
		||||
            Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
 | 
			
		||||
            Encoding.C64OS -> C64osEncoding.decode(bytes)
 | 
			
		||||
            else -> throw InternalCompilerException("unsupported encoding $encoding")
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
 | 
			
		||||
import java.io.CharConversionException
 | 
			
		||||
import java.nio.charset.Charset
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
open class IsoEncodingBase(charsetName: String) {
 | 
			
		||||
    val charset: Charset = Charset.forName(charsetName)
 | 
			
		||||
 | 
			
		||||
    fun encode(str: String): Result<List<UByte>, CharConversionException> {
 | 
			
		||||
    fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
 | 
			
		||||
        return try {
 | 
			
		||||
            val mapped = str.map { chr ->
 | 
			
		||||
                when (chr) {
 | 
			
		||||
                    '\u0000' -> 0u
 | 
			
		||||
                    '\n' -> if(newlineToCarriageReturn) 13u else 10u
 | 
			
		||||
                    in '\u8000'..'\u80ff' -> {
 | 
			
		||||
                        // special case: take the lower 8 bit hex value directly
 | 
			
		||||
                        (chr.code - 0x8000).toUByte()
 | 
			
		||||
@@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
 | 
			
		||||
    fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
 | 
			
		||||
        return try {
 | 
			
		||||
            Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
 | 
			
		||||
            Ok(String(bytes.map {
 | 
			
		||||
                when(it) {
 | 
			
		||||
                    13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
 | 
			
		||||
                    else -> it.toByte()
 | 
			
		||||
                }
 | 
			
		||||
            }.toByteArray(), charset))
 | 
			
		||||
        } catch (ce: CharConversionException) {
 | 
			
		||||
            Err(ce)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
 | 
			
		||||
object KatakanaEncoding {
 | 
			
		||||
    val charset: Charset = Charset.forName("JIS_X0201")
 | 
			
		||||
 | 
			
		||||
    fun encode(str: String): Result<List<UByte>, CharConversionException> {
 | 
			
		||||
    fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
 | 
			
		||||
        return try {
 | 
			
		||||
            val mapped = str.map { chr ->
 | 
			
		||||
                when (chr) {
 | 
			
		||||
                    '\n' -> if(newlineToCarriageReturn) 13u else 10u
 | 
			
		||||
 | 
			
		||||
                    '\u0000' -> 0u
 | 
			
		||||
                    '\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
 | 
			
		||||
@@ -112,9 +113,14 @@ object KatakanaEncoding {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
 | 
			
		||||
    fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
 | 
			
		||||
        return try {
 | 
			
		||||
            Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
 | 
			
		||||
            Ok(String(bytes.map {
 | 
			
		||||
                when(it) {
 | 
			
		||||
                    13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
 | 
			
		||||
                    else -> it.toByte()
 | 
			
		||||
                }
 | 
			
		||||
            }.toByteArray(), charset))
 | 
			
		||||
        } catch (ce: CharConversionException) {
 | 
			
		||||
            Err(ce)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ object PetsciiEncoding {
 | 
			
		||||
        '\ufffe',    //       0x07 -> UNDEFINED
 | 
			
		||||
        '\uf118',    //       0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
 | 
			
		||||
        '\uf119',    //       0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
 | 
			
		||||
        '\ufffe',    //       0x0A -> UNDEFINED
 | 
			
		||||
        '\n',        //       0x0A -> LINE FEED (RETURN)
 | 
			
		||||
        '\ufffe',    //       0x0B -> UNDEFINED
 | 
			
		||||
        '\ufffe',    //       0x0C -> UNDEFINED
 | 
			
		||||
        '\n'    ,    //       0x0D -> LINE FEED (RETURN)
 | 
			
		||||
@@ -1117,6 +1117,8 @@ object PetsciiEncoding {
 | 
			
		||||
            val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
 | 
			
		||||
            return screencode?.toUByte() ?: when (chr) {
 | 
			
		||||
                '\u0000' -> 0u
 | 
			
		||||
                '\n' -> 141u
 | 
			
		||||
                '\r' -> 141u
 | 
			
		||||
                in '\u8000'..'\u80ff' -> {
 | 
			
		||||
                    // special case: take the lower 8 bit hex value directly
 | 
			
		||||
                    (chr.code - 0x8000).toUByte()
 | 
			
		||||
 
 | 
			
		||||
@@ -14,14 +14,9 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
 | 
			
		||||
    override val SCRATCH_REG = 0x75u     // temp storage for a register byte, must be B1+1
 | 
			
		||||
    override val SCRATCH_W1 = 0xfbu      // temp storage 1 for a word  $fb+$fc
 | 
			
		||||
    override val SCRATCH_W2 = 0xfdu      // temp storage 2 for a word  $fd+$fe
 | 
			
		||||
    override val SCRATCH_PTR = 0x0bu     // temp storage for a pointer $0b+$0c
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        if (options.floats) {
 | 
			
		||||
            throw InternalCompilerException("C128 target doesn't yet support floating point routines")
 | 
			
		||||
            // note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
 | 
			
		||||
            //       they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (options.floats && options.zeropage !in arrayOf(
 | 
			
		||||
                ZeropageType.FLOATSAFE,
 | 
			
		||||
                ZeropageType.BASICSAFE,
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
 | 
			
		||||
    override val SCRATCH_REG = 0x03u     // temp storage for a register byte, must be B1+1
 | 
			
		||||
    override val SCRATCH_W1 = 0xfbu      // temp storage 1 for a word  $fb+$fc
 | 
			
		||||
    override val SCRATCH_W2 = 0xfdu      // temp storage 2 for a word  $fd+$fe
 | 
			
		||||
    override val SCRATCH_PTR = 0x9bu     // temp storage for a pointer $9b+$9c
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
@@ -21,7 +22,6 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
 | 
			
		||||
 | 
			
		||||
        if (options.zeropage == ZeropageType.FULL) {
 | 
			
		||||
            free.addAll(0x02u..0xffu)
 | 
			
		||||
            free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
 | 
			
		||||
            free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u))        // these are updated by IRQ
 | 
			
		||||
        } else {
 | 
			
		||||
            if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
 | 
			
		||||
    override val SCRATCH_REG = 0x7bu     // temp storage for a register byte, must be B1+1
 | 
			
		||||
    override val SCRATCH_W1 = 0x7cu      // temp storage 1 for a word  $7c+$7d
 | 
			
		||||
    override val SCRATCH_W2 = 0x7eu      // temp storage 2 for a word  $7e+$7f
 | 
			
		||||
    override val SCRATCH_PTR = 0x22u     // temp storage for a pointer $22+$23
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ class ConfigurableZeropage(
 | 
			
		||||
    override val SCRATCH_REG: UInt,     // temp storage for a register byte, must be B1+1
 | 
			
		||||
    override val SCRATCH_W1: UInt,      // temp storage 1 for a word
 | 
			
		||||
    override val SCRATCH_W2: UInt,      // temp storage 2 for a word
 | 
			
		||||
    override val SCRATCH_PTR: UInt,     // temp storage for a pointer
 | 
			
		||||
 | 
			
		||||
    val virtualRegistersStart: UInt,        // location of 32 bytes for the r0-r15 virtual registers
 | 
			
		||||
    basicsafe: List<UIntRange>,
 | 
			
		||||
    kernalsafe: List<UIntRange>,
 | 
			
		||||
@@ -19,7 +21,7 @@ class ConfigurableZeropage(
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        if (options.floats) {
 | 
			
		||||
            TODO("floats")
 | 
			
		||||
            TODO("floats in configurable target zp")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(SCRATCH_REG!=SCRATCH_B1+1u)
 | 
			
		||||
@@ -30,7 +32,7 @@ class ConfigurableZeropage(
 | 
			
		||||
            ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
 | 
			
		||||
            ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
 | 
			
		||||
            ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
 | 
			
		||||
            ZeropageType.FLOATSAFE -> TODO("floatsafe")
 | 
			
		||||
            ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val distinctFree = free.distinct()
 | 
			
		||||
 
 | 
			
		||||
@@ -14,12 +14,9 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
 | 
			
		||||
    override val SCRATCH_REG = 0xb4u     // temp storage for a register byte, must be B1+1
 | 
			
		||||
    override val SCRATCH_W1 = 0xb6u      // temp storage 1 for a word
 | 
			
		||||
    override val SCRATCH_W2 = 0xb8u      // temp storage 2 for a word
 | 
			
		||||
    override val SCRATCH_PTR = 0xb1u     // temp storage for a pointer $b1+$b2
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        if (options.floats) {
 | 
			
		||||
            throw InternalCompilerException("PET target doesn't yet support floating point routines")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (options.floats && options.zeropage !in arrayOf(
 | 
			
		||||
                ZeropageType.FLOATSAFE,
 | 
			
		||||
                ZeropageType.BASICSAFE,
 | 
			
		||||
@@ -38,7 +35,7 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
 | 
			
		||||
            }
 | 
			
		||||
            ZeropageType.FLOATSAFE,
 | 
			
		||||
            ZeropageType.BASICSAFE -> {
 | 
			
		||||
                free.addAll(0xb3u..0xbau)       // TODO more?
 | 
			
		||||
                free.addAll(0xb1u..0xbau)       // TODO more?
 | 
			
		||||
            }
 | 
			
		||||
            ZeropageType.DONTUSE -> {
 | 
			
		||||
                free.clear()  // don't use zeropage at all
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
}
 | 
			
		||||
@@ -9,12 +7,10 @@ dependencies {
 | 
			
		||||
    implementation(project(":simpleAst"))
 | 
			
		||||
    // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
			
		||||
    // implementation "org.jetbrains.kotlin:kotlin-reflect"
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
 | 
			
		||||
 | 
			
		||||
    testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
 | 
			
		||||
    testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
 | 
			
		||||
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
 | 
			
		||||
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sourceSets {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@
 | 
			
		||||
    <orderEntry type="module" module-name="codeCore" />
 | 
			
		||||
    <orderEntry type="module" module-name="simpleAst" />
 | 
			
		||||
    <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
 | 
			
		||||
  </component>
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -73,6 +73,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationT
 | 
			
		||||
        numberOfOptimizations++
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mods = optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen)
 | 
			
		||||
    if(mods.isNotEmpty()) {
 | 
			
		||||
        apply(mods, lines)
 | 
			
		||||
        linesByFourteen = getLinesBy(lines, 14)
 | 
			
		||||
        numberOfOptimizations++
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return numberOfOptimizations
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -408,7 +415,7 @@ private fun optimizeStoreLoadSame(
 | 
			
		||||
                    // a branch instruction follows, we can only remove the load instruction if
 | 
			
		||||
                    // another load instruction of the same register precedes the store instruction
 | 
			
		||||
                    // (otherwise wrong cpu flags are used)
 | 
			
		||||
                    val loadinstruction = second.substring(0, 3)
 | 
			
		||||
                    val loadinstruction = second.take(3)
 | 
			
		||||
                    lines[0].value.trimStart().startsWith(loadinstruction)
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
@@ -446,15 +453,30 @@ private fun optimizeStoreLoadSame(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // lda X + sta X,  ldy X + sty X,   ldx X + stx X  -> the second instruction can be eliminated
 | 
			
		||||
        if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
 | 
			
		||||
            (first.startsWith("ldx ") && second.startsWith("stx ")) ||
 | 
			
		||||
            (first.startsWith("ldy ") && second.startsWith("sty "))
 | 
			
		||||
        if (first.startsWith("lda ") && second.startsWith("sta ") ||
 | 
			
		||||
            first.startsWith("ldx ") && second.startsWith("stx ") ||
 | 
			
		||||
            first.startsWith("ldy ") && second.startsWith("sty ")
 | 
			
		||||
        ) {
 | 
			
		||||
            val firstLoc = first.substring(4).trimStart()
 | 
			
		||||
            val secondLoc = second.substring(4).trimStart()
 | 
			
		||||
            if (firstLoc == secondLoc)
 | 
			
		||||
                mods.add(Modification(lines[2].index, true, null))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //  all 3 registers:  lda VALUE + sta SOMEWHERE + lda VALUE  -> last load can be eliminated IF NOT IO ADDRESS
 | 
			
		||||
        if (first.startsWith("lda ") && second.startsWith("sta ") && third.startsWith("lda ") ||
 | 
			
		||||
            first.startsWith("ldx ") && second.startsWith("stx ") && third.startsWith("ldx ") ||
 | 
			
		||||
            first.startsWith("ldy ") && second.startsWith("sty ") && third.startsWith("ldy ")
 | 
			
		||||
        ) {
 | 
			
		||||
            val firstVal = first.substring(4).trimStart()
 | 
			
		||||
            val thirdVal = third.substring(4).trimStart()
 | 
			
		||||
            if (firstVal == thirdVal) {
 | 
			
		||||
                val address = getAddressArg(third, symbolTable)
 | 
			
		||||
                if (address != null && !machine.isIOAddress(address)) {
 | 
			
		||||
                    mods.add(Modification(lines[3].index, true, null))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return mods
 | 
			
		||||
}
 | 
			
		||||
@@ -512,6 +534,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
 | 
			
		||||
    // rts + jmp -> remove jmp
 | 
			
		||||
    // rts + bxx -> remove bxx
 | 
			
		||||
    // lda  + cmp #0 -> remove cmp,  same for cpy and cpx.
 | 
			
		||||
    // bra/jmp + bra/jmp -> remove second bra/jmp   (bra bra / jmp jmp are not removed because this is likely a jump table!)
 | 
			
		||||
    // and some other optimizations.
 | 
			
		||||
 | 
			
		||||
    val mods = mutableListOf<Modification>()
 | 
			
		||||
@@ -567,6 +590,15 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // only remove bra followed by jmp or jmp followed by bra
 | 
			
		||||
            // bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
 | 
			
		||||
            if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
 | 
			
		||||
                mods.add(Modification(lines[1].index, true, null))
 | 
			
		||||
            }
 | 
			
		||||
            if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
 | 
			
		||||
                mods.add(Modification(lines[1].index, true, null))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
@@ -684,23 +716,32 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
 | 
			
		||||
        optimize('x', lines)
 | 
			
		||||
        optimize('y', lines)
 | 
			
		||||
 | 
			
		||||
        val first = lines[1].value.trimStart()
 | 
			
		||||
        val second = lines[2].value.trimStart()
 | 
			
		||||
        val third = lines[3].value.trimStart()
 | 
			
		||||
        val first = lines[0].value.trimStart()
 | 
			
		||||
        val second = lines[1].value.trimStart()
 | 
			
		||||
        val third = lines[2].value.trimStart()
 | 
			
		||||
        val fourth = lines[3].value.trimStart()
 | 
			
		||||
 | 
			
		||||
        // phy + ldy + pla -> tya + ldy
 | 
			
		||||
        // phx + ldx + pla -> txa + ldx
 | 
			
		||||
        // pha + lda + pla -> nop
 | 
			
		||||
        // pha + tya + tay + pla -> nop
 | 
			
		||||
        // pha + txa + tax + pla -> nop
 | 
			
		||||
        when (first) {
 | 
			
		||||
            "phy" if second.startsWith("ldy ") && third=="pla" -> {
 | 
			
		||||
                mods.add(Modification(lines[3].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[1].index, false, "  tya"))
 | 
			
		||||
                mods.add(Modification(lines[2].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[0].index, false, "  tya"))
 | 
			
		||||
            }
 | 
			
		||||
            "phx" if second.startsWith("ldx ") && third=="pla" -> {
 | 
			
		||||
                mods.add(Modification(lines[3].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[1].index, false, "  txa"))
 | 
			
		||||
                mods.add(Modification(lines[2].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[0].index, false, "  txa"))
 | 
			
		||||
            }
 | 
			
		||||
            "pha" if second.startsWith("lda ") && third=="pla" -> {
 | 
			
		||||
                mods.add(Modification(lines[0].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[1].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[2].index, true, null))
 | 
			
		||||
            }
 | 
			
		||||
            "pha" if ((second=="tya" && third=="tay") || (second=="txa" && third=="tax")) && fourth=="pla" -> {
 | 
			
		||||
                mods.add(Modification(lines[0].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[1].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[2].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[3].index, true, null))
 | 
			
		||||
@@ -712,7 +753,6 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
 | 
			
		||||
    return mods
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
 | 
			
		||||
    // Asm peephole:   lda var2 / tsb var1 / lda var1  Replace this with this to save 1 cycle:   lda var1 / ora var2 / sta var1
 | 
			
		||||
    val mods = mutableListOf<Modification>()
 | 
			
		||||
@@ -757,3 +797,120 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue
 | 
			
		||||
 | 
			
		||||
    return mods
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
 | 
			
		||||
    /*
 | 
			
		||||
        ; FIRST SEQUYENCE: P8ZP_SCRATCH_PTR += AY :
 | 
			
		||||
        clc
 | 
			
		||||
        adc  P8ZP_SCRATCH_PTR
 | 
			
		||||
        pha
 | 
			
		||||
        tya
 | 
			
		||||
        adc  P8ZP_SCRATCH_PTR+1
 | 
			
		||||
        tay
 | 
			
		||||
        pla
 | 
			
		||||
        sta  P8ZP_SCRATCH_PTR
 | 
			
		||||
        sty  P8ZP_SCRATCH_PTR+1
 | 
			
		||||
 | 
			
		||||
	    ->
 | 
			
		||||
 | 
			
		||||
        clc
 | 
			
		||||
    	adc  P8ZP_SCRATCH_PTR
 | 
			
		||||
    	sta  P8ZP_SCRATCH_PTR
 | 
			
		||||
    	tya
 | 
			
		||||
    	adc  P8ZP_SCRATCH_PTR+1
 | 
			
		||||
    	sta  P8ZP_SCRATCH_PTR+1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    	also SECOND SEQUENCE:
 | 
			
		||||
 | 
			
		||||
        ldx  VALUE/  ldy  VALUE
 | 
			
		||||
	    sta  SOMEWHERE_WITHOUT_,x_OR_,y
 | 
			
		||||
	    txa /   tya
 | 
			
		||||
    	ldy  #1
 | 
			
		||||
    	sta  SOMEWHERE
 | 
			
		||||
     	 -->
 | 
			
		||||
    	sta  SOMEWHERE_WITHOUT_,x_OR_,y
 | 
			
		||||
    	lda  VALUE
 | 
			
		||||
    	ldy  #1
 | 
			
		||||
    	sta  SOMEWHERE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        also THIRD SEQUENCE:
 | 
			
		||||
 | 
			
		||||
        ldx  VALUE
 | 
			
		||||
	    ldy  #0
 | 
			
		||||
	    sta  SOMEWHERE_WITHOUT_,x
 | 
			
		||||
	    txa
 | 
			
		||||
	    iny
 | 
			
		||||
	    sta  SOMEWHERE
 | 
			
		||||
         -->
 | 
			
		||||
	    ldy  #0
 | 
			
		||||
	    sta  SOMEWHERE_WITHOUT_,x
 | 
			
		||||
	    lda  VALUE
 | 
			
		||||
	    iny
 | 
			
		||||
	    sta  SOMEWHERE
 | 
			
		||||
    */
 | 
			
		||||
    val mods = mutableListOf<Modification>()
 | 
			
		||||
    for (lines in linesByFourteen) {
 | 
			
		||||
        val first = lines[0].value.trimStart()
 | 
			
		||||
        val second = lines[1].value.trimStart()
 | 
			
		||||
        val third = lines[2].value.trimStart()
 | 
			
		||||
        val fourth = lines[3].value.trimStart()
 | 
			
		||||
        val fifth = lines[4].value.trimStart()
 | 
			
		||||
        val sixth = lines[5].value.trimStart()
 | 
			
		||||
        val seventh = lines[6].value.trimStart()
 | 
			
		||||
        val eight = lines[7].value.trimStart()
 | 
			
		||||
        val ninth = lines[8].value.trimStart()
 | 
			
		||||
 | 
			
		||||
        // FIRST SEQUENCE
 | 
			
		||||
        if(first=="clc" && second.startsWith("adc") && third=="pha" && fourth=="tya" &&
 | 
			
		||||
            fifth.startsWith("adc") && sixth=="tay" && seventh=="pla" && eight.startsWith("sta") && ninth.startsWith("sty")) {
 | 
			
		||||
            val var2 = second.substring(4)
 | 
			
		||||
            val var5 = fifth.substring(4).substringBefore('+')
 | 
			
		||||
            val var8 = eight.substring(4)
 | 
			
		||||
            val var9 = ninth.substring(4).substringBefore('+')
 | 
			
		||||
            if(var2==var5 && var2==var8 && var2==var9) {
 | 
			
		||||
                if(fifth.endsWith("$var5+1") && ninth.endsWith("$var9+1")) {
 | 
			
		||||
                    mods.add(Modification(lines[2].index, false, "  sta  $var2"))
 | 
			
		||||
                    mods.add(Modification(lines[5].index, false, "  sta  $var2+1"))
 | 
			
		||||
                    mods.add(Modification(lines[6].index, true, null))
 | 
			
		||||
                    mods.add(Modification(lines[7].index, true, null))
 | 
			
		||||
                    mods.add(Modification(lines[8].index, true, null))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // SECOND SEQUENCE
 | 
			
		||||
        if(first.startsWith("ldx ") && second.startsWith("sta ") &&
 | 
			
		||||
            third=="txa" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
 | 
			
		||||
        ) {
 | 
			
		||||
            if(",x" !in second) {
 | 
			
		||||
                val value = first.substring(4)
 | 
			
		||||
                mods.add(Modification(lines[0].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[2].index, false, "  lda  $value"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if(first.startsWith("ldy ") && second.startsWith("sta ") &&
 | 
			
		||||
            third=="tya" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
 | 
			
		||||
        ) {
 | 
			
		||||
            if(",y" !in second) {
 | 
			
		||||
                val value = first.substring(4)
 | 
			
		||||
                mods.add(Modification(lines[0].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[2].index, false, "  lda  $value"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // THIRD SEQUENCE
 | 
			
		||||
        if(first.startsWith("ldx ") && second.startsWith("ldy ") && third.startsWith("sta ") &&
 | 
			
		||||
            fourth=="txa" && fifth=="iny" && sixth.startsWith("sta ")
 | 
			
		||||
        ) {
 | 
			
		||||
            if(",x" !in third) {
 | 
			
		||||
                val value = first.substring(4)
 | 
			
		||||
                mods.add(Modification(lines[0].index, true, null))
 | 
			
		||||
                mods.add(Modification(lines[3].index, false, "  lda  $value"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    return mods
 | 
			
		||||
}
 | 
			
		||||
@@ -28,6 +28,14 @@ internal class AssemblyProgram(
 | 
			
		||||
 | 
			
		||||
        val assemblerCommand: List<String>
 | 
			
		||||
 | 
			
		||||
        fun addRemainingOptions(command: MutableList<String>, program: Path, assembly: Path): List<String> {
 | 
			
		||||
            if(options.compTarget.additionalAssemblerOptions.isNotEmpty())
 | 
			
		||||
                command.addAll(options.compTarget.additionalAssemblerOptions)
 | 
			
		||||
 | 
			
		||||
            command.addAll(listOf("--output", program.toString(), assembly.toString()))
 | 
			
		||||
            return command
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        when(options.output) {
 | 
			
		||||
            OutputType.PRG -> {
 | 
			
		||||
                // CBM machines .prg generation.
 | 
			
		||||
@@ -47,8 +55,7 @@ internal class AssemblyProgram(
 | 
			
		||||
                    command.add("--list=$listFile")
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
 | 
			
		||||
                assemblerCommand = command
 | 
			
		||||
                assemblerCommand = addRemainingOptions(command, prgFile, assemblyFile)
 | 
			
		||||
                if(!options.quiet)
 | 
			
		||||
                    println("\nCreating prg for target ${compTarget.name}.")
 | 
			
		||||
            }
 | 
			
		||||
@@ -69,8 +76,7 @@ internal class AssemblyProgram(
 | 
			
		||||
                if(options.asmListfile)
 | 
			
		||||
                    command.add("--list=$listFile")
 | 
			
		||||
 | 
			
		||||
                command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
 | 
			
		||||
                assemblerCommand = command
 | 
			
		||||
                assemblerCommand = addRemainingOptions(command,xexFile, assemblyFile)
 | 
			
		||||
                if(!options.quiet)
 | 
			
		||||
                    println("\nCreating xex for target ${compTarget.name}.")
 | 
			
		||||
            }
 | 
			
		||||
@@ -90,8 +96,7 @@ internal class AssemblyProgram(
 | 
			
		||||
                if(options.asmListfile)
 | 
			
		||||
                    command.add("--list=$listFile")
 | 
			
		||||
 | 
			
		||||
                command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
 | 
			
		||||
                assemblerCommand = command
 | 
			
		||||
                assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
 | 
			
		||||
                if(!options.quiet)
 | 
			
		||||
                    println("\nCreating raw binary for target ${compTarget.name}.")
 | 
			
		||||
            }
 | 
			
		||||
@@ -122,14 +127,10 @@ internal class AssemblyProgram(
 | 
			
		||||
                    command.add("--nostart")       // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
 | 
			
		||||
                assemblerCommand = command
 | 
			
		||||
                assemblerCommand = addRemainingOptions(command, binFile, assemblyFile)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(options.compTarget.additionalAssemblerOptions!=null)
 | 
			
		||||
            assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
 | 
			
		||||
 | 
			
		||||
        val proc = ProcessBuilder(assemblerCommand)
 | 
			
		||||
        if(!options.quiet)
 | 
			
		||||
            proc.inheritIO()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
package prog8.codegen.cpu6502
 | 
			
		||||
 | 
			
		||||
import prog8.code.StMemorySlabBlockName
 | 
			
		||||
import prog8.code.StStructInstanceBlockName
 | 
			
		||||
import prog8.code.SymbolTable
 | 
			
		||||
import prog8.code.ast.*
 | 
			
		||||
import prog8.code.core.*
 | 
			
		||||
import prog8.codegen.cpu6502.assignment.*
 | 
			
		||||
@@ -24,15 +27,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
        val sscope = fcall.definingISub()
 | 
			
		||||
 | 
			
		||||
        when (fcall.name) {
 | 
			
		||||
            "lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
 | 
			
		||||
            "msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
 | 
			
		||||
            "msw" -> funcMsw(fcall, resultRegister)
 | 
			
		||||
            "lsw" -> funcLsw(fcall, resultRegister)
 | 
			
		||||
            "msb" -> funcMsb(fcall, resultRegister)
 | 
			
		||||
            "lsb" -> funcLsb(fcall, resultRegister)
 | 
			
		||||
            "msb__long" -> funcMsbLong(fcall, resultRegister)
 | 
			
		||||
            "lsb" -> funcLsb(fcall, resultRegister, false)
 | 
			
		||||
            "lsb__long" -> funcLsb(fcall, resultRegister, true)
 | 
			
		||||
            "mkword" -> funcMkword(fcall, resultRegister)
 | 
			
		||||
            "clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultRegister)
 | 
			
		||||
            "min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
 | 
			
		||||
            "max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
 | 
			
		||||
            "abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
 | 
			
		||||
            "mklong", "mklong2" -> funcMklong(fcall)  // result is in R14:R15
 | 
			
		||||
            "clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword", "clamp__long" -> funcClamp(fcall, resultRegister)
 | 
			
		||||
            "min__byte", "min__ubyte", "min__word", "min__uword", "min__long" -> funcMin(fcall, resultRegister)
 | 
			
		||||
            "max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(fcall, resultRegister)
 | 
			
		||||
            "abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
 | 
			
		||||
            "sgn" -> funcSgn(fcall, resultRegister, sscope)
 | 
			
		||||
            "sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
 | 
			
		||||
            "divmod__ubyte" -> funcDivmod(fcall)
 | 
			
		||||
@@ -45,9 +51,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
            "setmsb" -> funcSetLsbMsb(fcall, true)
 | 
			
		||||
            "memory" -> funcMemory(fcall, discardResult, resultRegister)
 | 
			
		||||
            "peekw" -> funcPeekW(fcall, resultRegister)
 | 
			
		||||
            "peekl" -> funcPeekL(fcall, resultRegister)
 | 
			
		||||
            "peekf" -> funcPeekF(fcall, resultRegister)
 | 
			
		||||
            "peekbool" -> funcPeekBool(fcall, resultRegister)
 | 
			
		||||
            "peek" -> throw AssemblyError("peek() should have been replaced by @()")
 | 
			
		||||
            "pokew" -> funcPokeW(fcall)
 | 
			
		||||
            "pokel" -> funcPokeL(fcall)
 | 
			
		||||
            "pokef" -> funcPokeF(fcall)
 | 
			
		||||
            "pokemon" -> {
 | 
			
		||||
                val memread = PtMemoryByte(fcall.position)
 | 
			
		||||
@@ -60,12 +69,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                asmgen.out("  pla")
 | 
			
		||||
            }
 | 
			
		||||
            "poke" -> throw AssemblyError("poke() should have been replaced by @()")
 | 
			
		||||
            "pokebool" -> funcPokeBool(fcall)
 | 
			
		||||
            "rsave" -> funcRsave()
 | 
			
		||||
            "rrestore" -> funcRrestore()
 | 
			
		||||
            "cmp" -> funcCmp(fcall)
 | 
			
		||||
            "callfar" -> funcCallFar(fcall, resultRegister)
 | 
			
		||||
            "callfar2" -> funcCallFar2(fcall, resultRegister)
 | 
			
		||||
            "call" -> funcCall(fcall)
 | 
			
		||||
            "prog8_lib_structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
 | 
			
		||||
            "prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
 | 
			
		||||
            "prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
 | 
			
		||||
            "prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
 | 
			
		||||
@@ -340,9 +351,37 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                }
 | 
			
		||||
            } else
 | 
			
		||||
                throw AssemblyError("args for cmp() should have same dt")
 | 
			
		||||
        } else {
 | 
			
		||||
            // arg1 is a word
 | 
			
		||||
        } else if(arg1.type.isWord) {
 | 
			
		||||
            if(arg2.type.isWord) {
 | 
			
		||||
                if(arg1.type.isSigned) {
 | 
			
		||||
                    when (arg2) {
 | 
			
		||||
                        is PtIdentifier -> {
 | 
			
		||||
                            asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                sec
 | 
			
		||||
                                sbc  ${asmgen.asmVariableName(arg2)}
 | 
			
		||||
                                tya
 | 
			
		||||
                                sbc  ${asmgen.asmVariableName(arg2)}+1""")
 | 
			
		||||
                        }
 | 
			
		||||
                        is PtBool -> TODO("word compare against bool  ${arg2.position}")
 | 
			
		||||
                        is PtNumber -> {
 | 
			
		||||
                            asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                sec
 | 
			
		||||
                                sbc  #<${arg2.number.toInt()}
 | 
			
		||||
                                tya
 | 
			
		||||
                                sbc  #>${arg2.number.toInt()}""")
 | 
			
		||||
                        }
 | 
			
		||||
                        else -> {
 | 
			
		||||
                            asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                sec
 | 
			
		||||
                                sbc  P8ZP_SCRATCH_W1
 | 
			
		||||
                                tya
 | 
			
		||||
                                sbc  P8ZP_SCRATCH_W1+1""")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    when (arg2) {
 | 
			
		||||
                        is PtIdentifier -> {
 | 
			
		||||
                            asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
 | 
			
		||||
@@ -370,6 +409,52 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
+""")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else
 | 
			
		||||
                throw AssemblyError("args for cmp() should have same dt")
 | 
			
		||||
        } else if(arg1.type.isLong) {
 | 
			
		||||
            if(arg2.type.isLong) {
 | 
			
		||||
                if(arg1 is PtIdentifier && arg2 is PtNumber) {
 | 
			
		||||
                    val var1 = asmgen.asmVariableName(arg1)
 | 
			
		||||
                    val hex = arg2.number.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        sec
 | 
			
		||||
                        lda  $var1
 | 
			
		||||
                        sbc  #$${hex.substring(6, 8)}
 | 
			
		||||
                        lda  $var1+1
 | 
			
		||||
                        sbc  #$${hex.substring(4, 6)}
 | 
			
		||||
                        lda  $var1+2
 | 
			
		||||
                        sbc  #$${hex.substring(2, 4)}
 | 
			
		||||
                        lda  $var1+3
 | 
			
		||||
                        sbc  #$${hex.take(2)}""")
 | 
			
		||||
                } else if(arg1 is PtIdentifier && arg2 is PtIdentifier) {
 | 
			
		||||
                    val var1 = asmgen.asmVariableName(arg1)
 | 
			
		||||
                    val var2 = asmgen.asmVariableName(arg2)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        sec
 | 
			
		||||
                        lda  $var1
 | 
			
		||||
                        sbc  $var2
 | 
			
		||||
                        lda  $var1+1
 | 
			
		||||
                        sbc  $var2+1
 | 
			
		||||
                        lda  $var1+2
 | 
			
		||||
                        sbc  $var2+2
 | 
			
		||||
                        lda  $var1+3
 | 
			
		||||
                        sbc  $var2+3""")
 | 
			
		||||
                } else {
 | 
			
		||||
                    // cmp() doesn't return a value and as such can't be used in an expression, so no need to save the temp registers' original values
 | 
			
		||||
                    assignAsmGen.assignExpressionToRegister(arg2, RegisterOrPair.R14R15_32, true)
 | 
			
		||||
                    assignAsmGen.assignExpressionToRegister(arg1, RegisterOrPair.R12R13_32, true)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        sec
 | 
			
		||||
                        lda  cx16.r12
 | 
			
		||||
                        sbc  cx16.r14
 | 
			
		||||
                        lda  cx16.r12+1
 | 
			
		||||
                        sbc  cx16.r14+1
 | 
			
		||||
                        lda  cx16.r12+2
 | 
			
		||||
                        sbc  cx16.r14+2
 | 
			
		||||
                        lda  cx16.r12+3
 | 
			
		||||
                        sbc  cx16.r14+3""")
 | 
			
		||||
                }
 | 
			
		||||
            } else
 | 
			
		||||
                throw AssemblyError("args for cmp() should have same dt")
 | 
			
		||||
        }
 | 
			
		||||
@@ -380,8 +465,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
            throw AssemblyError("should not discard result of memory allocation at $fcall")
 | 
			
		||||
        val name = (fcall.args[0] as PtString).value
 | 
			
		||||
        require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
 | 
			
		||||
        val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
 | 
			
		||||
        val addressOf = PtAddressOf(fcall.position)
 | 
			
		||||
 | 
			
		||||
        val slabname = PtIdentifier("$StMemorySlabBlockName.memory_$name", DataType.UWORD, fcall.position)
 | 
			
		||||
        val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
 | 
			
		||||
        addressOf.add(slabname)
 | 
			
		||||
        addressOf.parent = fcall
 | 
			
		||||
        val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
 | 
			
		||||
@@ -390,6 +476,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
        asmgen.translateNormalAssignment(assign, fcall.definingISub())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        if(discardResult)
 | 
			
		||||
            throw AssemblyError("should not discard result of struct allocation at $fcall")
 | 
			
		||||
        // ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
 | 
			
		||||
        val prefix = if(fcall.args.isEmpty()) "${StStructInstanceBlockName}_bss" else StStructInstanceBlockName
 | 
			
		||||
        val labelname = PtIdentifier("$prefix.${SymbolTable.labelnameForStructInstance(fcall)}", fcall.type, fcall.position)
 | 
			
		||||
        val addressOf = PtAddressOf(fcall.type, true, fcall.position)
 | 
			
		||||
        addressOf.add(labelname)
 | 
			
		||||
        addressOf.parent = fcall
 | 
			
		||||
        val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
 | 
			
		||||
        val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
 | 
			
		||||
        val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
 | 
			
		||||
        asmgen.translateNormalAssignment(assign, fcall.definingISub())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
 | 
			
		||||
        translateArguments(fcall, scope)
 | 
			
		||||
        when(fcall.args[0].type.base) {
 | 
			
		||||
@@ -401,6 +503,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                asmgen.out("  jsr  prog8_lib.func_sqrt16_into_A")
 | 
			
		||||
                assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> TODO("sqrt LONG ${fcall.position}")
 | 
			
		||||
            BaseDataType.FLOAT -> {
 | 
			
		||||
                asmgen.out("  jsr  floats.func_sqrt_into_FAC1")
 | 
			
		||||
                assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
 | 
			
		||||
@@ -415,8 +518,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
            BaseDataType.UBYTE -> {
 | 
			
		||||
                when (what) {
 | 
			
		||||
                    is PtArrayIndexer -> {
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        asmgen.out("  lda  ${varname},x |  lsr  a |  bcc  + |  ora  #$80 |+  |  sta  ${varname},x")
 | 
			
		||||
                    }
 | 
			
		||||
                    is PtMemoryByte -> {
 | 
			
		||||
@@ -438,8 +544,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
            BaseDataType.UWORD -> {
 | 
			
		||||
                when (what) {
 | 
			
		||||
                    is PtArrayIndexer -> {
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        if(what.splitWords)
 | 
			
		||||
                            asmgen.out("  lsr  ${varname}_msb,x |  ror  ${varname}_lsb,x |  bcc  + |  lda  ${varname}_msb,x |  ora  #$80 |  sta  ${varname}_msb,x |+ ")
 | 
			
		||||
                        else
 | 
			
		||||
@@ -452,6 +560,26 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                // a bit strange, rotating a signed type, but it has to do for now
 | 
			
		||||
                when(what) {
 | 
			
		||||
                    is PtArrayIndexer -> TODO("ror2 long array ${what.position}")
 | 
			
		||||
                    is PtIdentifier -> {
 | 
			
		||||
                        val variable = asmgen.asmVariableName(what)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            lsr  $variable+3
 | 
			
		||||
                            ror  $variable+2
 | 
			
		||||
                            ror  $variable+1
 | 
			
		||||
                            ror  $variable
 | 
			
		||||
                            bcc  +
 | 
			
		||||
                            lda  $variable+3
 | 
			
		||||
                            ora  #$80
 | 
			
		||||
                            sta  $variable+3
 | 
			
		||||
+""")
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird type")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -465,7 +593,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  php")   // save Carry
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  plp")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        asmgen.out("  ror  ${varname},x")
 | 
			
		||||
                    }
 | 
			
		||||
                    is PtMemoryByte -> {
 | 
			
		||||
@@ -498,7 +629,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  php")   // save Carry
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  plp")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        if(what.splitWords)
 | 
			
		||||
                            asmgen.out("  ror  ${varname}_msb,x |  ror  ${varname}_lsb,x")
 | 
			
		||||
                        else
 | 
			
		||||
@@ -511,6 +644,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                // a bit strange, rotating a signed type, but it has to do for now
 | 
			
		||||
                when(what) {
 | 
			
		||||
                    is PtArrayIndexer -> TODO("ror long array ${what.position}")
 | 
			
		||||
                    is PtIdentifier -> {
 | 
			
		||||
                        val variable = asmgen.asmVariableName(what)
 | 
			
		||||
                        asmgen.out("  ror  $variable+3 |  ror  $variable+2 |  ror  $variable+1 |  ror  $variable")
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird type")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -521,8 +665,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
            BaseDataType.UBYTE -> {
 | 
			
		||||
                when (what) {
 | 
			
		||||
                    is PtArrayIndexer -> {
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        asmgen.out("  lda  ${varname},x |  cmp  #$80 |  rol  a |  sta  ${varname},x")
 | 
			
		||||
                    }
 | 
			
		||||
                    is PtMemoryByte -> {
 | 
			
		||||
@@ -545,7 +691,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                when (what) {
 | 
			
		||||
                    is PtArrayIndexer -> {
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        if(what.splitWords)
 | 
			
		||||
                            asmgen.out("  asl  ${varname}_lsb,x |  rol  ${varname}_msb,x |  bcc  + |  inc  ${varname}_lsb,x |+")
 | 
			
		||||
                        else
 | 
			
		||||
@@ -558,6 +706,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                // a bit strange, rotating a signed type, but it has to do for now
 | 
			
		||||
                when(what) {
 | 
			
		||||
                    is PtArrayIndexer -> TODO("rol2 long array ${what.position}")
 | 
			
		||||
                    is PtIdentifier -> {
 | 
			
		||||
                        val variable = asmgen.asmVariableName(what)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            asl  $variable
 | 
			
		||||
                            rol  $variable+1
 | 
			
		||||
                            rol  $variable+2
 | 
			
		||||
                            rol  $variable+3
 | 
			
		||||
                            bcc  +
 | 
			
		||||
                            inc  $variable
 | 
			
		||||
+""")
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird type")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -571,7 +737,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  php")   // save Carry
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  plp")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        asmgen.out("  rol  ${varname},x")
 | 
			
		||||
                    }
 | 
			
		||||
                    is PtMemoryByte -> {
 | 
			
		||||
@@ -604,7 +772,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  php")   // save Carry
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
 | 
			
		||||
                        if(!what.index.isSimple()) asmgen.out("  plp")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable)
 | 
			
		||||
                        if(what.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${what.position}")
 | 
			
		||||
                        val varname = asmgen.asmVariableName(what.variable!!)
 | 
			
		||||
                        if(what.splitWords)
 | 
			
		||||
                            asmgen.out("  rol  ${varname}_lsb,x |  rol  ${varname}_msb,x")
 | 
			
		||||
                        else
 | 
			
		||||
@@ -617,6 +787,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                // a bit strange, rotating a signed type, but it has to do for now
 | 
			
		||||
                when(what) {
 | 
			
		||||
                    is PtArrayIndexer -> TODO("rol long array ${what.position}")
 | 
			
		||||
                    is PtIdentifier -> {
 | 
			
		||||
                        val variable = asmgen.asmVariableName(what)
 | 
			
		||||
                        asmgen.out("  rol  $variable |  rol  $variable+1 |  rol  $variable+2 |  rol  $variable+3")
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird type")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird type")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -633,7 +814,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                val elementSize: Int
 | 
			
		||||
                val msbAdd: Int
 | 
			
		||||
                if(indexer.splitWords) {
 | 
			
		||||
                    val arrayVariable = indexer.variable
 | 
			
		||||
                    val arrayVariable = indexer.variable ?: TODO("support for ptr indexing ${indexer.position}")
 | 
			
		||||
                    indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
 | 
			
		||||
                    indexer.children[0].parent = indexer
 | 
			
		||||
                    elementSize = 1
 | 
			
		||||
@@ -688,6 +869,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
            BaseDataType.BYTE -> asmgen.out("  jsr  prog8_lib.func_sign_b_into_A")
 | 
			
		||||
            BaseDataType.UWORD -> asmgen.out("  jsr  prog8_lib.func_sign_uw_into_A")
 | 
			
		||||
            BaseDataType.WORD -> asmgen.out("  jsr  prog8_lib.func_sign_w_into_A")
 | 
			
		||||
            BaseDataType.LONG -> asmgen.out("  jsr  prog8_lib.func_sign_l_r14r15_into_A")          // note: long arg is stored in R14:R15
 | 
			
		||||
            BaseDataType.FLOAT -> asmgen.out("  jsr  floats.func_sign_f_into_A")
 | 
			
		||||
            else -> throw AssemblyError("weird type $dt")
 | 
			
		||||
        }
 | 
			
		||||
@@ -706,6 +888,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                asmgen.out("  jsr  prog8_lib.abs_w_into_AY")
 | 
			
		||||
                assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                asmgen.out("  jsr  prog8_lib.abs_l_into_R14R15")
 | 
			
		||||
                assignAsmGen.assignRegisterLong(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.R14R15_32, true, fcall.position, scope, asmgen), RegisterOrPair.R14R15_32)
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.FLOAT -> {
 | 
			
		||||
                asmgen.out("  jsr  floats.func_abs_f_into_FAC1")
 | 
			
		||||
                assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
 | 
			
		||||
@@ -739,7 +925,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                asmgen.assignConstFloatToPointerAY(number)
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
 | 
			
		||||
                val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
 | 
			
		||||
                asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
 | 
			
		||||
                asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
@@ -754,6 +940,65 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcPokeBool(fcall: PtBuiltinFunctionCall) {
 | 
			
		||||
        when(val addrExpr = fcall.args[0]) {
 | 
			
		||||
            is PtNumber -> {
 | 
			
		||||
                asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
 | 
			
		||||
                val addr = addrExpr.number.toHex()
 | 
			
		||||
                asmgen.out("  sta  $addr")
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            is PtIdentifier -> {
 | 
			
		||||
                val varname = asmgen.asmVariableName(addrExpr)
 | 
			
		||||
                if(asmgen.isZpVar(addrExpr)) {
 | 
			
		||||
                    // pointervar is already in the zero page, no need to copy
 | 
			
		||||
                    asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
 | 
			
		||||
                    asmgen.storeIndirectByteReg(CpuRegister.A, varname, 0u, false, false)
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            is PtBinaryExpression -> {
 | 
			
		||||
                val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
 | 
			
		||||
                val pointer = result?.first as? PtIdentifier
 | 
			
		||||
                if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
 | 
			
		||||
                    // can do ZP,Y indexing
 | 
			
		||||
                    val varname = asmgen.asmVariableName(pointer)
 | 
			
		||||
                    asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
 | 
			
		||||
                    asmgen.saveRegisterStack(CpuRegister.Y, false)
 | 
			
		||||
                    asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
 | 
			
		||||
                    asmgen.restoreRegisterStack(CpuRegister.Y, true)
 | 
			
		||||
                    asmgen.out("  sta  ($varname),y")
 | 
			
		||||
                    return
 | 
			
		||||
                } else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
 | 
			
		||||
                    asmgen.assignExpressionToRegister(addrExpr.right, RegisterOrPair.AY, false)
 | 
			
		||||
                    val ptrName = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        clc
 | 
			
		||||
                        adc  $ptrName
 | 
			
		||||
                        sta  P8ZP_SCRATCH_W2
 | 
			
		||||
                        tya
 | 
			
		||||
                        adc  $ptrName+1
 | 
			
		||||
                        sta  P8ZP_SCRATCH_W2+1""")
 | 
			
		||||
                    asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)      // TODO *could* overwerite SCRATCH_W2 if it's a compliated expression...
 | 
			
		||||
                    asmgen.storeIndirectByteReg(CpuRegister.A, "P8ZP_SCRATCH_W2", 0u, false, false)
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> { /* fall through */ }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // fall through method:
 | 
			
		||||
        if(fcall.args[1].isSimple()) {
 | 
			
		||||
            asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
 | 
			
		||||
            asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
 | 
			
		||||
        }  else {
 | 
			
		||||
            asmgen.pushCpuStack(BaseDataType.UBYTE, fcall.args[1])
 | 
			
		||||
            asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
 | 
			
		||||
            asmgen.restoreRegisterStack(CpuRegister.A, false)
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.storeIndirectByteReg(CpuRegister.A, "P8ZP_SCRATCH_W1", 0u, false, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
 | 
			
		||||
        when(val addrExpr = fcall.args[0]) {
 | 
			
		||||
            is PtNumber -> {
 | 
			
		||||
@@ -767,20 +1012,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                if(asmgen.isZpVar(addrExpr)) {
 | 
			
		||||
                    // pointervar is already in the zero page, no need to copy
 | 
			
		||||
                    asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
 | 
			
		||||
                    if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            sta  ($varname)
 | 
			
		||||
                            txa
 | 
			
		||||
                            ldy  #1
 | 
			
		||||
                            sta  ($varname),y""")
 | 
			
		||||
                    } else {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            ldy  #0
 | 
			
		||||
                            sta  ($varname),y
 | 
			
		||||
                            txa
 | 
			
		||||
                            iny
 | 
			
		||||
                            sta  ($varname),y""")
 | 
			
		||||
                    }
 | 
			
		||||
                    asmgen.storeIndirectWordReg(RegisterOrPair.AX, varname, 0u)
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -800,6 +1032,20 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                            iny
 | 
			
		||||
                            sta  ($varname),y""")
 | 
			
		||||
                    return
 | 
			
		||||
                } else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
 | 
			
		||||
                    asmgen.assignExpressionToRegister(addrExpr.right, RegisterOrPair.AY, false)
 | 
			
		||||
                    val ptrName = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        clc
 | 
			
		||||
                        adc  $ptrName
 | 
			
		||||
                        sta  P8ZP_SCRATCH_W2
 | 
			
		||||
                        tya
 | 
			
		||||
                        adc  $ptrName+1
 | 
			
		||||
                        sta  P8ZP_SCRATCH_W2+1""")
 | 
			
		||||
                    asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)      // TODO *could* overwerite SCRATCH_W2 if it's a compliated expression...
 | 
			
		||||
                    asmgen.out("  jsr  prog8_lib.func_pokew_scratchW2")
 | 
			
		||||
                    //asmgen.storeIndirectWordReg(RegisterOrPair.AY, "P8ZP_SCRATCH_W2", 0u)
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> { /* fall through */ }
 | 
			
		||||
@@ -810,12 +1056,65 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
        asmgen.out("  jsr  prog8_lib.func_pokew")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcPokeL(fcall: PtBuiltinFunctionCall) {
 | 
			
		||||
        // TODO optimize for the simple cases
 | 
			
		||||
        asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
 | 
			
		||||
        asmgen.saveRegisterStack(CpuRegister.A, false)
 | 
			
		||||
        asmgen.saveRegisterStack(CpuRegister.Y, false)
 | 
			
		||||
        // it's a statement so no need to preserve R14:R15
 | 
			
		||||
        asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R14R15_32, true)
 | 
			
		||||
        asmgen.restoreRegisterStack(CpuRegister.Y, false)
 | 
			
		||||
        asmgen.restoreRegisterStack(CpuRegister.A, false)
 | 
			
		||||
        asmgen.out("  jsr  prog8_lib.func_pokel")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcPeekF(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
 | 
			
		||||
        asmgen.out("  jsr  floats.MOVFM")
 | 
			
		||||
        if(resultRegister!=null) {
 | 
			
		||||
            assignAsmGen.assignFAC1float(
 | 
			
		||||
                AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
 | 
			
		||||
                AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), register=resultRegister, position=fcall.position))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcPeekBool(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        fun fallback() {
 | 
			
		||||
            asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
 | 
			
		||||
            asmgen.out("  jsr  prog8_lib.func_peek")
 | 
			
		||||
        }
 | 
			
		||||
        when(val addrExpr = fcall.args[0]) {
 | 
			
		||||
            is PtNumber -> {
 | 
			
		||||
                val addr = addrExpr.number.toHex()
 | 
			
		||||
                asmgen.out("  lda  $addr")
 | 
			
		||||
            }
 | 
			
		||||
            is PtIdentifier -> {
 | 
			
		||||
                val varname = asmgen.asmVariableName(addrExpr)
 | 
			
		||||
                if(asmgen.isZpVar(addrExpr))
 | 
			
		||||
                    asmgen.loadIndirectByte(varname, 0u)
 | 
			
		||||
                else
 | 
			
		||||
                    fallback()
 | 
			
		||||
            }
 | 
			
		||||
            is PtBinaryExpression -> {
 | 
			
		||||
                val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
 | 
			
		||||
                val pointer = result?.first as? PtIdentifier
 | 
			
		||||
                if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
 | 
			
		||||
                    // can do ZP,Y indexing
 | 
			
		||||
                    val varname = asmgen.asmVariableName(pointer)
 | 
			
		||||
                    asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
 | 
			
		||||
                    asmgen.out("  lda  ($varname),y")
 | 
			
		||||
                } else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
 | 
			
		||||
                    readValueFromPointerPlusOffset(addrExpr.left as PtIdentifier, addrExpr.right, BaseDataType.BOOL)
 | 
			
		||||
                } else fallback()
 | 
			
		||||
            }
 | 
			
		||||
            else -> fallback()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        when(resultRegister ?: RegisterOrPair.A) {
 | 
			
		||||
            RegisterOrPair.A -> {}
 | 
			
		||||
            RegisterOrPair.X -> asmgen.out("  tax")
 | 
			
		||||
            RegisterOrPair.Y -> asmgen.out("  tay")
 | 
			
		||||
            in Cx16VirtualRegisters -> asmgen.out("  sta  cx16.${resultRegister.toString().lowercase()}")
 | 
			
		||||
            else -> throw AssemblyError("invalid reg")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -831,25 +1130,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
            }
 | 
			
		||||
            is PtIdentifier -> {
 | 
			
		||||
                val varname = asmgen.asmVariableName(addrExpr)
 | 
			
		||||
                if(asmgen.isZpVar(addrExpr)) {
 | 
			
		||||
                    // pointervar is already in the zero page, no need to copy
 | 
			
		||||
                    if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            ldy  #1
 | 
			
		||||
                            lda  ($varname),y
 | 
			
		||||
                            tay
 | 
			
		||||
                            lda  ($varname)""")
 | 
			
		||||
                    } else {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            ldy  #0
 | 
			
		||||
                            lda  ($varname),y
 | 
			
		||||
                            tax
 | 
			
		||||
                            iny
 | 
			
		||||
                            lda  ($varname),y
 | 
			
		||||
                            tay
 | 
			
		||||
                            txa""")
 | 
			
		||||
                    }
 | 
			
		||||
                } else fallback()
 | 
			
		||||
                if(asmgen.isZpVar(addrExpr))
 | 
			
		||||
                    asmgen.loadIndirectWord(varname, 0u)
 | 
			
		||||
                else
 | 
			
		||||
                    fallback()
 | 
			
		||||
            }
 | 
			
		||||
            is PtBinaryExpression -> {
 | 
			
		||||
                val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
 | 
			
		||||
@@ -865,6 +1149,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                        lda  ($varname),y
 | 
			
		||||
                        tay
 | 
			
		||||
                        txa""")
 | 
			
		||||
                } else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
 | 
			
		||||
                    readValueFromPointerPlusOffset(addrExpr.left as PtIdentifier, addrExpr.right, BaseDataType.UWORD)
 | 
			
		||||
                } else fallback()
 | 
			
		||||
            }
 | 
			
		||||
            else -> fallback()
 | 
			
		||||
@@ -882,6 +1168,36 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun readValueFromPointerPlusOffset(ptr: PtIdentifier, offset: PtExpression, dt: BaseDataType) {
 | 
			
		||||
        val varname = asmgen.asmVariableName(ptr)
 | 
			
		||||
        asmgen.assignExpressionToRegister(offset, RegisterOrPair.AY)
 | 
			
		||||
        asmgen.out("""
 | 
			
		||||
            clc
 | 
			
		||||
            adc  $varname
 | 
			
		||||
            sta  P8ZP_SCRATCH_W1
 | 
			
		||||
            tya
 | 
			
		||||
            adc  $varname+1
 | 
			
		||||
            sta  P8ZP_SCRATCH_W1+1""")
 | 
			
		||||
        if (dt.isByteOrBool) {
 | 
			
		||||
            if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
 | 
			
		||||
                asmgen.out("  lda  (P8ZP_SCRATCH_W1)")
 | 
			
		||||
            } else {
 | 
			
		||||
                asmgen.out("  ldy  #0 |  lda  (P8ZP_SCRATCH_W1),y")
 | 
			
		||||
            }
 | 
			
		||||
        } else if(dt.isWord) {
 | 
			
		||||
            asmgen.out("  jsr  prog8_lib.func_peekw.from_scratchW1")
 | 
			
		||||
        } else throw AssemblyError("unsupported type for peek $dt")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcPeekL(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        // TODO optimize for the simple cases
 | 
			
		||||
        asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
 | 
			
		||||
        asmgen.out("  jsr  prog8_lib.func_peekl")
 | 
			
		||||
        val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.R14R15_32, true, fcall.position, fcall.definingISub(), asmgen)
 | 
			
		||||
        assignAsmGen.assignRegisterLong(targetReg, RegisterOrPair.R14R15_32)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        val signed = fcall.type.isSigned
 | 
			
		||||
        when {
 | 
			
		||||
@@ -901,6 +1217,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
 | 
			
		||||
                assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
 | 
			
		||||
            }
 | 
			
		||||
            fcall.type.isLong -> {
 | 
			
		||||
                TODO("clamp long  ${fcall.position}")
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("invalid dt")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -957,6 +1276,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
 | 
			
		||||
                asmgen.assignRegister(RegisterOrPair.AY, targetReg)
 | 
			
		||||
            }
 | 
			
		||||
            fcall.type.isLong -> {
 | 
			
		||||
                TODO("min long  ${fcall.position}")
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                throw AssemblyError("min float not supported")
 | 
			
		||||
            }
 | 
			
		||||
@@ -965,20 +1287,29 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
 | 
			
		||||
    private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        val signed = fcall.type.isSigned
 | 
			
		||||
        if(fcall.type.isByte) {
 | 
			
		||||
        when {
 | 
			
		||||
            fcall.type.isByte -> {
 | 
			
		||||
                asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type)     // left
 | 
			
		||||
                asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)          // right
 | 
			
		||||
                asmgen.out("  cmp  P8ZP_SCRATCH_B1")
 | 
			
		||||
            if(signed) asmgen.out("  bpl  +") else asmgen.out("  bcs  +")
 | 
			
		||||
                if (signed) asmgen.out("  bpl  +") else asmgen.out("  bcs  +")
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  P8ZP_SCRATCH_B1
 | 
			
		||||
+""")
 | 
			
		||||
            val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
 | 
			
		||||
+"""
 | 
			
		||||
                )
 | 
			
		||||
                val targetReg = AsmAssignTarget.fromRegisters(
 | 
			
		||||
                    resultRegister ?: RegisterOrPair.A,
 | 
			
		||||
                    signed,
 | 
			
		||||
                    fcall.position,
 | 
			
		||||
                    fcall.definingISub(),
 | 
			
		||||
                    asmgen
 | 
			
		||||
                )
 | 
			
		||||
                asmgen.assignRegister(RegisterOrPair.A, targetReg)
 | 
			
		||||
        } else if(fcall.type.isWord) {
 | 
			
		||||
            }
 | 
			
		||||
            fcall.type.isWord -> {
 | 
			
		||||
                asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type)     // left
 | 
			
		||||
                asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type)     // right
 | 
			
		||||
            if(signed) {
 | 
			
		||||
                if (signed) {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  P8ZP_SCRATCH_W1
 | 
			
		||||
                        ldy  P8ZP_SCRATCH_W1+1
 | 
			
		||||
@@ -1010,12 +1341,57 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                        ldy  P8ZP_SCRATCH_W2+1
 | 
			
		||||
+""")
 | 
			
		||||
                }
 | 
			
		||||
            val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
 | 
			
		||||
                val targetReg = AsmAssignTarget.fromRegisters(
 | 
			
		||||
                    resultRegister ?: RegisterOrPair.AY,
 | 
			
		||||
                    signed,
 | 
			
		||||
                    fcall.position,
 | 
			
		||||
                    fcall.definingISub(),
 | 
			
		||||
                    asmgen
 | 
			
		||||
                )
 | 
			
		||||
                asmgen.assignRegister(RegisterOrPair.AY, targetReg)
 | 
			
		||||
        } else {
 | 
			
		||||
            }
 | 
			
		||||
            fcall.type.isLong -> {
 | 
			
		||||
                TODO("max long  ${fcall.position}")
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                throw AssemblyError("max float not supported")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMklong(fcall: PtBuiltinFunctionCall) {
 | 
			
		||||
        // result long in R14:R15   (r14=lsw, r15=msw)
 | 
			
		||||
 | 
			
		||||
        fun isArgRegister(expression: PtExpression, reg: RegisterOrPair): Boolean {
 | 
			
		||||
            if(expression !is PtIdentifier)
 | 
			
		||||
                return false
 | 
			
		||||
            return expression.name.startsWith("cx16.${reg.name.lowercase()}")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(fcall.args.size==2) {
 | 
			
		||||
            // mklong2(msw, lsw)
 | 
			
		||||
            if(isArgRegister(fcall.args[0], RegisterOrPair.R14) || isArgRegister(fcall.args[0], RegisterOrPair.R15) ||
 | 
			
		||||
                isArgRegister(fcall.args[1], RegisterOrPair.R14) || isArgRegister(fcall.args[1], RegisterOrPair.R15)) {
 | 
			
		||||
                error("cannot use R14 and/or R15 as arguments for mklong2 because the result should go into R0:R1 ${fcall.position}")
 | 
			
		||||
            } else {
 | 
			
		||||
                assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r15", DataType.UWORD)
 | 
			
		||||
                assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r14", DataType.UWORD)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // mklong(msb, b2, b1, lsb)
 | 
			
		||||
            if(isArgRegister(fcall.args[0], RegisterOrPair.R14) || isArgRegister(fcall.args[0], RegisterOrPair.R15) ||
 | 
			
		||||
                isArgRegister(fcall.args[1], RegisterOrPair.R14) || isArgRegister(fcall.args[1], RegisterOrPair.R15) ||
 | 
			
		||||
                isArgRegister(fcall.args[2], RegisterOrPair.R14) || isArgRegister(fcall.args[2], RegisterOrPair.R15) ||
 | 
			
		||||
                isArgRegister(fcall.args[3], RegisterOrPair.R14) || isArgRegister(fcall.args[3], RegisterOrPair.R15)) {
 | 
			
		||||
                error("cannot use R14 and/or R15 as arguments for mklong because the result should go into R14:R15 ${fcall.position}")
 | 
			
		||||
            } else {
 | 
			
		||||
                assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r15H", DataType.UBYTE)
 | 
			
		||||
                assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r15L", DataType.UBYTE)
 | 
			
		||||
                assignAsmGen.assignExpressionToVariable(fcall.args[2], "cx16.r14H", DataType.UBYTE)
 | 
			
		||||
                assignAsmGen.assignExpressionToVariable(fcall.args[3], "cx16.r14L", DataType.UBYTE)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        val reg = resultRegister ?: RegisterOrPair.AY
 | 
			
		||||
@@ -1084,10 +1460,43 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMsbLong(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        val arg = fcall.args.single()
 | 
			
		||||
        if (!arg.type.isLong)
 | 
			
		||||
            throw AssemblyError("msb__long requires long argument")
 | 
			
		||||
        if (arg is PtNumber)
 | 
			
		||||
            throw AssemblyError("msb(const) should have been const-folded away")
 | 
			
		||||
 | 
			
		||||
        if (arg is PtIdentifier) {
 | 
			
		||||
            val sourceName = asmgen.asmVariableName(arg)
 | 
			
		||||
            when(resultRegister) {
 | 
			
		||||
                null, RegisterOrPair.A -> asmgen.out("  lda  $sourceName+3")
 | 
			
		||||
                RegisterOrPair.X -> asmgen.out("  ldx  $sourceName+3")
 | 
			
		||||
                RegisterOrPair.Y -> asmgen.out("  ldy  $sourceName+3")
 | 
			
		||||
                RegisterOrPair.AX -> asmgen.out("  lda  $sourceName+3 |  ldx  #0")
 | 
			
		||||
                RegisterOrPair.AY -> asmgen.out("  lda  $sourceName+3 |  ldy  #0")
 | 
			
		||||
                RegisterOrPair.XY -> asmgen.out("  ldx  $sourceName+3 |  ldy  #0")
 | 
			
		||||
                in Cx16VirtualRegisters -> {
 | 
			
		||||
                    val regname = resultRegister.name.lowercase()
 | 
			
		||||
                    if(asmgen.isTargetCpu(CpuType.CPU65C02))
 | 
			
		||||
                        asmgen.out("  lda  $sourceName+3 |  sta  cx16.$regname |  stz  cx16.$regname+1")
 | 
			
		||||
                    else
 | 
			
		||||
                        asmgen.out("  lda  $sourceName+3 |  sta  cx16.$regname |  lda  #0 |  sta  cx16.$regname+1")
 | 
			
		||||
                }
 | 
			
		||||
                in combinedLongRegisters -> {
 | 
			
		||||
                    TODO("msb__long into long register ${fcall.position}")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            TODO("msb__long from $arg  ${fcall.position}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        val arg = fcall.args.single()
 | 
			
		||||
        if (!arg.type.isWord)
 | 
			
		||||
            throw AssemblyError("msb required word argument")
 | 
			
		||||
            throw AssemblyError("msb requires word argument")
 | 
			
		||||
        if (arg is PtNumber)
 | 
			
		||||
            throw AssemblyError("msb(const) should have been const-folded away")
 | 
			
		||||
        if (arg is PtIdentifier) {
 | 
			
		||||
@@ -1106,13 +1515,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    else
 | 
			
		||||
                        asmgen.out("  lda  $sourceName+1 |  sta  cx16.$regname |  lda  #0 |  sta  cx16.$regname+1")
 | 
			
		||||
                }
 | 
			
		||||
                in combinedLongRegisters -> {
 | 
			
		||||
                    TODO("msb into long register ${fcall.position}")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
 | 
			
		||||
                // just read the msb byte out of the word array
 | 
			
		||||
                if(arg.splitWords) {
 | 
			
		||||
                    val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
 | 
			
		||||
                    if(arg.variable==null)
 | 
			
		||||
                        TODO("support for ptr indexing ${arg.position}")
 | 
			
		||||
                    val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
 | 
			
		||||
                    when(resultRegister) {
 | 
			
		||||
                        null, RegisterOrPair.A -> {
 | 
			
		||||
                            asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
 | 
			
		||||
@@ -1129,7 +1543,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                        else -> throw AssemblyError("invalid reg")
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    val arrayVar = asmgen.asmVariableName(arg.variable)
 | 
			
		||||
                    if(arg.variable==null)
 | 
			
		||||
                        TODO("support for ptr indexing ${arg.position}")
 | 
			
		||||
                    val arrayVar = asmgen.asmVariableName(arg.variable!!)
 | 
			
		||||
                    when(resultRegister) {
 | 
			
		||||
                        null, RegisterOrPair.A -> {
 | 
			
		||||
                            asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
 | 
			
		||||
@@ -1178,15 +1594,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
 | 
			
		||||
                    asmgen.out("  sty  ${reg}L |  lda  #0 |  sta  ${reg}H |  lda  ${reg}L")
 | 
			
		||||
                }
 | 
			
		||||
                in combinedLongRegisters -> {
 | 
			
		||||
                    TODO("msb into long register ${fcall.position}")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
    private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, fromLong: Boolean) {
 | 
			
		||||
        val arg = fcall.args.single()
 | 
			
		||||
        if (!arg.type.isWord)
 | 
			
		||||
            throw AssemblyError("lsb required word argument")
 | 
			
		||||
        if(fromLong) {
 | 
			
		||||
            if (!arg.type.isLong) throw AssemblyError("lsb__long requires long")
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!arg.type.isWord) throw AssemblyError("lsb requires word")
 | 
			
		||||
        }
 | 
			
		||||
        if (arg is PtNumber)
 | 
			
		||||
            throw AssemblyError("lsb(const) should have been const-folded away")
 | 
			
		||||
 | 
			
		||||
@@ -1206,12 +1628,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    else
 | 
			
		||||
                        asmgen.out("  lda  $sourceName |  sta  cx16.$regname |  lda  #0 |  sta  cx16.$regname+1")
 | 
			
		||||
                }
 | 
			
		||||
                in combinedLongRegisters -> {
 | 
			
		||||
                    TODO("lsb into long register ${fcall.position}")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
 | 
			
		||||
                // just read the lsb byte out of the word array
 | 
			
		||||
                val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
 | 
			
		||||
                if(arg.variable==null)
 | 
			
		||||
                    TODO("support for ptr indexing ${arg.position}")
 | 
			
		||||
 | 
			
		||||
                val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable!!)+"_lsb" else asmgen.asmVariableName(arg.variable!!)
 | 
			
		||||
                when(resultRegister) {
 | 
			
		||||
                    null, RegisterOrPair.A -> {
 | 
			
		||||
                        asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
 | 
			
		||||
@@ -1261,11 +1689,87 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
 | 
			
		||||
                    asmgen.out("  sta  ${reg}L |  ldy  #0 |  sty  ${reg}H |  cmp  #0")
 | 
			
		||||
                }
 | 
			
		||||
                in combinedLongRegisters -> {
 | 
			
		||||
                    TODO("lsb into long register ${fcall.position}")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMsw(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        val arg = fcall.args.single()
 | 
			
		||||
        if (!arg.type.isLong)
 | 
			
		||||
            throw AssemblyError("msw requires long argument")
 | 
			
		||||
        if (arg is PtNumber)
 | 
			
		||||
            throw AssemblyError("msw(const) should have been const-folded away")
 | 
			
		||||
        if (arg is PtIdentifier) {
 | 
			
		||||
            val sourceName = asmgen.asmVariableName(arg)
 | 
			
		||||
            when(resultRegister) {
 | 
			
		||||
                RegisterOrPair.AX -> asmgen.out("  lda  $sourceName+2 |  ldx  $sourceName+3")
 | 
			
		||||
                null, RegisterOrPair.AY -> asmgen.out("  lda  $sourceName+2 |  ldy  $sourceName+3")
 | 
			
		||||
                RegisterOrPair.XY -> asmgen.out("  ldx  $sourceName+2 |  ldy  $sourceName+3")
 | 
			
		||||
                in Cx16VirtualRegisters -> {
 | 
			
		||||
                    val regname = resultRegister.name.lowercase()
 | 
			
		||||
                    asmgen.out("  lda  $sourceName+2 |  sta  cx16.$regname |  lda  $sourceName+3 |  sta  cx16.$regname+1")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // TODO can't efficiently preserve R14:R15 on the stack because the result of the routine is likely to be in CPU registers,
 | 
			
		||||
            //      which in turn would be clobbered by popping stuff from the stack...
 | 
			
		||||
            asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15_32, true)
 | 
			
		||||
            when(resultRegister) {
 | 
			
		||||
                RegisterOrPair.AX -> asmgen.out("  lda  cx16.r15 |  ldx  cx16.r15+1")
 | 
			
		||||
                null, RegisterOrPair.AY -> asmgen.out("  lda  cx16.r15 |  ldy  cx16.r15+1")
 | 
			
		||||
                RegisterOrPair.XY -> asmgen.out("  ldx  cx16.r15 |  ldy  cx16.r15+1")
 | 
			
		||||
                in Cx16VirtualRegisters -> {
 | 
			
		||||
                    val regname = resultRegister.name.lowercase()
 | 
			
		||||
                    asmgen.out("  lda  cx16.r15 |  sta  cx16.$regname |  lda  cx16.r15+1 |  sta  cx16.$regname+1")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcLsw(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
 | 
			
		||||
        val arg = fcall.args.single()
 | 
			
		||||
        if (!arg.type.isLong)
 | 
			
		||||
            throw AssemblyError("lsw requires long argument")
 | 
			
		||||
        if (arg is PtNumber)
 | 
			
		||||
            throw AssemblyError("lsw(const) should have been const-folded away")
 | 
			
		||||
        if (arg is PtIdentifier) {
 | 
			
		||||
            val sourceName = asmgen.asmVariableName(arg)
 | 
			
		||||
            when(resultRegister) {
 | 
			
		||||
                RegisterOrPair.AX -> asmgen.out("  lda  $sourceName |  ldx  $sourceName+1")
 | 
			
		||||
                null, RegisterOrPair.AY -> asmgen.out("  lda  $sourceName |  ldy  $sourceName+1")
 | 
			
		||||
                RegisterOrPair.XY -> asmgen.out("  ldx  $sourceName |  ldy  $sourceName+1")
 | 
			
		||||
                in Cx16VirtualRegisters -> {
 | 
			
		||||
                    val regname = resultRegister.name.lowercase()
 | 
			
		||||
                    asmgen.out("  lda  $sourceName |  sta  cx16.$regname |  lda  $sourceName+1 |  sta  cx16.$regname+1")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // TODO can't efficiently preserve R14:R15 on the stack because the result of the routine is likely to be in CPU registers,
 | 
			
		||||
            //      which in turn would be clobbered by popping stuff from the stack...
 | 
			
		||||
            asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15_32, true)
 | 
			
		||||
            when(resultRegister) {
 | 
			
		||||
                RegisterOrPair.AX -> asmgen.out("  lda  cx16.r14 |  ldx  cx16.r14+1")
 | 
			
		||||
                null, RegisterOrPair.AY -> asmgen.out("  lda  cx16.r14 |  ldy  cx16.r14+1")
 | 
			
		||||
                RegisterOrPair.XY -> asmgen.out("  ldx  cx16.r14 |  ldy  cx16.r14+1")
 | 
			
		||||
                in Cx16VirtualRegisters -> {
 | 
			
		||||
                    if(resultRegister!=RegisterOrPair.R14) {
 | 
			
		||||
                        val regname = resultRegister.name.lowercase()
 | 
			
		||||
                        asmgen.out("  lda  cx16.r14 |  sta  cx16.$regname |  lda  cx16.r14+1 |  sta  cx16.$regname+1")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("invalid reg")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
 | 
			
		||||
        val signature = BuiltinFunctions.getValue(call.name)
 | 
			
		||||
        val callConv = signature.callConvention(call.args.map {
 | 
			
		||||
@@ -1276,7 +1780,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
        fun getSourceForFloat(value: PtExpression): AsmAssignSource {
 | 
			
		||||
            return when (value) {
 | 
			
		||||
                is PtIdentifier -> {
 | 
			
		||||
                    val addr = PtAddressOf(value.position)
 | 
			
		||||
                    val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
 | 
			
		||||
                    addr.add(value)
 | 
			
		||||
                    addr.parent = call
 | 
			
		||||
                    AsmAssignSource.fromAstSource(addr, program, asmgen)
 | 
			
		||||
@@ -1290,7 +1794,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
 | 
			
		||||
                    asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
 | 
			
		||||
                    val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
 | 
			
		||||
                    val addr = PtAddressOf(value.position)
 | 
			
		||||
                    val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
 | 
			
		||||
                    addr.add(variable)
 | 
			
		||||
                    addr.parent = call
 | 
			
		||||
                    asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
 | 
			
		||||
@@ -1308,9 +1812,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                    val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
 | 
			
		||||
                    val src = when  {
 | 
			
		||||
                        conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
 | 
			
		||||
                        conv.dt==BaseDataType.LONG -> TODO("LONG argument for builtin func ${call.position}")
 | 
			
		||||
                        conv.dt.isPassByRef -> {
 | 
			
		||||
                            // put the address of the argument in AY
 | 
			
		||||
                            val addr = PtAddressOf(value.position)
 | 
			
		||||
                            val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
 | 
			
		||||
                            addr.add(value)
 | 
			
		||||
                            addr.parent = call
 | 
			
		||||
                            AsmAssignSource.fromAstSource(addr, program, asmgen)
 | 
			
		||||
@@ -1326,9 +1831,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                conv.reg != null -> {
 | 
			
		||||
                    val src = when {
 | 
			
		||||
                        conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
 | 
			
		||||
                        conv.dt==BaseDataType.LONG -> AsmAssignSource.fromAstSource(value, program, asmgen)
 | 
			
		||||
                        conv.dt.isPassByRef -> {
 | 
			
		||||
                            // put the address of the argument in AY
 | 
			
		||||
                            val addr = PtAddressOf(value.position)
 | 
			
		||||
                            val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,value.position)
 | 
			
		||||
                            addr.add(value)
 | 
			
		||||
                            addr.parent = call
 | 
			
		||||
                            AsmAssignSource.fromAstSource(addr, program, asmgen)
 | 
			
		||||
@@ -1337,7 +1843,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
 | 
			
		||||
                            AsmAssignSource.fromAstSource(value, program, asmgen)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
 | 
			
		||||
                    val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, conv.dt.isSigned, value.position, null, asmgen)
 | 
			
		||||
                    val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
 | 
			
		||||
                    asmgen.translateNormalAssignment(assign, scope)
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ internal class ForLoopsAsmGen(
 | 
			
		||||
        if(asmgen.options.romable) {
 | 
			
		||||
            // cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
 | 
			
		||||
            // so we need to store the loop end value in a newly allocated temporary variable
 | 
			
		||||
            val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
 | 
			
		||||
            val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
 | 
			
		||||
            asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
 | 
			
		||||
            asmgen.out("  sta  $toValueVar")
 | 
			
		||||
            // pre-check for end already reached
 | 
			
		||||
@@ -296,7 +296,7 @@ $modifiedLabel  cmp  #0     ; modified
 | 
			
		||||
        if(asmgen.options.romable) {
 | 
			
		||||
            // cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
 | 
			
		||||
            // so we need to store the loop end value in a newly allocated temporary variable
 | 
			
		||||
            val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
 | 
			
		||||
            val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
 | 
			
		||||
            asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
 | 
			
		||||
            precheckFromToWord(iterableDt, stepsize, varname, endLabel)
 | 
			
		||||
            asmgen.out("  sta  $toValueVar")
 | 
			
		||||
@@ -420,6 +420,7 @@ $endLabel""")
 | 
			
		||||
        val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
 | 
			
		||||
        asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
 | 
			
		||||
        precheckFromToWord(iterableDt, stepsize, varname, endLabel)
 | 
			
		||||
        asmgen.romableError("self-modifying code (forloop over words range)", stmt.position)  // TODO fix romable; there is self-modifying code below
 | 
			
		||||
        asmgen.out("""
 | 
			
		||||
                sty  $modifiedLabel+1
 | 
			
		||||
                sta  $modifiedLabel2+1
 | 
			
		||||
@@ -442,7 +443,6 @@ $modifiedLabel  sbc  #0    ; modified
 | 
			
		||||
                eor  #$80
 | 
			
		||||
+               bpl  $loopLabel                
 | 
			
		||||
$endLabel""")
 | 
			
		||||
        asmgen.romableError("self-modifying code (forloop over words range)", stmt.position)  // TODO fix romable
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
 | 
			
		||||
@@ -500,15 +500,15 @@ $endLabel""")
 | 
			
		||||
        val endLabel = asmgen.makeLabel("for_end")
 | 
			
		||||
        asmgen.loopEndLabels.add(endLabel)
 | 
			
		||||
        val iterableName = asmgen.asmVariableName(ident)
 | 
			
		||||
        val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
 | 
			
		||||
        val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
 | 
			
		||||
            is StStaticVariable -> symbol.length!!
 | 
			
		||||
            is StMemVar -> symbol.length!!
 | 
			
		||||
            else -> 0
 | 
			
		||||
            else -> 0u
 | 
			
		||||
        }
 | 
			
		||||
        when {
 | 
			
		||||
            iterableDt.isString -> {
 | 
			
		||||
 | 
			
		||||
        fun iterateStrings() {
 | 
			
		||||
            if(asmgen.options.romable) {
 | 
			
		||||
                    val indexVar = asmgen.getTempVarName(BaseDataType.UBYTE)
 | 
			
		||||
                val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                        ldy  #0
 | 
			
		||||
                        sty  $indexVar
 | 
			
		||||
@@ -538,15 +538,19 @@ $indexVar   .byte  0
 | 
			
		||||
$endLabel""")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
            iterableDt.isByteArray || iterableDt.isBoolArray -> {
 | 
			
		||||
                val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(iterableDt.elementType().base) else asmgen.makeLabel("for_index")
 | 
			
		||||
 | 
			
		||||
        fun iterateBytes() {
 | 
			
		||||
            val indexVar = if(asmgen.options.romable)
 | 
			
		||||
                asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
 | 
			
		||||
            else
 | 
			
		||||
                asmgen.makeLabel("for_index")
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                    ldy  #0
 | 
			
		||||
$loopLabel          sty  $indexVar
 | 
			
		||||
                    lda  $iterableName,y
 | 
			
		||||
                    sta  ${asmgen.asmVariableName(stmt.variable)}""")
 | 
			
		||||
            asmgen.translate(stmt.statements)
 | 
			
		||||
                if(numElements<=255) {
 | 
			
		||||
            if(numElements<=255u) {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                        ldy  $indexVar
 | 
			
		||||
                        iny
 | 
			
		||||
@@ -562,7 +566,7 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
                        beq  $endLabel""")
 | 
			
		||||
            }
 | 
			
		||||
            if(!asmgen.options.romable) {
 | 
			
		||||
                    if(numElements>=16) {
 | 
			
		||||
                if(numElements>=16u) {
 | 
			
		||||
                    // allocate index var on ZP if possible, otherwise inline
 | 
			
		||||
                    val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
 | 
			
		||||
                    result.fold(
 | 
			
		||||
@@ -575,8 +579,12 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out(endLabel)
 | 
			
		||||
        }
 | 
			
		||||
            iterableDt.isSplitWordArray -> {
 | 
			
		||||
                val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
 | 
			
		||||
 | 
			
		||||
        fun iterateSplitWords() {
 | 
			
		||||
            val indexVar = if(asmgen.options.romable)
 | 
			
		||||
                asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
 | 
			
		||||
            else
 | 
			
		||||
                asmgen.makeLabel("for_index")
 | 
			
		||||
            val loopvarName = asmgen.asmVariableName(stmt.variable)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                    ldy  #0
 | 
			
		||||
@@ -586,7 +594,7 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
                    lda  ${iterableName}_msb,y
 | 
			
		||||
                    sta  $loopvarName+1""")
 | 
			
		||||
            asmgen.translate(stmt.statements)
 | 
			
		||||
                if(numElements<=255) {
 | 
			
		||||
            if(numElements<=255u) {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                        ldy  $indexVar
 | 
			
		||||
                        iny
 | 
			
		||||
@@ -602,7 +610,7 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
                        beq  $endLabel""")
 | 
			
		||||
            }
 | 
			
		||||
            if(!asmgen.options.romable) {
 | 
			
		||||
                    if(numElements>=16) {
 | 
			
		||||
                if(numElements>=16u) {
 | 
			
		||||
                    // allocate index var on ZP if possible, otherwise inline
 | 
			
		||||
                    val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
 | 
			
		||||
                    result.fold(
 | 
			
		||||
@@ -615,9 +623,12 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out(endLabel)
 | 
			
		||||
        }
 | 
			
		||||
            iterableDt.isWordArray -> {
 | 
			
		||||
                val length = numElements * 2
 | 
			
		||||
                val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
 | 
			
		||||
 | 
			
		||||
        fun iterateWords(actuallyLongs: Boolean = false) {
 | 
			
		||||
            val indexVar = if(asmgen.options.romable)
 | 
			
		||||
                asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
 | 
			
		||||
            else
 | 
			
		||||
                asmgen.makeLabel("for_index")
 | 
			
		||||
            val loopvarName = asmgen.asmVariableName(stmt.variable)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                    ldy  #0
 | 
			
		||||
@@ -626,17 +637,48 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
                    sta  $loopvarName
 | 
			
		||||
                    lda  $iterableName+1,y
 | 
			
		||||
                    sta  $loopvarName+1""")
 | 
			
		||||
            if(actuallyLongs) {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $iterableName+2,y
 | 
			
		||||
                    sta  $loopvarName+2
 | 
			
		||||
                    lda  $iterableName+3,y
 | 
			
		||||
                    sta  $loopvarName+3""")
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.translate(stmt.statements)
 | 
			
		||||
                if(length<=127) {
 | 
			
		||||
            if(numElements<=127u) {
 | 
			
		||||
                if(actuallyLongs) {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        ldy  $indexVar
 | 
			
		||||
                        iny
 | 
			
		||||
                        iny
 | 
			
		||||
                        cpy  #$length
 | 
			
		||||
                        iny
 | 
			
		||||
                        iny
 | 
			
		||||
                        cpy  #${numElements*4u}
 | 
			
		||||
                        beq  $endLabel
 | 
			
		||||
                        bne  $loopLabel""")
 | 
			
		||||
                } else {
 | 
			
		||||
                    // length is 128 words, 256 bytes
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        ldy  $indexVar
 | 
			
		||||
                        iny
 | 
			
		||||
                        iny
 | 
			
		||||
                        cpy  #${numElements*2u}
 | 
			
		||||
                        beq  $endLabel
 | 
			
		||||
                        bne  $loopLabel""")
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if(actuallyLongs) {
 | 
			
		||||
                    // array size is 64 longs, 256 bytes
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        ldy  $indexVar
 | 
			
		||||
                        iny
 | 
			
		||||
                        iny
 | 
			
		||||
                        iny
 | 
			
		||||
                        iny
 | 
			
		||||
                        bne  $loopLabel
 | 
			
		||||
                        beq  $endLabel""")
 | 
			
		||||
 | 
			
		||||
                } else {
 | 
			
		||||
                    // array size is 128 words, 256 bytes
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        ldy  $indexVar
 | 
			
		||||
                        iny
 | 
			
		||||
@@ -644,8 +686,9 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
                        bne  $loopLabel
 | 
			
		||||
                        beq  $endLabel""")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if(!asmgen.options.romable) {
 | 
			
		||||
                    if(length>=16) {
 | 
			
		||||
                if(numElements>=16u) {
 | 
			
		||||
                    // allocate index var on ZP if possible, otherwise inline
 | 
			
		||||
                    val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
 | 
			
		||||
                    result.fold(
 | 
			
		||||
@@ -658,9 +701,14 @@ $loopLabel          sty  $indexVar
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out(endLabel)
 | 
			
		||||
        }
 | 
			
		||||
            iterableDt.isFloatArray -> {
 | 
			
		||||
                throw AssemblyError("for loop with floating point variables is not supported")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        when {
 | 
			
		||||
            iterableDt.isString -> iterateStrings()
 | 
			
		||||
            iterableDt.isByteArray || iterableDt.isBoolArray -> iterateBytes()
 | 
			
		||||
            iterableDt.isSplitWordArray -> iterateSplitWords()
 | 
			
		||||
            iterableDt.isWordArray -> iterateWords()
 | 
			
		||||
            iterableDt.isLongArray -> iterateWords(true)
 | 
			
		||||
            iterableDt.isFloatArray -> throw AssemblyError("for loop with floating point variables is not supported")
 | 
			
		||||
            else -> throw AssemblyError("can't iterate over $iterableDt")
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.loopEndLabels.removeLast()
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
 | 
			
		||||
        // we consider them NOT to be optimized into (possibly different) CPU registers.
 | 
			
		||||
        // Just load them in whatever the register spec says.
 | 
			
		||||
        return when (params.size) {
 | 
			
		||||
            1 -> params[0].type.isIntegerOrBool && params[0].register == null
 | 
			
		||||
            1 -> params[0].register == null && (params[0].type.isWordOrByteOrBool || params[0].type.isPointer)
 | 
			
		||||
            2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
 | 
			
		||||
            else -> false
 | 
			
		||||
        }
 | 
			
		||||
@@ -36,7 +36,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
 | 
			
		||||
        val symbol = asmgen.symbolTable.lookup(call.name)!!
 | 
			
		||||
        if(symbol.type == StNodeType.LABEL) {
 | 
			
		||||
            require(call.void)
 | 
			
		||||
            asmgen.out("  jsr  ${asmgen.asmSymbolName(symbol.scopedName)}")
 | 
			
		||||
            asmgen.out("  jsr  ${asmgen.asmSymbolName(symbol.scopedNameString)}")
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -139,15 +139,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if(sub is PtSub) {
 | 
			
		||||
            if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
 | 
			
		||||
            val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
 | 
			
		||||
            if(optimizeIntArgsViaCpuRegisters(parameters)) {
 | 
			
		||||
                // Note that if the args fit into cpu registers, we don't concern ourselves here
 | 
			
		||||
                // if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
 | 
			
		||||
                // That is now up to the subroutine itself.
 | 
			
		||||
                useCpuRegistersForArgs(call.args, sub)
 | 
			
		||||
            } else {
 | 
			
		||||
                // arguments via variables
 | 
			
		||||
                val paramValues = sub.parameters.zip(call.args)
 | 
			
		||||
                val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
 | 
			
		||||
                val paramValues = parameters.zip(call.args)
 | 
			
		||||
                val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
 | 
			
		||||
                if (normalParams.isNotEmpty()) {
 | 
			
		||||
                    for (p in normalParams)
 | 
			
		||||
                        argumentViaVariable(sub, p.first, p.second)
 | 
			
		||||
@@ -166,7 +167,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
 | 
			
		||||
        val params = sub.parameters
 | 
			
		||||
        val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
 | 
			
		||||
        when(params.size) {
 | 
			
		||||
            1 -> {
 | 
			
		||||
                val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
 | 
			
		||||
@@ -205,8 +206,11 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
 | 
			
		||||
            is PtBuiltinFunctionCall -> {
 | 
			
		||||
                if (arg.name in arrayOf("lsb", "msb", "lsw", "msw"))
 | 
			
		||||
                    return usesOtherRegistersWhileEvaluating(arg.args[0])
 | 
			
		||||
                if (arg.name == "mkword")
 | 
			
		||||
                if (arg.name == "mkword" || arg.name == "mklong2")
 | 
			
		||||
                    return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
 | 
			
		||||
                if (arg.name == "mklong")
 | 
			
		||||
                    return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1]) ||
 | 
			
		||||
                           usesOtherRegistersWhileEvaluating(arg.args[2]) || usesOtherRegistersWhileEvaluating(arg.args[3])
 | 
			
		||||
                return !arg.isSimple()
 | 
			
		||||
            }
 | 
			
		||||
            is PtAddressOf -> false
 | 
			
		||||
@@ -332,14 +336,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
 | 
			
		||||
            } else {
 | 
			
		||||
                val scope = value.definingISub()
 | 
			
		||||
                val target: AsmAssignTarget =
 | 
			
		||||
                    if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
 | 
			
		||||
                    if(parameter.value.type.isByte && register.isWord())
 | 
			
		||||
                        AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
 | 
			
		||||
                    else {
 | 
			
		||||
                        AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
 | 
			
		||||
                    }
 | 
			
		||||
                val src = if(value.type.isPassByRef) {
 | 
			
		||||
                    if(value is PtIdentifier) {
 | 
			
		||||
                        val addr = PtAddressOf(Position.DUMMY)
 | 
			
		||||
                        val addr = PtAddressOf(value.type.typeForAddressOf(false), false, Position.DUMMY)
 | 
			
		||||
                        addr.add(value)
 | 
			
		||||
                        addr.parent = scope as PtNode
 | 
			
		||||
                        AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,13 @@ import prog8.code.ast.*
 | 
			
		||||
import prog8.code.core.*
 | 
			
		||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
 | 
			
		||||
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
 | 
			
		||||
import prog8.codegen.cpu6502.assignment.PointerAssignmentsGen
 | 
			
		||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
 | 
			
		||||
 | 
			
		||||
internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                            private val st: SymbolTable,
 | 
			
		||||
                            private val asmgen: AsmGen6502Internal,
 | 
			
		||||
                            private val pointergen: PointerAssignmentsGen,
 | 
			
		||||
                            private val assignmentAsmGen: AssignmentAsmGen,
 | 
			
		||||
                            private val errors: IErrorReporter) {
 | 
			
		||||
 | 
			
		||||
@@ -38,13 +40,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                // use a BIT instruction to test for bit 7 or 6 set/clear
 | 
			
		||||
                val (testBitSet, variable, bitmask) = useBIT
 | 
			
		||||
                return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val rightDt = compareCond.right.type
 | 
			
		||||
            return when {
 | 
			
		||||
                rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
 | 
			
		||||
                rightDt.isWord -> translateIfWord(stmt, compareCond, jumpAfterIf)
 | 
			
		||||
                rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
 | 
			
		||||
                rightDt.isLong -> translateIfLong(stmt, compareCond, jumpAfterIf)
 | 
			
		||||
                rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
 | 
			
		||||
                else -> throw AssemblyError("weird dt")
 | 
			
		||||
            }
 | 
			
		||||
@@ -64,6 +66,16 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val dereference = stmt.condition as? PtPointerDeref
 | 
			
		||||
        if(dereference!=null) {
 | 
			
		||||
            val (zpPtrVar, offset) = pointergen.deref(dereference)
 | 
			
		||||
            asmgen.loadIndirectByte(zpPtrVar, offset)
 | 
			
		||||
            return if (jumpAfterIf != null)
 | 
			
		||||
                translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("beq", stmt)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -87,6 +99,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                if(testForBitSet) {
 | 
			
		||||
                    if(jumpAfterIf!=null) {
 | 
			
		||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        branch("bmi", target)
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@@ -94,6 +107,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                } else {
 | 
			
		||||
                    if(jumpAfterIf!=null) {
 | 
			
		||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        branch("bpl", target)
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@@ -107,6 +121,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                if(testForBitSet) {
 | 
			
		||||
                    if(jumpAfterIf!=null) {
 | 
			
		||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        branch("bvs", target)
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@@ -114,6 +129,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                } else {
 | 
			
		||||
                    if(jumpAfterIf!=null) {
 | 
			
		||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        branch("bvc", target)
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@@ -146,6 +162,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
            translateIfElseBodies("beq", ifElse)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateIfElseBodiesSignedByte(elseConditional: String, value: PtExpression, stmt: PtIfElse) {
 | 
			
		||||
        fun branchElse(label: String) {
 | 
			
		||||
            when (elseConditional) {
 | 
			
		||||
                "<" -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        bvc  +
 | 
			
		||||
                        eor  #$80
 | 
			
		||||
+                       bpl  $label""")
 | 
			
		||||
                }
 | 
			
		||||
                ">=" -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        bvc  +
 | 
			
		||||
                        eor  #$80
 | 
			
		||||
+                       bmi  $label""")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("wrong conditional $elseConditional")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        val afterIfLabel = asmgen.makeLabel("afterif")
 | 
			
		||||
        asmgen.cmpAwithByteValue(value, true)
 | 
			
		||||
        if(stmt.hasElse()) {
 | 
			
		||||
            // if and else blocks
 | 
			
		||||
            val elseLabel = asmgen.makeLabel("else")
 | 
			
		||||
            branchElse(elseLabel)
 | 
			
		||||
            asmgen.translate(stmt.ifScope)
 | 
			
		||||
            asmgen.jmp(afterIfLabel)
 | 
			
		||||
            asmgen.out(elseLabel)
 | 
			
		||||
            asmgen.translate(stmt.elseScope)
 | 
			
		||||
        } else {
 | 
			
		||||
            // no else block
 | 
			
		||||
            branchElse(afterIfLabel)
 | 
			
		||||
            asmgen.translate(stmt.ifScope)
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.out(afterIfLabel)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateJumpElseBodiesSignedByte(elseConditional: String, value: PtExpression, jump: PtJump, elseBlock: PtNodeGroup) {
 | 
			
		||||
        fun branchTarget(label: String) {
 | 
			
		||||
            when (elseConditional) {
 | 
			
		||||
                "<" -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        bvc  +
 | 
			
		||||
                        eor  #$80
 | 
			
		||||
+                       bmi  $label""")
 | 
			
		||||
                }
 | 
			
		||||
                ">=" -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        bvc  +
 | 
			
		||||
                        eor  #$80
 | 
			
		||||
+                       bpl  $label""")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("wrong conditional $elseConditional")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fun branchElse(label: String) {
 | 
			
		||||
            when (elseConditional) {
 | 
			
		||||
                "<" -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        bvc  +
 | 
			
		||||
                        eor  #$80
 | 
			
		||||
+                       bpl  $label""")
 | 
			
		||||
                }
 | 
			
		||||
                ">=" -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        bvc  +
 | 
			
		||||
                        eor  #$80
 | 
			
		||||
+                       bmi  $label""")
 | 
			
		||||
                }
 | 
			
		||||
                else -> throw AssemblyError("wrong conditional $elseConditional")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var target = asmgen.getJumpTarget(jump, false)
 | 
			
		||||
        asmgen.cmpAwithByteValue(value, true)
 | 
			
		||||
        if(target.indirect) {
 | 
			
		||||
            branchElse("+")
 | 
			
		||||
            if(target.needsExpressionEvaluation)
 | 
			
		||||
                target = asmgen.getJumpTarget(jump)
 | 
			
		||||
            asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
 | 
			
		||||
            asmgen.out("+")
 | 
			
		||||
        } else {
 | 
			
		||||
            require(!target.needsExpressionEvaluation)
 | 
			
		||||
            branchTarget(target.asmLabel)
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.translate(elseBlock)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
 | 
			
		||||
        // comparison value is already in A
 | 
			
		||||
        val afterIfLabel = asmgen.makeLabel("afterif")
 | 
			
		||||
@@ -172,9 +275,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
            asmgen.out("  $falseBranch  +")
 | 
			
		||||
            if(target.needsExpressionEvaluation)
 | 
			
		||||
                target = asmgen.getJumpTarget(jump)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
            asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
 | 
			
		||||
            asmgen.out("+")
 | 
			
		||||
        } else {
 | 
			
		||||
            require(!target.needsExpressionEvaluation)
 | 
			
		||||
            asmgen.out("  $branchInstr  ${target.asmLabel}")
 | 
			
		||||
@@ -210,38 +312,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                    translateIfElseBodies("beq", stmt)
 | 
			
		||||
            }
 | 
			
		||||
            "<" -> translateByteLess(stmt, signed, jumpAfterIf)
 | 
			
		||||
            "<=" -> {
 | 
			
		||||
                // X<=Y -> Y>=X (reverse of >=)
 | 
			
		||||
                asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
 | 
			
		||||
                asmgen.cmpAwithByteValue(condition.left, false)
 | 
			
		||||
                return if(signed) {
 | 
			
		||||
                    if(jumpAfterIf!=null)
 | 
			
		||||
                        translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
                    else
 | 
			
		||||
                        translateIfElseBodies("bmi", stmt)
 | 
			
		||||
                } else {
 | 
			
		||||
                    if(jumpAfterIf!=null)
 | 
			
		||||
                        translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
                    else
 | 
			
		||||
                        translateIfElseBodies("bcc", stmt)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            "<=" -> translateByteLessEqual(stmt, signed, jumpAfterIf)
 | 
			
		||||
            ">" -> translateByteGreater(stmt, signed, jumpAfterIf)
 | 
			
		||||
            ">=" -> {
 | 
			
		||||
                asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
 | 
			
		||||
                asmgen.cmpAwithByteValue(condition.right, false)
 | 
			
		||||
                return if(signed) {
 | 
			
		||||
                    if(jumpAfterIf!=null)
 | 
			
		||||
                        translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
                    else
 | 
			
		||||
                        translateIfElseBodies("bmi", stmt)
 | 
			
		||||
                } else {
 | 
			
		||||
                    if(jumpAfterIf!=null)
 | 
			
		||||
                        translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
                    else
 | 
			
		||||
                        translateIfElseBodies("bcc", stmt)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ">=" -> translateByteGreaterEqual(stmt, signed, jumpAfterIf)
 | 
			
		||||
            in LogicalOperators -> {
 | 
			
		||||
                val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
 | 
			
		||||
                if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
 | 
			
		||||
@@ -287,6 +360,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                            asmgen.out("  bmi  + |  beq +")
 | 
			
		||||
                            if(target.needsExpressionEvaluation)
 | 
			
		||||
                                target = asmgen.getJumpTarget(jumpAfterIf)
 | 
			
		||||
                            require(!target.indexedX)
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -354,6 +428,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                            asmgen.out("  bmi  + |  bne  ++")
 | 
			
		||||
                            if(target.needsExpressionEvaluation)
 | 
			
		||||
                                target = asmgen.getJumpTarget(jumpAfterIf)
 | 
			
		||||
                            require(!target.indexedX)
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
+                               jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -402,13 +477,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
    private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
 | 
			
		||||
        val condition = stmt.condition as PtBinaryExpression
 | 
			
		||||
        asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
 | 
			
		||||
        asmgen.cmpAwithByteValue(condition.right, false)
 | 
			
		||||
        if(signed) {
 | 
			
		||||
            if(jumpAfterIf!=null)
 | 
			
		||||
                translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
                translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bpl", stmt)
 | 
			
		||||
                translateIfElseBodiesSignedByte("<", condition.right, stmt)
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.cmpAwithByteValue(condition.right, false)
 | 
			
		||||
            if(jumpAfterIf!=null)
 | 
			
		||||
                translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
@@ -416,16 +491,33 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateByteLessEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
 | 
			
		||||
        // X<=Y -> Y>=X (reverse of >=)
 | 
			
		||||
        val condition = stmt.condition as PtBinaryExpression
 | 
			
		||||
        asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
 | 
			
		||||
        return if(signed) {
 | 
			
		||||
            if(jumpAfterIf!=null)
 | 
			
		||||
                translateJumpElseBodiesSignedByte(">=", condition.left, jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodiesSignedByte(">=", condition.left, stmt)
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.cmpAwithByteValue(condition.left, false)
 | 
			
		||||
            if(jumpAfterIf!=null)
 | 
			
		||||
                translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bcc", stmt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
 | 
			
		||||
        val condition = stmt.condition as PtBinaryExpression
 | 
			
		||||
        if(signed) {
 | 
			
		||||
            // X>Y --> Y<X
 | 
			
		||||
            asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
 | 
			
		||||
            asmgen.cmpAwithByteValue(condition.left, true)
 | 
			
		||||
            if (jumpAfterIf != null)
 | 
			
		||||
                translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
                translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bpl", stmt)
 | 
			
		||||
                translateIfElseBodiesSignedByte("<", condition.left, stmt)
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
 | 
			
		||||
            asmgen.cmpAwithByteValue(condition.right, false)
 | 
			
		||||
@@ -435,6 +527,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                    asmgen.out("  bcc  + |  beq  +")
 | 
			
		||||
                    if(target.needsExpressionEvaluation)
 | 
			
		||||
                        target = asmgen.getJumpTarget(jumpAfterIf)
 | 
			
		||||
                    require(!target.indexedX)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -466,6 +559,46 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateByteGreaterEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
 | 
			
		||||
        val condition = stmt.condition as PtBinaryExpression
 | 
			
		||||
        asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
 | 
			
		||||
        return if(signed) {
 | 
			
		||||
            if(jumpAfterIf!=null)
 | 
			
		||||
                translateJumpElseBodiesSignedByte(">=", condition.right, jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodiesSignedByte(">=", condition.right, stmt)
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.cmpAwithByteValue(condition.right, false)
 | 
			
		||||
            if(jumpAfterIf!=null)
 | 
			
		||||
                translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bcc", stmt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateIfLong(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
 | 
			
		||||
        val constValue = condition.right.asConstInteger()
 | 
			
		||||
        if(constValue==0) {
 | 
			
		||||
            // optimized comparisons with zero
 | 
			
		||||
            return when (condition.operator) {
 | 
			
		||||
                "==" -> longEqualsZero(condition.left, false, jumpAfterIf, stmt)
 | 
			
		||||
                "!=" -> longEqualsZero(condition.left, true, jumpAfterIf, stmt)
 | 
			
		||||
                "<" -> longLessZero(condition.left, false, jumpAfterIf, stmt)
 | 
			
		||||
                "<=" -> longLessZero(condition.left, true, jumpAfterIf, stmt)
 | 
			
		||||
                ">" -> longGreaterZero(condition.left, false, jumpAfterIf, stmt)
 | 
			
		||||
                ">=" -> longGreaterZero(condition.left, true, jumpAfterIf, stmt)
 | 
			
		||||
                else -> throw AssemblyError("expected comparison operator")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return when (condition.operator) {
 | 
			
		||||
            "==" -> longEqualsValue(condition.left, condition.right, false, jumpAfterIf, stmt)
 | 
			
		||||
            "!=" -> longEqualsValue(condition.left, condition.right, true, jumpAfterIf, stmt)
 | 
			
		||||
            "<", "<=", ">", ">=" -> compareLongValues(condition.left, condition.operator, condition.right, jumpAfterIf, stmt)
 | 
			
		||||
            else -> throw AssemblyError("expected comparison operator")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
 | 
			
		||||
        val signed = condition.left.type.isSigned
 | 
			
		||||
        val constValue = condition.right.asConstInteger()
 | 
			
		||||
@@ -497,7 +630,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
    private fun wordGreaterEqualsZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
 | 
			
		||||
        // special case for word>=0
 | 
			
		||||
        if(signed) {
 | 
			
		||||
            loadAndCmp0MSB(value)
 | 
			
		||||
            loadAndCmp0MSB(value, false)
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
@@ -510,7 +643,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
    private fun wordLessZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
 | 
			
		||||
        // special case for word<0
 | 
			
		||||
        if(signed) {
 | 
			
		||||
            loadAndCmp0MSB(value)
 | 
			
		||||
            loadAndCmp0MSB(value, false)
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
@@ -536,6 +669,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
+                           bpl  +""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -591,6 +725,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
			
		||||
                            bcs  +""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
_jump                       jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -668,6 +803,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
+                           bpl  +""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                           jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -722,6 +858,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                            bcc  +""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -781,33 +918,44 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun loadAndCmp0MSB(value: PtExpression) {
 | 
			
		||||
    private fun loadAndCmp0MSB(value: PtExpression, long: Boolean) {
 | 
			
		||||
        when(value) {
 | 
			
		||||
            is PtArrayIndexer -> {
 | 
			
		||||
                val varname = asmgen.asmVariableName(value.variable)
 | 
			
		||||
                if(value.variable==null)
 | 
			
		||||
                    TODO("support for ptr indexing ${value.position}")
 | 
			
		||||
                val varname = asmgen.asmVariableName(value.variable!!)
 | 
			
		||||
                asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
 | 
			
		||||
                if(value.splitWords)
 | 
			
		||||
                if(value.splitWords) {
 | 
			
		||||
                    require(!long)
 | 
			
		||||
                    asmgen.out("  lda  ${varname}_msb,y")
 | 
			
		||||
                }
 | 
			
		||||
                else if(long)
 | 
			
		||||
                    asmgen.out("  lda  $varname+3,y")
 | 
			
		||||
                else
 | 
			
		||||
                    asmgen.out("  lda  $varname+1,y")
 | 
			
		||||
            }
 | 
			
		||||
            is PtIdentifier -> {
 | 
			
		||||
                val varname = asmgen.asmVariableName(value)
 | 
			
		||||
                if(long)
 | 
			
		||||
                    asmgen.out("  lda  $varname+3")
 | 
			
		||||
                else
 | 
			
		||||
                    asmgen.out("  lda  $varname+1")
 | 
			
		||||
            }
 | 
			
		||||
            is PtAddressOf -> {
 | 
			
		||||
                require(!long)
 | 
			
		||||
                if(value.isFromArrayElement) {
 | 
			
		||||
                    asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
 | 
			
		||||
                    asmgen.out("  cpy  #0")
 | 
			
		||||
                } else {
 | 
			
		||||
                    var varname = asmgen.asmVariableName(value.identifier)
 | 
			
		||||
                    if(value.identifier.type.isSplitWordArray) {
 | 
			
		||||
                    var varname = asmgen.asmVariableName(value.identifier!!)
 | 
			
		||||
                    if(value.identifier!!.type.isSplitWordArray) {
 | 
			
		||||
                        varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
 | 
			
		||||
                    }
 | 
			
		||||
                    asmgen.out("  lda  #>$varname")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                require(!long)
 | 
			
		||||
                asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
 | 
			
		||||
                asmgen.out("  cpy  #0")
 | 
			
		||||
            }
 | 
			
		||||
@@ -830,6 +978,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                            bne  ++""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
+                           jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -878,8 +1027,10 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
            if(value is PtIdentifier)
 | 
			
		||||
                return compareLsbMsb(value.name, value.name+"+1")
 | 
			
		||||
            if(value is PtArrayIndexer) {
 | 
			
		||||
                if(value.variable==null)
 | 
			
		||||
                    TODO("support for ptr indexing ${value.position}")
 | 
			
		||||
                val constIndex = value.index.asConstInteger()
 | 
			
		||||
                val varname = asmgen.asmVariableName(value.variable)
 | 
			
		||||
                val varname = asmgen.asmVariableName(value.variable!!)
 | 
			
		||||
                if(constIndex!=null) {
 | 
			
		||||
                    if(value.splitWords) {
 | 
			
		||||
                        return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
 | 
			
		||||
@@ -909,6 +1060,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                        bne  ++""")
 | 
			
		||||
                    if(target.needsExpressionEvaluation)
 | 
			
		||||
                        target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                    require(!target.indexedX)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
+                       jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -974,6 +1126,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                            beq  ++""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
+                           jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1023,8 +1176,10 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
            if(value is PtIdentifier)
 | 
			
		||||
                return compareLsbMsb(value.name, value.name+"+1")
 | 
			
		||||
            if(value is PtArrayIndexer) {
 | 
			
		||||
                if(value.variable==null)
 | 
			
		||||
                    TODO("support for ptr indexing ${value.position}")
 | 
			
		||||
                val constIndex = value.index.asConstInteger()
 | 
			
		||||
                val varname = asmgen.asmVariableName(value.variable)
 | 
			
		||||
                val varname = asmgen.asmVariableName(value.variable!!)
 | 
			
		||||
                if(constIndex!=null) {
 | 
			
		||||
                    if(value.splitWords) {
 | 
			
		||||
                        return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
 | 
			
		||||
@@ -1054,6 +1209,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                        beq  ++""")
 | 
			
		||||
                    if(target.needsExpressionEvaluation)
 | 
			
		||||
                        target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                    require(!target.indexedX)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
+                       jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1103,6 +1259,63 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun longEqualsZero(value: PtExpression, notEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
 | 
			
		||||
        if(value is PtIdentifier) {
 | 
			
		||||
            val varname = asmgen.asmVariableName(value)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  $varname
 | 
			
		||||
                ora  $varname+1
 | 
			
		||||
                ora  $varname+2
 | 
			
		||||
                ora  $varname+3""")
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
 | 
			
		||||
            asmgen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  cx16.r14
 | 
			
		||||
                ora  cx16.r14+1
 | 
			
		||||
                ora  cx16.r14+2
 | 
			
		||||
                ora  cx16.r14+3
 | 
			
		||||
                sta  P8ZP_SCRATCH_REG""")
 | 
			
		||||
            asmgen.popLongRegisters(RegisterOrPair.R14R15_32, 1)
 | 
			
		||||
            asmgen.out("  lda  P8ZP_SCRATCH_REG  ; restore flags")
 | 
			
		||||
        }
 | 
			
		||||
        if(notEquals) {
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("bne", "beq", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("beq", stmt)
 | 
			
		||||
        } else {
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("beq", "bne", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bne", stmt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
 | 
			
		||||
        if(lessEquals) {
 | 
			
		||||
            TODO("long <= 0 ${value.position}")
 | 
			
		||||
        } else {
 | 
			
		||||
            loadAndCmp0MSB(value, true)
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bpl", stmt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
 | 
			
		||||
        if(lessEquals) {
 | 
			
		||||
            TODO("long >= 0 ${value.position}")
 | 
			
		||||
        } else {
 | 
			
		||||
            loadAndCmp0MSB(value, true)
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bmi", stmt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun wordEqualsZero(value: PtExpression, notEquals: Boolean, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
 | 
			
		||||
 | 
			
		||||
        // special case for (u)word == 0
 | 
			
		||||
@@ -1136,7 +1349,9 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                is PtArrayIndexer -> {
 | 
			
		||||
                    val constIndex = value.index.asConstInteger()
 | 
			
		||||
                    if(constIndex!=null) {
 | 
			
		||||
                        val varName = asmgen.asmVariableName(value.variable)
 | 
			
		||||
                        if(value.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${value.position}")
 | 
			
		||||
                        val varName = asmgen.asmVariableName(value.variable!!)
 | 
			
		||||
                        if(value.splitWords) {
 | 
			
		||||
                            return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
 | 
			
		||||
                        }
 | 
			
		||||
@@ -1157,7 +1372,9 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                is PtArrayIndexer -> {
 | 
			
		||||
                    val constIndex = value.index.asConstInteger()
 | 
			
		||||
                    if (constIndex != null) {
 | 
			
		||||
                        val varName = asmgen.asmVariableName(value.variable)
 | 
			
		||||
                        if(value.variable==null)
 | 
			
		||||
                            TODO("support for ptr indexing ${value.position}")
 | 
			
		||||
                        val varName = asmgen.asmVariableName(value.variable!!)
 | 
			
		||||
                        if(value.splitWords) {
 | 
			
		||||
                            return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
 | 
			
		||||
                        }
 | 
			
		||||
@@ -1176,6 +1393,140 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun longEqualsValue(
 | 
			
		||||
        left: PtExpression,
 | 
			
		||||
        right: PtExpression,
 | 
			
		||||
        notEquals: Boolean,
 | 
			
		||||
        jump: PtJump?,
 | 
			
		||||
        stmt: PtIfElse
 | 
			
		||||
    ) {
 | 
			
		||||
        val constRight = right.asConstInteger()
 | 
			
		||||
        val variableRight = (right as? PtIdentifier)?.name
 | 
			
		||||
 | 
			
		||||
        if(left is PtIdentifier) {
 | 
			
		||||
            val leftvar = asmgen.asmVariableName(left)
 | 
			
		||||
            if(constRight!=null) {
 | 
			
		||||
                val hex = constRight.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $leftvar
 | 
			
		||||
                    cmp  #$${hex.substring(6,8)}
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  $leftvar+1
 | 
			
		||||
                    cmp  #$${hex.substring(4, 6)}
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  $leftvar+2
 | 
			
		||||
                    cmp  #$${hex.substring(2, 4)}
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  $leftvar+3
 | 
			
		||||
                    cmp  #$${hex.take(2)}
 | 
			
		||||
+""")
 | 
			
		||||
            } else if(variableRight!=null) {
 | 
			
		||||
                require(right.type.isLong)
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  #<$leftvar
 | 
			
		||||
                    ldy  #>$leftvar
 | 
			
		||||
                    sta  P8ZP_SCRATCH_W1
 | 
			
		||||
                    sty  P8ZP_SCRATCH_W1+1
 | 
			
		||||
                    lda  #<$variableRight
 | 
			
		||||
                    ldy  #>$variableRight
 | 
			
		||||
                    jsr  prog8_lib.long_not_equals""")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
 | 
			
		||||
            asmgen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
 | 
			
		||||
            if(constRight!=null) {
 | 
			
		||||
                val hex = constRight.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  cx16.r14
 | 
			
		||||
                    cmp  #$${hex.substring(6,8)}
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  cx16.r14+1
 | 
			
		||||
                    cmp  #$${hex.substring(4, 6)}
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  cx16.r14+2
 | 
			
		||||
                    cmp  #$${hex.substring(2, 4)}
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  cx16.r14+3
 | 
			
		||||
                    cmp  #$${hex.take(2)}
 | 
			
		||||
+""")
 | 
			
		||||
            } else if(variableRight!=null) {
 | 
			
		||||
                require(right.type.isLong)
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  cx16.r14
 | 
			
		||||
                    cmp  $variableRight
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  cx16.r14+1
 | 
			
		||||
                    cmp  $variableRight+1
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  cx16.r14+2
 | 
			
		||||
                    cmp  $variableRight+2
 | 
			
		||||
                    bne  +
 | 
			
		||||
                    lda  cx16.r14+3
 | 
			
		||||
                    cmp  $variableRight+3
 | 
			
		||||
+""")
 | 
			
		||||
            } else {
 | 
			
		||||
                TODO("long == value expression ${right.position}")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(notEquals) {
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("bne", "beq", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("beq", stmt)
 | 
			
		||||
        } else {
 | 
			
		||||
            if (jump != null)
 | 
			
		||||
                translateJumpElseBodies("beq", "bne", jump, stmt.elseScope)
 | 
			
		||||
            else
 | 
			
		||||
                translateIfElseBodies("bne", stmt)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun compareLongValues(
 | 
			
		||||
        left: PtExpression,
 | 
			
		||||
        operator: String,
 | 
			
		||||
        right: PtExpression,
 | 
			
		||||
        jump: PtJump?,
 | 
			
		||||
        stmt: PtIfElse
 | 
			
		||||
    ) {
 | 
			
		||||
        // this comparison is not part of an expression but part of an if statement, there's no need to save the previous values of the temp registers
 | 
			
		||||
        if(operator=="<" || operator ==">=") {
 | 
			
		||||
            assignmentAsmGen.assignExpressionToRegister(right, RegisterOrPair.R14R15_32, left.type.isSigned)
 | 
			
		||||
            assignmentAsmGen.assignExpressionToRegister(left, RegisterOrPair.R12R13_32, left.type.isSigned)
 | 
			
		||||
        } else {
 | 
			
		||||
            // flip operands
 | 
			
		||||
            assignmentAsmGen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
 | 
			
		||||
            assignmentAsmGen.assignExpressionToRegister(right, RegisterOrPair.R12R13_32, left.type.isSigned)
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.out("""
 | 
			
		||||
            sec
 | 
			
		||||
            lda  cx16.r12
 | 
			
		||||
            sbc  cx16.r14
 | 
			
		||||
            lda  cx16.r12+1
 | 
			
		||||
            sbc  cx16.r14+1
 | 
			
		||||
            lda  cx16.r12+2
 | 
			
		||||
            sbc  cx16.r14+2
 | 
			
		||||
            lda  cx16.r12+3
 | 
			
		||||
            sbc  cx16.r14+3""")
 | 
			
		||||
        when(operator) {
 | 
			
		||||
            "<", ">" -> {
 | 
			
		||||
                if (jump != null)
 | 
			
		||||
                    translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
 | 
			
		||||
                else
 | 
			
		||||
                    translateIfElseBodies("bpl", stmt)
 | 
			
		||||
            }
 | 
			
		||||
            ">=", "<=" -> {
 | 
			
		||||
                if (jump != null)
 | 
			
		||||
                    translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
 | 
			
		||||
                else
 | 
			
		||||
                    translateIfElseBodies("bmi", stmt)
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw RuntimeException("invalid operator $operator")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun wordEqualsValue(
 | 
			
		||||
        left: PtExpression,
 | 
			
		||||
        right: PtExpression,
 | 
			
		||||
@@ -1197,6 +1548,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                        beq  ++""")
 | 
			
		||||
                    if(target.needsExpressionEvaluation)
 | 
			
		||||
                        target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                    require(!target.indexedX)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
+                       jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1249,6 +1601,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                        bne  +""")
 | 
			
		||||
                    if(target.needsExpressionEvaluation)
 | 
			
		||||
                        target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                    require(!target.indexedX)
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1303,6 +1656,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                            beq  ++""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
+                           jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1362,6 +1716,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                            bne  +""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1424,6 +1779,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                            beq  ++""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
+                           jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1482,6 +1838,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                            bne  +""")
 | 
			
		||||
                        if(target.needsExpressionEvaluation)
 | 
			
		||||
                            target = asmgen.getJumpTarget(jump)
 | 
			
		||||
                        require(!target.indexedX)
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            jmp  (${target.asmLabel})
 | 
			
		||||
+""")
 | 
			
		||||
@@ -1532,8 +1889,10 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
        fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) {
 | 
			
		||||
            val constIndex = left.index.asConstInteger()
 | 
			
		||||
            if(constIndex!=null) {
 | 
			
		||||
                if(left.variable==null)
 | 
			
		||||
                    TODO("support for ptr indexing ${left.position}")
 | 
			
		||||
                asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
 | 
			
		||||
                val varName = asmgen.asmVariableName(left.variable)
 | 
			
		||||
                val varName = asmgen.asmVariableName(left.variable!!)
 | 
			
		||||
                if(left.splitWords) {
 | 
			
		||||
                    return if(notEquals)
 | 
			
		||||
                        translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex")
 | 
			
		||||
@@ -1596,13 +1955,13 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is PtAddressOf -> {
 | 
			
		||||
                    if(left.isFromArrayElement)
 | 
			
		||||
                    if(left.isFromArrayElement) {
 | 
			
		||||
                        fallbackTranslateForSimpleCondition(stmt)
 | 
			
		||||
                    else {
 | 
			
		||||
                        val varname = if(left.identifier.type.isSplitWordArray) {
 | 
			
		||||
                            if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
 | 
			
		||||
                    } else {
 | 
			
		||||
                            left.identifier.name
 | 
			
		||||
                        val varname = if(left.identifier!!.type.isSplitWordArray) {
 | 
			
		||||
                            if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
 | 
			
		||||
                        } else {
 | 
			
		||||
                            left.identifier!!.name
 | 
			
		||||
                        }
 | 
			
		||||
                        asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
 | 
			
		||||
                        translateAYNotEquals("#<$varname", "#>$varname")
 | 
			
		||||
@@ -1648,13 +2007,13 @@ _jump                       jmp  (${target.asmLabel})
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is PtAddressOf -> {
 | 
			
		||||
                    if(left.isFromArrayElement)
 | 
			
		||||
                    if(left.isFromArrayElement) {
 | 
			
		||||
                        fallbackTranslateForSimpleCondition(stmt)
 | 
			
		||||
                    else {
 | 
			
		||||
                        val varname = if(left.identifier.type.isSplitWordArray) {
 | 
			
		||||
                            if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
 | 
			
		||||
                    } else {
 | 
			
		||||
                            left.identifier.name
 | 
			
		||||
                        val varname = if(left.identifier!!.type.isSplitWordArray) {
 | 
			
		||||
                            if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
 | 
			
		||||
                        } else {
 | 
			
		||||
                            left.identifier!!.name
 | 
			
		||||
                        }
 | 
			
		||||
                        asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
 | 
			
		||||
                        translateAYEquals("#<$varname", "#>$varname")
 | 
			
		||||
 
 | 
			
		||||
@@ -9,34 +9,94 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
 | 
			
		||||
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
 | 
			
		||||
 | 
			
		||||
    internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
 | 
			
		||||
        require(target.datatype==expr.type || target.datatype.isUnsignedWord && expr.type.isString)
 | 
			
		||||
        require(target.datatype==expr.type ||
 | 
			
		||||
                target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
 | 
			
		||||
                target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
 | 
			
		||||
        val falseLabel = asmgen.makeLabel("ifexpr_false")
 | 
			
		||||
        val endLabel = asmgen.makeLabel("ifexpr_end")
 | 
			
		||||
        evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
 | 
			
		||||
        when {
 | 
			
		||||
            expr.type.isByteOrBool -> {
 | 
			
		||||
 | 
			
		||||
        if(expr.type.isByteOrBool) {
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
 | 
			
		||||
            asmgen.jmp(endLabel)
 | 
			
		||||
            asmgen.out(falseLabel)
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
 | 
			
		||||
            asmgen.out(endLabel)
 | 
			
		||||
            assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
 | 
			
		||||
            }
 | 
			
		||||
            expr.type.isWord || expr.type.isString -> {
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
 | 
			
		||||
                asmgen.jmp(endLabel)
 | 
			
		||||
                asmgen.out(falseLabel)
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
 | 
			
		||||
                asmgen.out(endLabel)
 | 
			
		||||
                assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
 | 
			
		||||
            }
 | 
			
		||||
            expr.type.isFloat -> {
 | 
			
		||||
        } else if(expr.type.isFloat) {
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
 | 
			
		||||
            asmgen.jmp(endLabel)
 | 
			
		||||
            asmgen.out(falseLabel)
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
 | 
			
		||||
            asmgen.out(endLabel)
 | 
			
		||||
            asmgen.assignRegister(RegisterOrPair.FAC1, target)
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.assignExpressionTo(expr.truevalue, target)
 | 
			
		||||
            asmgen.jmp(endLabel)
 | 
			
		||||
            asmgen.out(falseLabel)
 | 
			
		||||
            asmgen.assignExpressionTo(expr.falsevalue, target)
 | 
			
		||||
            asmgen.out(endLabel)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun assignBranchCondExpression(target: AsmAssignTarget, expr: PtBranchCondExpression) {
 | 
			
		||||
        require(target.datatype==expr.type ||
 | 
			
		||||
                target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
 | 
			
		||||
                target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
 | 
			
		||||
 | 
			
		||||
        if(target.kind==TargetStorageKind.REGISTER && target.datatype.isUnsignedByte && target.register==RegisterOrPair.A) {
 | 
			
		||||
            if(expr.condition==BranchCondition.CC) {
 | 
			
		||||
                if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
 | 
			
		||||
                    asmgen.out("  lda  #0 |  rol a")
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
 | 
			
		||||
                    asmgen.out("  lda  #0 |  rol a |  eor  #1")
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if(expr.condition==BranchCondition.CS) {
 | 
			
		||||
                if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
 | 
			
		||||
                    asmgen.out("  lda  #0 |  rol a |  eor  #1")
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
 | 
			
		||||
                    asmgen.out("  lda  #0 |  rol a")
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val trueLabel = asmgen.makeLabel("branchexpr_true")
 | 
			
		||||
        val endLabel = asmgen.makeLabel("branchexpr_end")
 | 
			
		||||
        val branch = asmgen.branchInstruction(expr.condition, false)
 | 
			
		||||
 | 
			
		||||
        asmgen.out("  $branch  $trueLabel")
 | 
			
		||||
 | 
			
		||||
        when {
 | 
			
		||||
            expr.type.isByteOrBool -> {
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
 | 
			
		||||
                asmgen.jmp(endLabel)
 | 
			
		||||
                asmgen.out(trueLabel)
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
 | 
			
		||||
                asmgen.out(endLabel)
 | 
			
		||||
                assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
 | 
			
		||||
            }
 | 
			
		||||
            expr.type.isWord || expr.type.isString -> {
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
 | 
			
		||||
                asmgen.jmp(endLabel)
 | 
			
		||||
                asmgen.out(trueLabel)
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
 | 
			
		||||
                asmgen.out(endLabel)
 | 
			
		||||
                assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
 | 
			
		||||
            }
 | 
			
		||||
            expr.type.isFloat -> {
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
 | 
			
		||||
                asmgen.jmp(endLabel)
 | 
			
		||||
                asmgen.out(trueLabel)
 | 
			
		||||
                asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
 | 
			
		||||
                asmgen.out(endLabel)
 | 
			
		||||
                asmgen.assignRegister(RegisterOrPair.FAC1, target)
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird dt")
 | 
			
		||||
        }
 | 
			
		||||
@@ -49,6 +109,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
 | 
			
		||||
                return when {
 | 
			
		||||
                    rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
 | 
			
		||||
                    rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
 | 
			
		||||
                    rightDt.isLong -> translateIfExpressionLongConditionBranch(condition, falseLabel)
 | 
			
		||||
                    rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
 | 
			
		||||
                    else -> throw AssemblyError("weird dt")
 | 
			
		||||
                }
 | 
			
		||||
@@ -135,6 +196,36 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
 | 
			
		||||
        asmgen.out("  beq  $falseLabel")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateIfExpressionLongConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
 | 
			
		||||
        // TODO can we reuse this whole thing from IfElse ?
 | 
			
		||||
        val constValue = condition.right.asConstInteger()
 | 
			
		||||
        if(constValue!=null) {
 | 
			
		||||
            if (constValue == 0) {
 | 
			
		||||
                when (condition.operator) {
 | 
			
		||||
                    "==" -> return translateLongExprIsZero(condition.left, falseLabel)
 | 
			
		||||
                    "!=" -> return translateLongExprIsNotZero(condition.left, falseLabel)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (constValue != 0) {
 | 
			
		||||
                when (condition.operator) {
 | 
			
		||||
                    "==" -> return translateLongExprEqualsNumber(condition.left, constValue, falseLabel)
 | 
			
		||||
                    "!=" -> return translateLongExprNotEqualsNumber(condition.left, constValue, falseLabel)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        val variable = condition.right as? PtIdentifier
 | 
			
		||||
        if(variable!=null) {
 | 
			
		||||
            when (condition.operator) {
 | 
			
		||||
                "==" -> return translateLongExprEqualsVariable(condition.left, variable, falseLabel)
 | 
			
		||||
                "!=" -> return translateLongExprNotEqualsVariable(condition.left, variable, falseLabel)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
 | 
			
		||||
        asmgen.assignConditionValueToRegisterAndTest(condition)
 | 
			
		||||
        asmgen.out("  beq  $falseLabel")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) {
 | 
			
		||||
        val constValue = (condition.right as? PtNumber)?.number
 | 
			
		||||
        if(constValue==0.0) {
 | 
			
		||||
@@ -274,6 +365,91 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateLongExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
 | 
			
		||||
        // if L==number
 | 
			
		||||
        // TODO reuse code from ifElse?
 | 
			
		||||
        val hex = number.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
        if(expr is PtIdentifier) {
 | 
			
		||||
            val varname = asmgen.asmVariableName(expr)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  $varname
 | 
			
		||||
                cmp  #$${hex.substring(6, 8)}
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  $varname+1
 | 
			
		||||
                cmp  #$${hex.substring(4, 6)}
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  $varname+2
 | 
			
		||||
                cmp  #$${hex.substring(2, 4)}
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  $varname+3
 | 
			
		||||
                cmp  #$${hex.take(2)}
 | 
			
		||||
                bne  $falseLabel""")
 | 
			
		||||
        } else {
 | 
			
		||||
            // TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  cx16.r14
 | 
			
		||||
                cmp  #$${hex.substring(6, 8)}
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  cx16.r14+1
 | 
			
		||||
                cmp  #$${hex.substring(4, 6)}
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  cx16.r14+2
 | 
			
		||||
                cmp  #$${hex.substring(2, 4)}
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  cx16.r14+3
 | 
			
		||||
                cmp  #$${hex.take(2)}
 | 
			
		||||
                bne  $falseLabel""")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateLongExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
 | 
			
		||||
        // if L==variable
 | 
			
		||||
        // TODO reuse code from ifElse?
 | 
			
		||||
        val varname2 = asmgen.asmVariableName(variable)
 | 
			
		||||
        if(expr is PtIdentifier) {
 | 
			
		||||
            val varname = asmgen.asmVariableName(expr)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  $varname
 | 
			
		||||
                cmp  $varname2
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  $varname+1
 | 
			
		||||
                cmp  $varname2+1
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  $varname+2
 | 
			
		||||
                cmp  $varname2+2
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  $varname+3
 | 
			
		||||
                cmp  $varname2+3
 | 
			
		||||
                bne  $falseLabel""")
 | 
			
		||||
        } else {
 | 
			
		||||
            // TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  cx16.r14
 | 
			
		||||
                cmp  $varname2
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  cx16.r14+1
 | 
			
		||||
                cmp  $varname2+1
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  cx16.r14+2
 | 
			
		||||
                cmp  $varname2+2
 | 
			
		||||
                bne  $falseLabel
 | 
			
		||||
                lda  cx16.r14+3
 | 
			
		||||
                cmp  $varname2+3
 | 
			
		||||
                bne  $falseLabel""")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateLongExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
 | 
			
		||||
        TODO("if expression LONG != number ${expr.position}")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateLongExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
 | 
			
		||||
        // if L!=variable
 | 
			
		||||
        TODO("if expression LONG != variable ${expr.position}")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) {
 | 
			
		||||
        // if w!=0
 | 
			
		||||
        // TODO reuse code from ifElse?
 | 
			
		||||
@@ -304,6 +480,56 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateLongExprIsZero(expr: PtExpression, falseLabel: String) {
 | 
			
		||||
        // if L==0
 | 
			
		||||
        // TODO reuse code from ifElse?
 | 
			
		||||
        if(expr is PtIdentifier) {
 | 
			
		||||
            val varname = asmgen.asmVariableName(expr)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  $varname
 | 
			
		||||
                ora  $varname+1
 | 
			
		||||
                ora  $varname+2
 | 
			
		||||
                ora  $varname+3
 | 
			
		||||
                bne  $falseLabel""")
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  cx16.r14
 | 
			
		||||
                ora  cx16.r14+1
 | 
			
		||||
                ora  cx16.r14+2
 | 
			
		||||
                ora  cx16.r14+3
 | 
			
		||||
                sta  P8ZP_SCRATCH_REG""")
 | 
			
		||||
            asmgen.popLongRegisters(RegisterOrPair.R14R15_32, 1)
 | 
			
		||||
            asmgen.out("  lda  P8ZP_SCRATCH_REG |  bne  $falseLabel")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateLongExprIsNotZero(expr: PtExpression, falseLabel: String) {
 | 
			
		||||
        // if L!=0
 | 
			
		||||
        // TODO reuse code from ifElse?
 | 
			
		||||
        if(expr is PtIdentifier) {
 | 
			
		||||
            val varname = asmgen.asmVariableName(expr)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  $varname
 | 
			
		||||
                ora  $varname+1
 | 
			
		||||
                ora  $varname+2
 | 
			
		||||
                ora  $varname+3
 | 
			
		||||
                beq  $falseLabel""")
 | 
			
		||||
        } else {
 | 
			
		||||
            asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
 | 
			
		||||
            asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
 | 
			
		||||
            asmgen.out("""
 | 
			
		||||
                lda  cx16.r14
 | 
			
		||||
                ora  cx16.r14+1
 | 
			
		||||
                ora  cx16.r14+2
 | 
			
		||||
                ora  cx16.r14+3
 | 
			
		||||
                sta  P8ZP_SCRATCH_REG""")
 | 
			
		||||
            asmgen.popLongRegisters(RegisterOrPair.R14R15_32, 1)
 | 
			
		||||
            asmgen.out("  lda  P8ZP_SCRATCH_REG |  beq  $falseLabel")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
 | 
			
		||||
        // optimized code for byte comparisons with 0
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,8 +44,8 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                asmgen.out("${flt.value}\t.byte  $floatFill  ; float $floatvalue")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            structInstances2asm()
 | 
			
		||||
            memorySlabs()
 | 
			
		||||
            tempVars()
 | 
			
		||||
            footer()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -71,6 +71,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
 | 
			
		||||
        asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1}    ; word")
 | 
			
		||||
        asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2}    ; word")
 | 
			
		||||
        asmgen.out("P8ZP_SCRATCH_PTR = ${zp.SCRATCH_PTR}  ; word")
 | 
			
		||||
        if(compTarget.name=="c64") {
 | 
			
		||||
            if(options.floats)
 | 
			
		||||
                asmgen.out("PROG8_C64_BANK_CONFIG=31  ; basic+IO+kernal")
 | 
			
		||||
@@ -208,40 +209,20 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
 | 
			
		||||
    private fun memorySlabs() {
 | 
			
		||||
        if(symboltable.allMemorySlabs.isNotEmpty()) {
 | 
			
		||||
            asmgen.out("; memory slabs\n  .section slabs_BSS")
 | 
			
		||||
            asmgen.out("prog8_slabs\t.block")
 | 
			
		||||
            asmgen.out("; memory slabs\n  .section BSS_SLABS")
 | 
			
		||||
            asmgen.out("$StMemorySlabBlockName\t.block")
 | 
			
		||||
            for (slab in symboltable.allMemorySlabs) {
 | 
			
		||||
                if (slab.align > 1u)
 | 
			
		||||
                    asmgen.out("\t.align  ${slab.align.toHex()}")
 | 
			
		||||
                asmgen.out("${slab.name}\t.fill  ${slab.size}")
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out("\t.bend\n  .send slabs_BSS")
 | 
			
		||||
            asmgen.out("\t.bend\n  .send BSS_SLABS")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun tempVars() {
 | 
			
		||||
        asmgen.out("; expression temp vars\n  .section BSS")
 | 
			
		||||
        for((dt, count) in asmgen.tempVarsCounters) {
 | 
			
		||||
            if(count>0) {
 | 
			
		||||
                for(num in 1..count) {
 | 
			
		||||
                    val name = asmgen.buildTempVarName(dt, num)
 | 
			
		||||
                    when (dt) {
 | 
			
		||||
                        BaseDataType.BOOL  -> asmgen.out("$name    .byte  ?")
 | 
			
		||||
                        BaseDataType.BYTE  -> asmgen.out("$name    .char  ?")
 | 
			
		||||
                        BaseDataType.UBYTE -> asmgen.out("$name    .byte  ?")
 | 
			
		||||
                        BaseDataType.WORD  -> asmgen.out("$name    .sint  ?")
 | 
			
		||||
                        BaseDataType.UWORD -> asmgen.out("$name    .word  ?")
 | 
			
		||||
                        BaseDataType.FLOAT -> asmgen.out("$name    .fill  ${options.compTarget.FLOAT_MEM_SIZE}")
 | 
			
		||||
                        BaseDataType.LONG -> throw AssemblyError("should not have a variable with long dt only constants")
 | 
			
		||||
                        else -> throw AssemblyError("weird dt for extravar $dt")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.out("  .send BSS")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun footer() {
 | 
			
		||||
        asmgen.out("  .dsection STRUCTINSTANCES\n")
 | 
			
		||||
 | 
			
		||||
        var relocateBssVars = false
 | 
			
		||||
        var relocateBssSlabs = false
 | 
			
		||||
        var relocatedBssStart = 0u
 | 
			
		||||
@@ -297,14 +278,14 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
 | 
			
		||||
        if(relocateBssVars) {
 | 
			
		||||
            if(!relocateBssSlabs)
 | 
			
		||||
                asmgen.out("  .dsection slabs_BSS")
 | 
			
		||||
                asmgen.out("  .dsection BSS_SLABS")
 | 
			
		||||
            asmgen.out("prog8_program_end\t; end of program label for progend()")
 | 
			
		||||
            asmgen.out("  * = ${relocatedBssStart.toHex()}")
 | 
			
		||||
            asmgen.out("  .dsection BSS_NOCLEAR")
 | 
			
		||||
            asmgen.out("prog8_bss_section_start")
 | 
			
		||||
            asmgen.out("  .dsection BSS")
 | 
			
		||||
            if(relocateBssSlabs)
 | 
			
		||||
                asmgen.out("  .dsection slabs_BSS")
 | 
			
		||||
                asmgen.out("  .dsection BSS_SLABS")
 | 
			
		||||
            asmgen.out("  .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
 | 
			
		||||
            asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -313,12 +294,12 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
            asmgen.out("  .dsection BSS")
 | 
			
		||||
            asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
 | 
			
		||||
            if(!relocateBssSlabs)
 | 
			
		||||
                asmgen.out("  .dsection slabs_BSS")
 | 
			
		||||
                asmgen.out("  .dsection BSS_SLABS")
 | 
			
		||||
            asmgen.out("prog8_program_end\t; end of program label for progend()")
 | 
			
		||||
            if(relocateBssSlabs) {
 | 
			
		||||
                asmgen.out("  * = ${relocatedBssStart.toHex()}")
 | 
			
		||||
                asmgen.out("  .dsection slabs_BSS")
 | 
			
		||||
                asmgen.out("  .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
 | 
			
		||||
                asmgen.out("  .dsection BSS_SLABS")
 | 
			
		||||
                asmgen.out("  .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for BSS_SLABS section\"")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -351,10 +332,9 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        if (initializers.isNotEmpty()) {
 | 
			
		||||
            asmgen.out("prog8_init_vars\t.block")
 | 
			
		||||
            initializers.forEach { assign ->
 | 
			
		||||
                if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
 | 
			
		||||
                val constvalue = assign.value as? PtNumber
 | 
			
		||||
                if(constvalue==null || constvalue.number!=0.0 || allocator.isZpVar(assign.target.identifier!!.name))
 | 
			
		||||
                    asmgen.translate(assign)
 | 
			
		||||
                else
 | 
			
		||||
                    throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
 | 
			
		||||
                // the other variables that should be set to zero are done so as part of the BSS section clear.
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out("  rts\n  .bend")
 | 
			
		||||
@@ -372,7 +352,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        val varsInBlock = getVars(scope)
 | 
			
		||||
 | 
			
		||||
        // Zeropage Variables
 | 
			
		||||
        val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
 | 
			
		||||
        val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
 | 
			
		||||
        zeropagevars2asm(varnames)
 | 
			
		||||
 | 
			
		||||
        // MemDefs and Consts
 | 
			
		||||
@@ -386,11 +366,94 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
 | 
			
		||||
        // normal statically allocated variables
 | 
			
		||||
        val variables = varsInBlock
 | 
			
		||||
            .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
 | 
			
		||||
            .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
 | 
			
		||||
            .map { it.value as StStaticVariable }
 | 
			
		||||
        nonZpVariables2asm(variables)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun asmTypeString(dt: DataType): String {
 | 
			
		||||
        return when {
 | 
			
		||||
            dt.isBool || dt.isUnsignedByte -> ".byte"
 | 
			
		||||
            dt.isSignedByte -> ".char"
 | 
			
		||||
            dt.isUnsignedWord || dt.isPointer -> ".word"
 | 
			
		||||
            dt.isSignedWord -> ".sint"
 | 
			
		||||
            dt.isLong -> ".dint"
 | 
			
		||||
            dt.isFloat -> ".byte"
 | 
			
		||||
            else -> {
 | 
			
		||||
                throw AssemblyError("weird dt")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun structInstances2asm() {
 | 
			
		||||
 | 
			
		||||
        fun initValues(instance: StStructInstance): List<String> {
 | 
			
		||||
            val structtype: StStruct = symboltable.lookup(instance.structName) as StStruct
 | 
			
		||||
            return structtype.fields.zip(instance.initialValues).map { (field, value) ->
 | 
			
		||||
                if(field.first.isFloat) {
 | 
			
		||||
                    "["+compTarget.getFloatAsmBytes(value.number!!)+"]"
 | 
			
		||||
                } else {
 | 
			
		||||
                    when {
 | 
			
		||||
                        value.number!=null -> {
 | 
			
		||||
                            if(field.first.isPointer)
 | 
			
		||||
                                "$"+value.number!!.toInt().toString(16)
 | 
			
		||||
                            else if(field.first.isInteger)
 | 
			
		||||
                                value.number!!.toInt().toString()
 | 
			
		||||
                            else
 | 
			
		||||
                                value.number.toString()
 | 
			
		||||
                        }
 | 
			
		||||
                        value.addressOfSymbol!=null -> value.addressOfSymbol!!
 | 
			
		||||
                        value.boolean!=null -> if(value.boolean==true) "1" else "0"
 | 
			
		||||
                        else -> throw AssemblyError("weird struct initial value $value")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        asmgen.out("; struct types")
 | 
			
		||||
        symboltable.allStructInstances.distinctBy { it.structName }.forEach {
 | 
			
		||||
            val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
 | 
			
		||||
            val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
 | 
			
		||||
            asmgen.out("${it.structName}    .struct $structargs\n")
 | 
			
		||||
            structtype.fields.withIndex().forEach { (index, field) ->
 | 
			
		||||
                val dt = field.first
 | 
			
		||||
                val varname = "f${index}"
 | 
			
		||||
                val type = asmTypeString(dt)
 | 
			
		||||
                asmgen.out("p8v_${field.second}  $type  \\$varname")        // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out("    .endstruct\n")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
 | 
			
		||||
        asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
 | 
			
		||||
        asmgen.out("    .section BSS\n")
 | 
			
		||||
        asmgen.out("${StStructInstanceBlockName}_bss  .block\n")
 | 
			
		||||
        instancesNoInit.forEach {
 | 
			
		||||
            val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
 | 
			
		||||
            val zerovalues = structtype.fields.map { field ->
 | 
			
		||||
                if(field.first.isFloat) {
 | 
			
		||||
                    val floatbytes = List(compTarget.memorySize(BaseDataType.FLOAT)) { "?" }
 | 
			
		||||
                    "[${floatbytes.joinToString(",")}]"
 | 
			
		||||
                }
 | 
			
		||||
                else "?"
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out("${it.name}    .dstruct  ${it.structName}, ${zerovalues.joinToString(",")}\n")
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.out("    .endblock\n")
 | 
			
		||||
        asmgen.out("    .send BSS\n")
 | 
			
		||||
 | 
			
		||||
        asmgen.out("; struct instances with initialization values\n")
 | 
			
		||||
        asmgen.out("    .section STRUCTINSTANCES\n")
 | 
			
		||||
        asmgen.out("$StStructInstanceBlockName  .block\n")
 | 
			
		||||
        instances.forEach {
 | 
			
		||||
            val instancename = it.name.substringAfter('.')
 | 
			
		||||
            asmgen.out("$instancename    .dstruct  ${it.structName}, ${initValues(it).joinToString(",")}\n")
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.out("    .endblock\n")
 | 
			
		||||
        asmgen.out("    .send STRUCTINSTANCES\n")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun translateAsmSubroutine(sub: PtAsmSub) {
 | 
			
		||||
        if(sub.inline) {
 | 
			
		||||
            return      // subroutine gets inlined at call site.
 | 
			
		||||
@@ -440,7 +503,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        val varsInSubroutine = getVars(scope)
 | 
			
		||||
 | 
			
		||||
        // Zeropage Variables
 | 
			
		||||
        val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
 | 
			
		||||
        val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
 | 
			
		||||
        zeropagevars2asm(varnames)
 | 
			
		||||
 | 
			
		||||
        // MemDefs and Consts
 | 
			
		||||
@@ -458,7 +521,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
 | 
			
		||||
            entrypointInitialization()
 | 
			
		||||
 | 
			
		||||
        val params = sub.parameters
 | 
			
		||||
        val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
 | 
			
		||||
        if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
 | 
			
		||||
            asmgen.out("; simple int arg(s) passed via cpu register(s)")
 | 
			
		||||
 | 
			
		||||
@@ -494,14 +557,15 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        sub.children.forEach { asmgen.translate(it) }
 | 
			
		||||
 | 
			
		||||
        asmgen.out("; variables")
 | 
			
		||||
        asmgen.out("    .section BSS")
 | 
			
		||||
        asmgen.out("    .section BSS_NOCLEAR")      // these extra vars are initialized before use
 | 
			
		||||
        val asmGenInfo = asmgen.subroutineExtra(sub)
 | 
			
		||||
        for((dt, name, addr) in asmGenInfo.extraVars) {
 | 
			
		||||
            if(addr!=null)
 | 
			
		||||
                asmgen.out("$name = $addr")
 | 
			
		||||
            else when(dt) {
 | 
			
		||||
                BaseDataType.UBYTE -> asmgen.out("$name    .byte  ?")
 | 
			
		||||
                BaseDataType.UWORD -> asmgen.out("$name    .word  ?")
 | 
			
		||||
                BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name    .byte  ?")
 | 
			
		||||
                BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name    .word  ?")
 | 
			
		||||
                BaseDataType.LONG -> asmgen.out("$name    .sint  ?")
 | 
			
		||||
                BaseDataType.FLOAT -> asmgen.out("$name    .fill  ${options.compTarget.FLOAT_MEM_SIZE}")
 | 
			
		||||
                else -> throw AssemblyError("weird dt for extravar $dt")
 | 
			
		||||
            }
 | 
			
		||||
@@ -510,11 +574,11 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
            asmgen.out("$subroutineFloatEvalResultVar1    .fill  ${options.compTarget.FLOAT_MEM_SIZE}")
 | 
			
		||||
        if(asmGenInfo.usedFloatEvalResultVar2)
 | 
			
		||||
            asmgen.out("$subroutineFloatEvalResultVar2    .fill  ${options.compTarget.FLOAT_MEM_SIZE}")
 | 
			
		||||
        asmgen.out("  .send BSS")
 | 
			
		||||
        asmgen.out("  .send BSS_NOCLEAR")
 | 
			
		||||
 | 
			
		||||
        // normal statically allocated variables
 | 
			
		||||
        val variables = varsInSubroutine
 | 
			
		||||
            .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
 | 
			
		||||
            .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
 | 
			
		||||
            .map { it.value as StStaticVariable }
 | 
			
		||||
        nonZpVariables2asm(variables)
 | 
			
		||||
 | 
			
		||||
@@ -569,12 +633,12 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
 | 
			
		||||
        stringVarsWithInitInZp.forEach {
 | 
			
		||||
            val varname = asmgen.asmVariableName(it.name)+"_init_value"
 | 
			
		||||
            outputStringvar(varname, 0, it.value.second, it.value.first)
 | 
			
		||||
            outputStringvar(varname, 0u, it.value.second, it.value.first)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        arrayVarsWithInitInZp.forEach {
 | 
			
		||||
            val varname = asmgen.asmVariableName(it.name)+"_init_value"
 | 
			
		||||
            arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
 | 
			
		||||
            arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        asmgen.out("+")
 | 
			
		||||
@@ -638,22 +702,37 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
 | 
			
		||||
        if(varsNoInit.isNotEmpty()) {
 | 
			
		||||
            asmgen.out("; non-zeropage variables")
 | 
			
		||||
            asmgen.out("  .section BSS")
 | 
			
		||||
            val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
 | 
			
		||||
            val (dirty, clean) = varsNoInit.partition { it.dirty }
 | 
			
		||||
 | 
			
		||||
            fun generate(section: String, variables: List<StStaticVariable>) {
 | 
			
		||||
                asmgen.out("  .section $section")
 | 
			
		||||
                val (notAligned, aligned) = variables.partition { it.align == 0u }
 | 
			
		||||
                notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
 | 
			
		||||
                    uninitializedVariable2asm(it)
 | 
			
		||||
                }
 | 
			
		||||
            aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach {
 | 
			
		||||
                uninitializedVariable2asm(it)
 | 
			
		||||
                aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
 | 
			
		||||
                    .forEach { uninitializedVariable2asm(it) }
 | 
			
		||||
                asmgen.out("  .send $section")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if(clean.isNotEmpty()) {
 | 
			
		||||
                // clean vars end up in BSS so they're at least cleared to 0 at startup
 | 
			
		||||
                generate("BSS", clean)
 | 
			
		||||
            }
 | 
			
		||||
            if(dirty.isNotEmpty()) {
 | 
			
		||||
                // Dirty vars actually are ALSO are put into BSS so they're cleared to 0 at program startup,
 | 
			
		||||
                // but NOT at each entry of the subroutine they're declared in.
 | 
			
		||||
                // This saves the STZ's instructions in the subroutine, while still having deterministic start state.
 | 
			
		||||
                // So there is no actual difference here when compared to the way non-dirty variables are allocated.
 | 
			
		||||
                generate("BSS", dirty)
 | 
			
		||||
            }
 | 
			
		||||
            asmgen.out("  .send BSS")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(varsWithInit.isNotEmpty()) {
 | 
			
		||||
            asmgen.out("; non-zeropage variables with init value")
 | 
			
		||||
            val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
 | 
			
		||||
            val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
 | 
			
		||||
            val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
 | 
			
		||||
            val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
 | 
			
		||||
            val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
 | 
			
		||||
            notAlignedStrings.forEach {
 | 
			
		||||
                outputStringvar(
 | 
			
		||||
                    it.name,
 | 
			
		||||
@@ -697,26 +776,31 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
            dt.isSignedByte -> asmgen.out("${variable.name}\t.char  ?")
 | 
			
		||||
            dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word  ?")
 | 
			
		||||
            dt.isSignedWord -> asmgen.out("${variable.name}\t.sint  ?")
 | 
			
		||||
            dt.isLong -> asmgen.out("${variable.name}\t.dint  ?")
 | 
			
		||||
            dt.isFloat -> asmgen.out("${variable.name}\t.fill  ${compTarget.FLOAT_MEM_SIZE}")
 | 
			
		||||
            dt.isSplitWordArray -> {
 | 
			
		||||
                alignVar(variable.align)
 | 
			
		||||
                val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
 | 
			
		||||
                val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 2
 | 
			
		||||
                asmgen.out("${variable.name}_lsb\t.fill  $numbytesPerHalf")
 | 
			
		||||
                asmgen.out("${variable.name}_msb\t.fill  $numbytesPerHalf")
 | 
			
		||||
            }
 | 
			
		||||
            dt.isArray -> {
 | 
			
		||||
                alignVar(variable.align)
 | 
			
		||||
                val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
 | 
			
		||||
                val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
 | 
			
		||||
                asmgen.out("${variable.name}\t.fill  $numbytes")
 | 
			
		||||
            }
 | 
			
		||||
            dt.isPointer -> asmgen.out("${variable.name}\t.word  ?")        // a pointer is just an uword address
 | 
			
		||||
            dt.isPointerArray -> {
 | 
			
		||||
                TODO("pointers are not supported yet for uninitialized array ${variable.astNode?.position}")
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                throw AssemblyError("weird dt")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun alignVar(align: Int) {
 | 
			
		||||
        if(align > 1)
 | 
			
		||||
    private fun alignVar(align: UInt) {
 | 
			
		||||
        if(align > 1u)
 | 
			
		||||
            asmgen.out("  .align  ${align.toHex()}")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -741,6 +825,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
//            dt.isSignedByte -> asmgen.out("${variable.name}\t.char  $initialValue")
 | 
			
		||||
//            dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word  ${initialValue.toHex()}")
 | 
			
		||||
//            dt.isSignedWord -> asmgen.out("${variable.name}\t.sint  $initialValue")
 | 
			
		||||
//            dt.isLong -> asmgen.out("${variable.name}\t.dint  $initialValue")
 | 
			
		||||
//            dt.isFloat -> {
 | 
			
		||||
//                if(initialValue==0) {
 | 
			
		||||
//                    asmgen.out("${variable.name}\t.byte  0,0,0,0,0  ; float")
 | 
			
		||||
@@ -753,7 +838,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                throw AssemblyError("all string vars should have been interned into prog")
 | 
			
		||||
            }
 | 
			
		||||
            dt.isArray -> {
 | 
			
		||||
                arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
 | 
			
		||||
                arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                throw AssemblyError("weird dt")
 | 
			
		||||
@@ -761,7 +846,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
 | 
			
		||||
    private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
 | 
			
		||||
        alignVar(align)
 | 
			
		||||
        when {
 | 
			
		||||
            dt.isUnsignedByteArray || dt.isBoolArray -> {
 | 
			
		||||
@@ -785,7 +870,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            dt.isSplitWordArray -> {
 | 
			
		||||
                if(dt.elementType().isUnsignedWord) {
 | 
			
		||||
                if(dt.elementType().isUnsignedWord || dt.elementType().isPointer) {
 | 
			
		||||
                    val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
 | 
			
		||||
                    asmgen.out("_array_$varname := ${data.joinToString()}")
 | 
			
		||||
                    asmgen.out("${varname}_lsb\t.byte <_array_$varname")
 | 
			
		||||
@@ -817,6 +902,16 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                        asmgen.out("  .sint  " + chunk.joinToString())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            dt.isLongArray -> {
 | 
			
		||||
                val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
 | 
			
		||||
                if (data.size <= 16)
 | 
			
		||||
                    asmgen.out("$varname\t.dint  ${data.joinToString()}")
 | 
			
		||||
                else {
 | 
			
		||||
                    asmgen.out(varname)
 | 
			
		||||
                    for (chunk in data.chunked(16))
 | 
			
		||||
                        asmgen.out("  .dint  " + chunk.joinToString())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            dt.isFloatArray -> {
 | 
			
		||||
                val array = value ?: zeroFilledArray(orNumberOfZeros!!)
 | 
			
		||||
                val floatFills = array.map {
 | 
			
		||||
@@ -833,7 +928,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
    private fun zeroFilledArray(numElts: Int): StArray {
 | 
			
		||||
        val values = mutableListOf<StArrayElement>()
 | 
			
		||||
        repeat(numElts) {
 | 
			
		||||
            values.add(StArrayElement(0.0, null, null))
 | 
			
		||||
            values.add(StArrayElement(0.0, null, null,null,null))
 | 
			
		||||
        }
 | 
			
		||||
        return values
 | 
			
		||||
    }
 | 
			
		||||
@@ -863,7 +958,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
 | 
			
		||||
    private fun outputStringvar(varname: String, align: UInt, encoding: Encoding, value: String) {
 | 
			
		||||
        alignVar(align)
 | 
			
		||||
        asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
 | 
			
		||||
        val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
 | 
			
		||||
@@ -891,7 +986,7 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                    val number = it.number!!.toInt()
 | 
			
		||||
                    "$"+number.toString(16).padStart(2, '0')
 | 
			
		||||
                }
 | 
			
		||||
            dt.isArray && dt.elementType().isUnsignedWord -> array.map {
 | 
			
		||||
            dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
 | 
			
		||||
                if(it.number!=null) {
 | 
			
		||||
                    "$" + it.number!!.toInt().toString(16).padStart(4, '0')
 | 
			
		||||
                }
 | 
			
		||||
@@ -903,9 +998,16 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                    else
 | 
			
		||||
                        asmgen.asmSymbolName(addrOfSymbol)
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                else if(it.structInstance!=null) {
 | 
			
		||||
                    asmgen.asmSymbolName("${StStructInstanceBlockName}.${it.structInstance!!}")
 | 
			
		||||
                }
 | 
			
		||||
                else if(it.structInstanceUninitialized!=null) {
 | 
			
		||||
                    asmgen.asmSymbolName("${StStructInstanceBlockName}_bss.${it.structInstanceUninitialized!!}")
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    throw AssemblyError("weird array elt")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("invalid dt")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -928,11 +1030,11 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                    else
 | 
			
		||||
                        "-$$hexnum"
 | 
			
		||||
                }
 | 
			
		||||
            dt.isArray && dt.elementType().isUnsignedWord -> array.map {
 | 
			
		||||
            dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
 | 
			
		||||
                val number = it.number!!.toInt()
 | 
			
		||||
                "$" + number.toString(16).padStart(4, '0')
 | 
			
		||||
            }
 | 
			
		||||
            dt.isArray && dt.elementType().isSignedWord -> array.map {
 | 
			
		||||
            dt.isSignedWordArray -> array.map {
 | 
			
		||||
                val number = it.number!!.toInt()
 | 
			
		||||
                val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
 | 
			
		||||
                if(number>=0)
 | 
			
		||||
@@ -940,6 +1042,14 @@ internal class ProgramAndVarsGen(
 | 
			
		||||
                else
 | 
			
		||||
                    "-$$hexnum"
 | 
			
		||||
            }
 | 
			
		||||
            dt.isLongArray -> array.map {
 | 
			
		||||
                val number = it.number!!.toInt()
 | 
			
		||||
                val hexnum = number.absoluteValue.toString(16).padStart(8, '0')
 | 
			
		||||
                if(number>=0)
 | 
			
		||||
                    "$$hexnum"
 | 
			
		||||
                else
 | 
			
		||||
                    "-$$hexnum"
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("invalid dt")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
 | 
			
		||||
        val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
 | 
			
		||||
        val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
 | 
			
		||||
        val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
 | 
			
		||||
        val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
 | 
			
		||||
        val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
 | 
			
		||||
        require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
 | 
			
		||||
 | 
			
		||||
        var numVariablesAllocatedInZP = 0
 | 
			
		||||
@@ -60,9 +60,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
 | 
			
		||||
 | 
			
		||||
        varsRequiringZp.forEach { variable ->
 | 
			
		||||
            val result = zeropage.allocate(
 | 
			
		||||
                variable.scopedName,
 | 
			
		||||
                variable.scopedNameString,
 | 
			
		||||
                variable.dt,
 | 
			
		||||
                variable.length,
 | 
			
		||||
                variable.length?.toInt(),
 | 
			
		||||
                variable.astNode?.position ?: Position.DUMMY,
 | 
			
		||||
                errors
 | 
			
		||||
            )
 | 
			
		||||
@@ -79,9 +79,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
 | 
			
		||||
        if(errors.noErrors()) {
 | 
			
		||||
            varsPreferringZp.forEach { variable ->
 | 
			
		||||
                val result = zeropage.allocate(
 | 
			
		||||
                    variable.scopedName,
 | 
			
		||||
                    variable.scopedNameString,
 | 
			
		||||
                    variable.dt,
 | 
			
		||||
                    variable.length,
 | 
			
		||||
                    variable.length?.toInt(),
 | 
			
		||||
                    variable.astNode?.position ?: Position.DUMMY,
 | 
			
		||||
                    errors
 | 
			
		||||
                )
 | 
			
		||||
@@ -92,16 +92,16 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
 | 
			
		||||
            // try to allocate the "don't care" interger variables into the zeropage until it is full.
 | 
			
		||||
            // TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
 | 
			
		||||
            if(errors.noErrors()) {
 | 
			
		||||
                val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
 | 
			
		||||
                val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
 | 
			
		||||
                for (variable in sortedList) {
 | 
			
		||||
                    if(variable.dt.isIntegerOrBool) {
 | 
			
		||||
                    if(variable.dt.isIntegerOrBool || variable.dt.isPointer) {
 | 
			
		||||
                        if(zeropage.free.isEmpty()) {
 | 
			
		||||
                            break
 | 
			
		||||
                        } else {
 | 
			
		||||
                            val result = zeropage.allocate(
 | 
			
		||||
                                variable.scopedName,
 | 
			
		||||
                                variable.scopedNameString,
 | 
			
		||||
                                variable.dt,
 | 
			
		||||
                                variable.length,
 | 
			
		||||
                                variable.length?.toInt(),
 | 
			
		||||
                                variable.astNode?.position ?: Position.DUMMY,
 | 
			
		||||
                                errors
 | 
			
		||||
                            )
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@ internal class AnyExprAsmGen(
 | 
			
		||||
    private val asmgen: AsmGen6502Internal
 | 
			
		||||
) {
 | 
			
		||||
    fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
 | 
			
		||||
        if(expr.operator==".")
 | 
			
		||||
            throw AssemblyError("pointer deref expression should have been handled elsewhere ${expr.position}")
 | 
			
		||||
        when {
 | 
			
		||||
            expr.type.isByteOrBool -> {
 | 
			
		||||
                if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
 | 
			
		||||
@@ -40,12 +42,24 @@ internal class AnyExprAsmGen(
 | 
			
		||||
                }
 | 
			
		||||
                throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
 | 
			
		||||
            }
 | 
			
		||||
            expr.type.isLong -> {
 | 
			
		||||
                require(expr.left.type.isLong && expr.right.type.isLong) {
 | 
			
		||||
                    "both operands must be longs"
 | 
			
		||||
                }
 | 
			
		||||
                throw AssemblyError("expression should have been handled otherwise: long ${expr.operator} at ${expr.position}")
 | 
			
		||||
            }
 | 
			
		||||
            expr.type.isFloat -> {
 | 
			
		||||
                require(expr.left.type.isFloat && expr.right.type.isFloat) {
 | 
			
		||||
                    "both operands must be floats"
 | 
			
		||||
                }
 | 
			
		||||
                return assignFloatBinExpr(expr, assign)
 | 
			
		||||
            }
 | 
			
		||||
            expr.type.isPointer -> {
 | 
			
		||||
                require((expr.left.type.isPointer || expr.left.type.isUnsignedWord) && (expr.right.type.isPointer || expr.right.type.isUnsignedWord)) {
 | 
			
		||||
                    "both operands must be pointers or uwords"
 | 
			
		||||
                }
 | 
			
		||||
                throw AssemblyError("expression should have been handled otherwise: pointer ${expr.operator} at ${expr.position}")
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird expression type in assignment")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -171,7 +185,7 @@ internal class AnyExprAsmGen(
 | 
			
		||||
                asmgen.assignRegister(RegisterOrPair.A, assign.target)
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            else -> TODO("float expression operator ${expr.operator}")
 | 
			
		||||
            else -> TODO("float expression operator ${expr.operator}  ${expr.position}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,11 @@ import prog8.codegen.cpu6502.AsmGen6502Internal
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
internal enum class TargetStorageKind {
 | 
			
		||||
    VARIABLE,
 | 
			
		||||
    VARIABLE,       // non-pointer variable
 | 
			
		||||
    ARRAY,
 | 
			
		||||
    MEMORY,
 | 
			
		||||
    REGISTER,
 | 
			
		||||
    POINTER,
 | 
			
		||||
    VOID              // assign nothing - used in multi-value assigns for void placeholders
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -32,6 +33,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
 | 
			
		||||
                               val array: PtArrayIndexer? = null,
 | 
			
		||||
                               val memory: PtMemoryByte? = null,
 | 
			
		||||
                               val register: RegisterOrPair? = null,
 | 
			
		||||
                               val pointer: PtPointerDeref? = null,
 | 
			
		||||
                               val origAstTarget: PtAssignTarget? = null
 | 
			
		||||
                               )
 | 
			
		||||
{
 | 
			
		||||
@@ -39,13 +41,24 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
 | 
			
		||||
    val asmVarname: String by lazy {
 | 
			
		||||
        if (array == null)
 | 
			
		||||
            variableAsmName!!
 | 
			
		||||
        else
 | 
			
		||||
            asmgen.asmVariableName(array.variable)
 | 
			
		||||
        else {
 | 
			
		||||
            if(array.variable==null)
 | 
			
		||||
                TODO("asmVarname for array with pointer $position")
 | 
			
		||||
            asmgen.asmVariableName(array.variable!!)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        if(register!=null && !datatype.isNumericOrBool)
 | 
			
		||||
            throw AssemblyError("must be numeric type")
 | 
			
		||||
        if(kind==TargetStorageKind.REGISTER)
 | 
			
		||||
            require(register!=null)
 | 
			
		||||
        else
 | 
			
		||||
            require(register==null)
 | 
			
		||||
        if(kind==TargetStorageKind.POINTER)
 | 
			
		||||
            require(pointer!=null)
 | 
			
		||||
        if(pointer!=null)
 | 
			
		||||
            require(kind==TargetStorageKind.POINTER)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
@@ -77,6 +90,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
 | 
			
		||||
                    }
 | 
			
		||||
                    array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget =  this)
 | 
			
		||||
                    memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory =  memory, origAstTarget =  this)
 | 
			
		||||
                    pointerDeref != null -> return AsmAssignTarget(TargetStorageKind.POINTER, asmgen, type, definingSub, target.position, pointer = pointerDeref, origAstTarget =  this)
 | 
			
		||||
                    else -> throw AssemblyError("weird target")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -100,25 +114,17 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
 | 
			
		||||
                    RegisterOrPair.FAC2 -> {
 | 
			
		||||
                        AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
 | 
			
		||||
                    }
 | 
			
		||||
                    RegisterOrPair.R0,
 | 
			
		||||
                    RegisterOrPair.R1,
 | 
			
		||||
                    RegisterOrPair.R2,
 | 
			
		||||
                    RegisterOrPair.R3,
 | 
			
		||||
                    RegisterOrPair.R4,
 | 
			
		||||
                    RegisterOrPair.R5,
 | 
			
		||||
                    RegisterOrPair.R6,
 | 
			
		||||
                    RegisterOrPair.R7,
 | 
			
		||||
                    RegisterOrPair.R8,
 | 
			
		||||
                    RegisterOrPair.R9,
 | 
			
		||||
                    RegisterOrPair.R10,
 | 
			
		||||
                    RegisterOrPair.R11,
 | 
			
		||||
                    RegisterOrPair.R12,
 | 
			
		||||
                    RegisterOrPair.R13,
 | 
			
		||||
                    RegisterOrPair.R14,
 | 
			
		||||
                    RegisterOrPair.R15 -> {
 | 
			
		||||
                    in Cx16VirtualRegisters -> {
 | 
			
		||||
                        val dt = if(signed) DataType.WORD else DataType.UWORD
 | 
			
		||||
                        AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
 | 
			
		||||
                    }
 | 
			
		||||
                    in combinedLongRegisters -> {
 | 
			
		||||
                        val dt = if(signed) DataType.LONG
 | 
			
		||||
                            else
 | 
			
		||||
                                TODO("unsigned long $pos")
 | 
			
		||||
                        AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird register $registers")
 | 
			
		||||
                }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -134,11 +140,14 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
 | 
			
		||||
                left is PtIdentifier && left.name==scopedName
 | 
			
		||||
            }
 | 
			
		||||
            TargetStorageKind.ARRAY -> {
 | 
			
		||||
                left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
 | 
			
		||||
                left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords && (left.pointerderef==null && array.pointerderef==null || left.pointerderef!! isSameAs array.pointerderef!!)
 | 
			
		||||
            }
 | 
			
		||||
            TargetStorageKind.MEMORY -> {
 | 
			
		||||
                left isSameAs memory!!
 | 
			
		||||
            }
 | 
			
		||||
            TargetStorageKind.POINTER -> {
 | 
			
		||||
                TODO("is pointer deref target same as expression? ${this.position}")
 | 
			
		||||
            }
 | 
			
		||||
            TargetStorageKind.REGISTER -> false
 | 
			
		||||
            TargetStorageKind.VOID -> false
 | 
			
		||||
        }
 | 
			
		||||
@@ -160,8 +169,11 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
 | 
			
		||||
    val asmVarname: String
 | 
			
		||||
        get() = if(array==null)
 | 
			
		||||
            variableAsmName!!
 | 
			
		||||
        else
 | 
			
		||||
            asmgen.asmVariableName(array.variable)
 | 
			
		||||
        else {
 | 
			
		||||
            if(array.variable==null)
 | 
			
		||||
                TODO("asmVarname for array with pointer")
 | 
			
		||||
            asmgen.asmVariableName(array.variable!!)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
 | 
			
		||||
@@ -203,7 +215,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
 | 
			
		||||
                    val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
 | 
			
		||||
                    val sub = symbol.astNode as IPtSubroutine
 | 
			
		||||
                    val returnType =
 | 
			
		||||
                        if(sub is PtSub && sub.returns.size>1)
 | 
			
		||||
                        if(sub is PtSub && sub.signature.returns.size>1)
 | 
			
		||||
                            DataType.UNDEFINED      // TODO list of types instead?
 | 
			
		||||
                        else
 | 
			
		||||
                            sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -9,6 +9,7 @@ import prog8.codegen.cpu6502.VariableAllocator
 | 
			
		||||
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                           private val assignmentAsmGen: AssignmentAsmGen,
 | 
			
		||||
                                           private val asmgen: AsmGen6502Internal,
 | 
			
		||||
                                           private val ptrgen: PointerAssignmentsGen,
 | 
			
		||||
                                           private val allocator: VariableAllocator
 | 
			
		||||
) {
 | 
			
		||||
    fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) {
 | 
			
		||||
@@ -84,33 +85,45 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    target.datatype.isWord -> {
 | 
			
		||||
                    target.datatype.isWord || target.datatype.isPointer -> {
 | 
			
		||||
                        val block = target.origAstTarget?.definingBlock()
 | 
			
		||||
                        val targetDt = if(target.datatype.isWord) target.datatype else DataType.UWORD   // pointers themselves that get a new value are just treated as UWORD variables
 | 
			
		||||
                        when(value.kind) {
 | 
			
		||||
                            SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt(), block)
 | 
			
		||||
                            SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt(), block)
 | 
			
		||||
                            SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname, value.datatype, block)
 | 
			
		||||
                            SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, regName(value), value.datatype, block)
 | 
			
		||||
                            SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, target.datatype, operator, value.memory!!)
 | 
			
		||||
                            SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.array!!, block)
 | 
			
		||||
                            SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.boolean!!.asInt(), block)
 | 
			
		||||
                            SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.number!!.number.toInt(), block)
 | 
			
		||||
                            SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, value.asmVarname, value.datatype, block)
 | 
			
		||||
                            SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, regName(value), value.datatype, block)
 | 
			
		||||
                            SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, targetDt, operator, value.memory!!)
 | 
			
		||||
                            SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.array!!, block)
 | 
			
		||||
                            SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                if(value.expression is PtTypeCast) {
 | 
			
		||||
                                    if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
 | 
			
		||||
                                    inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression, block)
 | 
			
		||||
                                    inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression, block)
 | 
			
		||||
                                }
 | 
			
		||||
                                else {
 | 
			
		||||
                                    inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression!!, block)
 | 
			
		||||
                                    inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression!!, block)
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    target.datatype.isLong -> {
 | 
			
		||||
                        when(value.kind) {
 | 
			
		||||
                            SourceStorageKind.LITERALBOOLEAN -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt())
 | 
			
		||||
                            SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.number!!.number.toInt())
 | 
			
		||||
                            SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(target.asmVarname, operator, value.asmVarname)
 | 
			
		||||
                            SourceStorageKind.EXPRESSION -> inplacemodificationLongWithExpression(target.asmVarname, operator, value.expression!!)
 | 
			
		||||
                            SourceStorageKind.REGISTER -> TODO("32 bits register inplace modification? ${target.position}")
 | 
			
		||||
                            SourceStorageKind.ARRAY -> TODO("inplace modify long with array ${target.position}")
 | 
			
		||||
                            SourceStorageKind.MEMORY -> TODO("memread into long ${target.position}")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    target.datatype.isFloat -> {
 | 
			
		||||
                        when(value.kind) {
 | 
			
		||||
                            SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt().toDouble())
 | 
			
		||||
                            SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.number!!.number)
 | 
			
		||||
                            SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(target.asmVarname, operator, value.asmVarname)
 | 
			
		||||
                            SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(target.asmVarname, operator, regName(value))
 | 
			
		||||
                            SourceStorageKind.MEMORY -> TODO("memread into float")
 | 
			
		||||
                            SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
 | 
			
		||||
                            SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.array!!)
 | 
			
		||||
                            SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                if(value.expression is PtTypeCast) {
 | 
			
		||||
@@ -154,7 +167,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                            SourceStorageKind.LITERALNUMBER -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
 | 
			
		||||
                            SourceStorageKind.VARIABLE -> inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
 | 
			
		||||
                            SourceStorageKind.REGISTER -> inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
 | 
			
		||||
                            SourceStorageKind.MEMORY -> TODO("memread into pointer")
 | 
			
		||||
                            SourceStorageKind.MEMORY -> TODO("memread into pointer ${target.position}")
 | 
			
		||||
                            SourceStorageKind.ARRAY -> inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
 | 
			
		||||
                            SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                if(value.expression is PtTypeCast) {
 | 
			
		||||
@@ -205,7 +218,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                asmgen.out("  ldx  P8ZP_SCRATCH_B1")
 | 
			
		||||
                            }
 | 
			
		||||
                            SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
 | 
			
		||||
                                val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, memory)
 | 
			
		||||
                                asmgen.out("  sta  $tempVar")
 | 
			
		||||
                                if(value.expression is PtTypeCast)
 | 
			
		||||
                                    inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression)
 | 
			
		||||
@@ -224,7 +237,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            TargetStorageKind.ARRAY -> {
 | 
			
		||||
                val indexNum = target.array!!.index as? PtNumber
 | 
			
		||||
                val deref = target.array!!.pointerderef
 | 
			
		||||
                if(deref!=null) {
 | 
			
		||||
                    TODO("inplace modification array indexed pointer deref ${target.position}")
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                val targetArrayVar = target.array.variable!!
 | 
			
		||||
                val indexNum = target.array.index as? PtNumber
 | 
			
		||||
                if (indexNum!=null) {
 | 
			
		||||
                    val index = indexNum.number.toInt()
 | 
			
		||||
                    if(target.array.splitWords) {
 | 
			
		||||
@@ -283,13 +302,32 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        target.datatype.isLong -> {
 | 
			
		||||
                            when(value.kind) {
 | 
			
		||||
                                SourceStorageKind.LITERALBOOLEAN -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.boolean!!.asInt())
 | 
			
		||||
                                SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.number!!.number.toInt())
 | 
			
		||||
                                SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(targetVarName, operator, value.asmVarname)
 | 
			
		||||
                                SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(targetVarName, operator, regName(value))
 | 
			
		||||
                                SourceStorageKind.MEMORY -> TODO("inplace long modifiication ${target.position}")
 | 
			
		||||
                                SourceStorageKind.ARRAY -> TODO("inplace long modifiication ${target.position}")
 | 
			
		||||
                                SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                    if(value.expression is PtTypeCast) {
 | 
			
		||||
                                        if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
 | 
			
		||||
                                        TODO("inplace long modifiication ${target.position}")
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        TODO("inplace long modifiication ${target.position}")
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        target.datatype.isFloat -> {
 | 
			
		||||
                            when(value.kind) {
 | 
			
		||||
                                SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.boolean!!.asInt().toDouble())
 | 
			
		||||
                                SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.number!!.number)
 | 
			
		||||
                                SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(targetVarName, operator, value.asmVarname)
 | 
			
		||||
                                SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(targetVarName, operator, regName(value))
 | 
			
		||||
                                SourceStorageKind.MEMORY -> TODO("memread into float array")
 | 
			
		||||
                                SourceStorageKind.MEMORY -> TODO("memread into float array ${target.position}")
 | 
			
		||||
                                SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(targetVarName, operator, value.array!!)
 | 
			
		||||
                                SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                    if(value.expression is PtTypeCast) {
 | 
			
		||||
@@ -301,7 +339,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        target.datatype.isPointer -> TODO("inplace modification of pointer array ${target.position}")
 | 
			
		||||
                        else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -320,7 +358,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                return
 | 
			
		||||
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
 | 
			
		||||
                            asmgen.saveRegisterStack(CpuRegister.Y, false)
 | 
			
		||||
                            asmgen.out("  lda  ${target.array.variable.name},y")
 | 
			
		||||
                            asmgen.out("  lda  ${targetArrayVar.name},y")
 | 
			
		||||
                            when(value.kind) {
 | 
			
		||||
                                SourceStorageKind.LITERALBOOLEAN -> {
 | 
			
		||||
                                    inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
 | 
			
		||||
@@ -356,7 +394,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                    val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
 | 
			
		||||
                                    val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, target.array)
 | 
			
		||||
                                    asmgen.out("  sta  $tempVar")
 | 
			
		||||
                                    if(value.expression is PtTypeCast)
 | 
			
		||||
                                        inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression)
 | 
			
		||||
@@ -366,7 +404,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                    asmgen.out("  lda  $tempVar")
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            asmgen.out("  sta  ${target.array.variable.name},y")
 | 
			
		||||
                            asmgen.out("  sta  ${targetArrayVar.name},y")
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        target.datatype.isWord -> {
 | 
			
		||||
@@ -377,11 +415,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
 | 
			
		||||
                            asmgen.saveRegisterStack(CpuRegister.Y, false)
 | 
			
		||||
                            if(target.array.splitWords) {
 | 
			
		||||
                                asmgen.out("  lda  ${target.array.variable.name}_lsb,y")
 | 
			
		||||
                                asmgen.out("  ldx  ${target.array.variable.name}_msb,y")
 | 
			
		||||
                                asmgen.out("  lda  ${targetArrayVar.name}_lsb,y")
 | 
			
		||||
                                asmgen.out("  ldx  ${targetArrayVar.name}_msb,y")
 | 
			
		||||
                            } else {
 | 
			
		||||
                                asmgen.out("  lda  ${target.array.variable.name},y")
 | 
			
		||||
                                asmgen.out("  ldx  ${target.array.variable.name}+1,y")
 | 
			
		||||
                                asmgen.out("  lda  ${targetArrayVar.name},y")
 | 
			
		||||
                                asmgen.out("  ldx  ${targetArrayVar.name}+1,y")
 | 
			
		||||
                            }
 | 
			
		||||
                            val block = target.origAstTarget?.definingBlock()
 | 
			
		||||
                            when(value.kind) {
 | 
			
		||||
@@ -439,7 +477,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                    val tempVar = asmgen.getTempVarName(BaseDataType.UWORD)
 | 
			
		||||
                                    val tempVar = asmgen.createTempVarReused(BaseDataType.UWORD, false, target.array)
 | 
			
		||||
                                    asmgen.out("  sta  $tempVar |  stx  $tempVar+1")
 | 
			
		||||
                                    if(value.expression is PtTypeCast)
 | 
			
		||||
                                        inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
 | 
			
		||||
@@ -450,14 +488,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                            }
 | 
			
		||||
                            asmgen.restoreRegisterStack(CpuRegister.Y, true)
 | 
			
		||||
                            if(target.array.splitWords)
 | 
			
		||||
                                asmgen.out("  sta  ${target.array.variable.name}_lsb,y |  txa |  sta  ${target.array.variable.name}_msb,y")
 | 
			
		||||
                                asmgen.out("  sta  ${targetArrayVar.name}_lsb,y |  txa |  sta  ${targetArrayVar.name}_msb,y")
 | 
			
		||||
                            else
 | 
			
		||||
                                asmgen.out("  sta  ${target.array.variable.name},y |  txa |  sta  ${target.array.variable.name}+1,y")
 | 
			
		||||
                                asmgen.out("  sta  ${targetArrayVar.name},y |  txa |  sta  ${targetArrayVar.name}+1,y")
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        target.datatype.isFloat -> {
 | 
			
		||||
                            // copy array value into tempvar
 | 
			
		||||
                            val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
 | 
			
		||||
                            val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, target.array)
 | 
			
		||||
                            asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                                ldy  #>${target.asmVarname}
 | 
			
		||||
@@ -480,7 +518,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
 | 
			
		||||
                                SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
 | 
			
		||||
                                SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(tempvar, operator, regName(value))
 | 
			
		||||
                                SourceStorageKind.MEMORY -> TODO("memread into float")
 | 
			
		||||
                                SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
 | 
			
		||||
                                SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(tempvar, operator, value.array!!)
 | 
			
		||||
                                SourceStorageKind.EXPRESSION -> {
 | 
			
		||||
                                    if(value.expression is PtTypeCast) {
 | 
			
		||||
@@ -504,18 +542,467 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                                                pla  ; restore array ptr lsb
 | 
			
		||||
                                                jsr  floats.copy_float""")
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        target.datatype.isPointer -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
 | 
			
		||||
                        else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            TargetStorageKind.POINTER -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
 | 
			
		||||
            TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
 | 
			
		||||
            TargetStorageKind.VOID -> { /* do nothing */ }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun inplacemodificationLongWithExpression(targetVar: String, operator: String, value: PtExpression) {
 | 
			
		||||
        // it's not an expression so no need to preserve R14:R15
 | 
			
		||||
        assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
 | 
			
		||||
        inplacemodificationLongWithVariable(targetVar, operator, "cx16.r14")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) {
 | 
			
		||||
        when(operator) {
 | 
			
		||||
            "+" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    clc
 | 
			
		||||
                    lda  $targetVar
 | 
			
		||||
                    adc  $sourceVar
 | 
			
		||||
                    sta  $targetVar
 | 
			
		||||
                    lda  $targetVar+1
 | 
			
		||||
                    adc  $sourceVar+1
 | 
			
		||||
                    sta  $targetVar+1
 | 
			
		||||
                    lda  $targetVar+2
 | 
			
		||||
                    adc  $sourceVar+2
 | 
			
		||||
                    sta  $targetVar+2
 | 
			
		||||
                    lda  $targetVar+3
 | 
			
		||||
                    adc  $sourceVar+3
 | 
			
		||||
                    sta  $targetVar+3""")
 | 
			
		||||
            }
 | 
			
		||||
            "-" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    sec
 | 
			
		||||
                    lda  $targetVar
 | 
			
		||||
                    sbc  $sourceVar
 | 
			
		||||
                    sta  $targetVar
 | 
			
		||||
                    lda  $targetVar+1
 | 
			
		||||
                    sbc  $sourceVar+1
 | 
			
		||||
                    sta  $targetVar+1
 | 
			
		||||
                    lda  $targetVar+2
 | 
			
		||||
                    sbc  $sourceVar+2
 | 
			
		||||
                    sta  $targetVar+2
 | 
			
		||||
                    lda  $targetVar+3
 | 
			
		||||
                    sbc  $sourceVar+3
 | 
			
		||||
                    sta  $targetVar+3""")
 | 
			
		||||
            }
 | 
			
		||||
            "<<" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    ldy  $sourceVar
 | 
			
		||||
-                   asl  $targetVar
 | 
			
		||||
                    rol  $targetVar+1
 | 
			
		||||
                    rol  $targetVar+2
 | 
			
		||||
                    rol  $targetVar+3
 | 
			
		||||
                    dey
 | 
			
		||||
                    bne  -""")
 | 
			
		||||
            }
 | 
			
		||||
            ">>" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    ldy  $sourceVar
 | 
			
		||||
-                   lda  $targetVar+3
 | 
			
		||||
                    asl  a    ; save sign bit
 | 
			
		||||
                    ror  $targetVar+3
 | 
			
		||||
                    ror  $targetVar+2
 | 
			
		||||
                    ror  $targetVar+1
 | 
			
		||||
                    ror  $targetVar
 | 
			
		||||
                    dey
 | 
			
		||||
                    bne  -""")
 | 
			
		||||
            }
 | 
			
		||||
            "|" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $targetVar
 | 
			
		||||
                    ora  $sourceVar
 | 
			
		||||
                    sta  $targetVar  
 | 
			
		||||
                    lda  $targetVar+1
 | 
			
		||||
                    ora  $sourceVar+1
 | 
			
		||||
                    sta  $targetVar+1
 | 
			
		||||
                    lda  $targetVar+2
 | 
			
		||||
                    ora  $sourceVar+2
 | 
			
		||||
                    sta  $targetVar+2
 | 
			
		||||
                    lda  $targetVar+3
 | 
			
		||||
                    ora  $sourceVar+3
 | 
			
		||||
                    sta  $targetVar+3""")
 | 
			
		||||
            }
 | 
			
		||||
            "&" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $targetVar
 | 
			
		||||
                    and  $sourceVar
 | 
			
		||||
                    sta  $targetVar  
 | 
			
		||||
                    lda  $targetVar+1
 | 
			
		||||
                    and  $sourceVar+1
 | 
			
		||||
                    sta  $targetVar+1
 | 
			
		||||
                    lda  $targetVar+2
 | 
			
		||||
                    and  $sourceVar+2
 | 
			
		||||
                    sta  $targetVar+2
 | 
			
		||||
                    lda  $targetVar+3
 | 
			
		||||
                    and  $sourceVar+3
 | 
			
		||||
                    sta  $targetVar+3""")
 | 
			
		||||
            }
 | 
			
		||||
            "^" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $targetVar
 | 
			
		||||
                    eor  $sourceVar
 | 
			
		||||
                    sta  $targetVar  
 | 
			
		||||
                    lda  $targetVar+1
 | 
			
		||||
                    eor  $sourceVar+1
 | 
			
		||||
                    sta  $targetVar+1
 | 
			
		||||
                    lda  $targetVar+2
 | 
			
		||||
                    eor  $sourceVar+2
 | 
			
		||||
                    sta  $targetVar+2
 | 
			
		||||
                    lda  $targetVar+3
 | 
			
		||||
                    eor  $sourceVar+3
 | 
			
		||||
                    sta  $targetVar+3""")
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                TODO("in-place modify LONG with variable")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) {
 | 
			
		||||
 | 
			
		||||
        fun inplaceLongShiftLeft() {
 | 
			
		||||
            when {
 | 
			
		||||
                value in 0..2 -> {
 | 
			
		||||
                    repeat(value) {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            asl  $variable
 | 
			
		||||
                            rol  $variable+1
 | 
			
		||||
                            rol  $variable+2
 | 
			
		||||
                            rol  $variable+3""")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                value in 3..7 -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        ldy  #$value
 | 
			
		||||
-                       asl  $variable
 | 
			
		||||
                        rol  $variable+1
 | 
			
		||||
                        rol  $variable+2
 | 
			
		||||
                        rol  $variable+3
 | 
			
		||||
                        dey
 | 
			
		||||
                        bne  -""")
 | 
			
		||||
                }
 | 
			
		||||
                value == 8 -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  $variable+2
 | 
			
		||||
                        sta  $variable+3
 | 
			
		||||
                        lda  $variable+1
 | 
			
		||||
                        sta  $variable+2
 | 
			
		||||
                        lda  $variable
 | 
			
		||||
                        sta  $variable+1
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable""")
 | 
			
		||||
                }
 | 
			
		||||
                value in 9..15 -> {
 | 
			
		||||
                    val shift = value - 8
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  $variable+2
 | 
			
		||||
                        sta  $variable+3
 | 
			
		||||
                        lda  $variable+1
 | 
			
		||||
                        sta  $variable+2
 | 
			
		||||
                        lda  $variable
 | 
			
		||||
                        sta  $variable+1
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable
 | 
			
		||||
                        ldy  #$shift
 | 
			
		||||
-                       asl  $variable+1
 | 
			
		||||
                        rol  $variable+2
 | 
			
		||||
                        rol  $variable+3
 | 
			
		||||
                        dey
 | 
			
		||||
                        bne  -""")
 | 
			
		||||
                }
 | 
			
		||||
                value == 16 -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  $variable+1
 | 
			
		||||
                        sta  $variable+3
 | 
			
		||||
                        lda  $variable
 | 
			
		||||
                        sta  $variable+2
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable
 | 
			
		||||
                        sta  $variable+1""")
 | 
			
		||||
                }
 | 
			
		||||
                value in 17..23 -> {
 | 
			
		||||
                    val shift = value-16
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  $variable+1
 | 
			
		||||
                        sta  $variable+3
 | 
			
		||||
                        lda  $variable
 | 
			
		||||
                        sta  $variable+2
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable
 | 
			
		||||
                        sta  $variable+1
 | 
			
		||||
                        ldy  #$shift
 | 
			
		||||
-                       asl  $variable+2
 | 
			
		||||
                        rol  $variable+3
 | 
			
		||||
                        dey
 | 
			
		||||
                        bne  -""")
 | 
			
		||||
                }
 | 
			
		||||
                value == 24 -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  $variable
 | 
			
		||||
                        sta  $variable+3
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable
 | 
			
		||||
                        sta  $variable+1
 | 
			
		||||
                        sta  $variable+2""")
 | 
			
		||||
                }
 | 
			
		||||
                value <= 31 -> {
 | 
			
		||||
                    val shift = value-24
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  $variable
 | 
			
		||||
                        ldy  #$shift
 | 
			
		||||
-                       asl  a
 | 
			
		||||
                        dey
 | 
			
		||||
                        bne  -
 | 
			
		||||
                        sta  $variable+3
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable
 | 
			
		||||
                        sta  $variable+1
 | 
			
		||||
                        sta  $variable+2""")
 | 
			
		||||
                }
 | 
			
		||||
                else -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable
 | 
			
		||||
                        sta  $variable+1
 | 
			
		||||
                        sta  $variable+2
 | 
			
		||||
                        sta  $variable+3""")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun inplaceLongShiftRight() {
 | 
			
		||||
            when {
 | 
			
		||||
                value in 0..2 -> {
 | 
			
		||||
                    repeat(value) {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            lda  $variable+3
 | 
			
		||||
                            asl  a      ; save sign bit
 | 
			
		||||
                            ror  $variable+3
 | 
			
		||||
                            ror  $variable+2
 | 
			
		||||
                            ror  $variable+1
 | 
			
		||||
                            ror  $variable""")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // TODO optimize for more cases 8, 16, 24 etc but don't forget to take the sign bit into account!
 | 
			
		||||
                value <= 31 -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        ldy  #$value
 | 
			
		||||
-                       lda  $variable+3
 | 
			
		||||
                        asl  a      ; save sign bit
 | 
			
		||||
                        ror  $variable+3
 | 
			
		||||
                        ror  $variable+2
 | 
			
		||||
                        ror  $variable+1
 | 
			
		||||
                        ror  $variable
 | 
			
		||||
                        dey
 | 
			
		||||
                        bne  -""")
 | 
			
		||||
                }
 | 
			
		||||
                else -> {
 | 
			
		||||
                    asmgen.out("""
 | 
			
		||||
                        lda  #0
 | 
			
		||||
                        sta  $variable
 | 
			
		||||
                        sta  $variable+1
 | 
			
		||||
                        sta  $variable+2
 | 
			
		||||
                        sta  $variable+3""")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        when(operator) {
 | 
			
		||||
            "+" -> {
 | 
			
		||||
                when(value) {
 | 
			
		||||
                    0 -> {}
 | 
			
		||||
                    1 -> {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            inc  $variable
 | 
			
		||||
                            bne  +
 | 
			
		||||
                            inc  $variable+1
 | 
			
		||||
                            bne  +
 | 
			
		||||
                            inc  $variable+2
 | 
			
		||||
                            bne  +
 | 
			
		||||
                            inc  $variable+3
 | 
			
		||||
+""")
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> {
 | 
			
		||||
                        if(value in 1..255) {
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                clc
 | 
			
		||||
                                lda  $variable
 | 
			
		||||
                                adc  #$value
 | 
			
		||||
                                sta  $variable
 | 
			
		||||
                                bcc  +
 | 
			
		||||
                                inc  $variable+1
 | 
			
		||||
                                bne  +
 | 
			
		||||
                                inc  $variable+2
 | 
			
		||||
                                bne  +
 | 
			
		||||
                                inc  $variable+3
 | 
			
		||||
+""")
 | 
			
		||||
                        } else if(value in 1..65535) {
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                clc
 | 
			
		||||
                                lda  $variable
 | 
			
		||||
                                adc  #<$value
 | 
			
		||||
                                sta  $variable
 | 
			
		||||
                                lda  $variable+1
 | 
			
		||||
                                adc  #>$value
 | 
			
		||||
                                sta  $variable+1
 | 
			
		||||
                                bcc  +
 | 
			
		||||
                                inc  $variable+2
 | 
			
		||||
                                bne  +
 | 
			
		||||
                                inc  $variable+3
 | 
			
		||||
+""")
 | 
			
		||||
                        } else {
 | 
			
		||||
                            val hex = value.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                clc
 | 
			
		||||
                                lda  $variable
 | 
			
		||||
                                adc  #$${hex.substring(6,8)}
 | 
			
		||||
                                sta  $variable
 | 
			
		||||
                                lda  $variable+1
 | 
			
		||||
                                adc  #$${hex.substring(4, 6)}
 | 
			
		||||
                                sta  $variable+1
 | 
			
		||||
                                lda  $variable+2
 | 
			
		||||
                                adc  #$${hex.substring(2, 4)}
 | 
			
		||||
                                sta  $variable+2
 | 
			
		||||
                                lda  $variable+3
 | 
			
		||||
                                adc  #$${hex.take(2)}
 | 
			
		||||
                                sta  $variable+3""")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            "-" -> {
 | 
			
		||||
                when(value) {
 | 
			
		||||
                    0 -> {}
 | 
			
		||||
                    1 -> {
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            lda  $variable
 | 
			
		||||
                            bne  +
 | 
			
		||||
                            dec  $variable+1
 | 
			
		||||
                            bne  +
 | 
			
		||||
                            dec  $variable+2
 | 
			
		||||
                            bne  +
 | 
			
		||||
                            dec  $variable+3
 | 
			
		||||
+                           dec  $variable""")
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> {
 | 
			
		||||
                        if(value in 1..255) {
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                lda  $variable
 | 
			
		||||
                                sec
 | 
			
		||||
                                sbc  #$value
 | 
			
		||||
                                sta  $variable
 | 
			
		||||
                                bcs  +
 | 
			
		||||
                                dec  $variable+1
 | 
			
		||||
                                bne  +
 | 
			
		||||
                                dec  $variable+2
 | 
			
		||||
                                bne  +
 | 
			
		||||
                                dec  $variable+3
 | 
			
		||||
+""")
 | 
			
		||||
                        } else if(value in 1..65535) {
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                lda  $variable
 | 
			
		||||
                                sec
 | 
			
		||||
                                sbc  #<$value
 | 
			
		||||
                                sta  $variable
 | 
			
		||||
                                lda  $variable+1
 | 
			
		||||
                                sbc  #>$value
 | 
			
		||||
                                sta  $variable+1
 | 
			
		||||
                                bcs  +
 | 
			
		||||
                                dec  $variable+2
 | 
			
		||||
                                bne  +
 | 
			
		||||
                                dec  $variable+3
 | 
			
		||||
+""")
 | 
			
		||||
                        } else {
 | 
			
		||||
                            val hex = value.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                sec
 | 
			
		||||
                                lda  $variable
 | 
			
		||||
                                sbc  #$${hex.substring(6,8)}
 | 
			
		||||
                                sta  $variable
 | 
			
		||||
                                lda  $variable+1
 | 
			
		||||
                                sbc  #$${hex.substring(4, 6)}
 | 
			
		||||
                                sta  $variable+1
 | 
			
		||||
                                lda  $variable+2
 | 
			
		||||
                                sbc  #$${hex.substring(2, 4)}
 | 
			
		||||
                                sta  $variable+2
 | 
			
		||||
                                lda  $variable+3
 | 
			
		||||
                                sbc  #$${hex.take(2)}
 | 
			
		||||
                                sta  $variable+3""")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            "<<" -> if (value > 0) inplaceLongShiftLeft()
 | 
			
		||||
            ">>" -> if (value > 0) inplaceLongShiftRight()
 | 
			
		||||
            "|" -> {
 | 
			
		||||
                val hex = value.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $variable
 | 
			
		||||
                    ora  #$${hex.substring(6,8)}
 | 
			
		||||
                    sta  $variable  
 | 
			
		||||
                    lda  $variable+1
 | 
			
		||||
                    ora  #$${hex.substring(4, 6)}
 | 
			
		||||
                    sta  $variable+1
 | 
			
		||||
                    lda  $variable+2
 | 
			
		||||
                    ora  #$${hex.substring(2, 4)}
 | 
			
		||||
                    sta  $variable+2
 | 
			
		||||
                    lda  $variable+3
 | 
			
		||||
                    ora  #$${hex.take(2)}
 | 
			
		||||
                    sta  $variable+3""")
 | 
			
		||||
            }
 | 
			
		||||
            "&" -> {
 | 
			
		||||
                val hex = value.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $variable
 | 
			
		||||
                    and  #$${hex.substring(6,8)}
 | 
			
		||||
                    sta  $variable  
 | 
			
		||||
                    lda  $variable+1
 | 
			
		||||
                    and  #$${hex.substring(4, 6)}
 | 
			
		||||
                    sta  $variable+1
 | 
			
		||||
                    lda  $variable+2
 | 
			
		||||
                    and  #$${hex.substring(2, 4)}
 | 
			
		||||
                    sta  $variable+2
 | 
			
		||||
                    lda  $variable+3
 | 
			
		||||
                    and  #$${hex.take(2)}
 | 
			
		||||
                    sta  $variable+3""")
 | 
			
		||||
            }
 | 
			
		||||
            "^" -> {
 | 
			
		||||
                val hex = value.toUInt().toString(16).padStart(8, '0')
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $variable
 | 
			
		||||
                    eor  #$${hex.substring(6,8)}
 | 
			
		||||
                    sta  $variable  
 | 
			
		||||
                    lda  $variable+1
 | 
			
		||||
                    eor  #$${hex.substring(4, 6)}
 | 
			
		||||
                    sta  $variable+1
 | 
			
		||||
                    lda  $variable+2
 | 
			
		||||
                    eor  #$${hex.substring(2, 4)}
 | 
			
		||||
                    sta  $variable+2
 | 
			
		||||
                    lda  $variable+3
 | 
			
		||||
                    eor  #$${hex.take(2)}
 | 
			
		||||
                    sta  $variable+3""")
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                TODO("inplace long $operator $value")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
 | 
			
		||||
        val arrayvar = asmgen.asmVariableName(array.variable)
 | 
			
		||||
        val arrayVar = array.variable
 | 
			
		||||
        if(arrayVar==null) {
 | 
			
		||||
            TODO("indexed inc/dec on pointer ${array.position}")
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
        val arrayvar = asmgen.asmVariableName(arrayVar)
 | 
			
		||||
        when {
 | 
			
		||||
            array.type.isByte -> {
 | 
			
		||||
                asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
 | 
			
		||||
@@ -571,6 +1058,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                asmgen.out(if(operator=="+") "  jsr  floats.inc_var_f" else "  jsr  floats.dec_var_f")
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            array.type.isPointer -> TODO("indexed inc/dec on pointer ${array.position}")
 | 
			
		||||
            else -> return false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -884,7 +1372,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean {
 | 
			
		||||
        if (target.datatype == value.type) {
 | 
			
		||||
        if (target.datatype == value.type || (target.datatype.isPointer && value.type.isWord)) {
 | 
			
		||||
            val childDt = value.value.type
 | 
			
		||||
            if (!value.type.isFloat && (value.type.equalsSize(childDt) || value.type.largerSizeThan(childDt))) {
 | 
			
		||||
                // this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
 | 
			
		||||
@@ -910,7 +1398,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
            "-" -> asmgen.out("  sec |  sbc  $otherName")
 | 
			
		||||
            "*" -> asmgen.out("  ldy  $otherName |  jsr  prog8_math.multiply_bytes")
 | 
			
		||||
            "/" -> asmgen.out("  ldy  $otherName |  jsr  prog8_math.divmod_ub_asm |  tya")
 | 
			
		||||
            "%" -> asmgen.out("  ldy  $otherName |  jsr  prog8_math.divmod_ub_asm")
 | 
			
		||||
            "%" -> asmgen.out("  ldy  $otherName |  jsr  prog8_math.remainder_ub_asm")
 | 
			
		||||
            "<<" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                        ldy  $otherName
 | 
			
		||||
@@ -951,7 +1439,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
+""")
 | 
			
		||||
            }
 | 
			
		||||
            // pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
 | 
			
		||||
            "<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons")
 | 
			
		||||
            "<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons ${pointervar.position}")
 | 
			
		||||
            else -> throw AssemblyError("invalid operator for in-place modification $operator")
 | 
			
		||||
        }
 | 
			
		||||
        asmgen.storeAIntoZpPointerVar(sourceName, false)
 | 
			
		||||
@@ -972,7 +1460,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                    } else {
 | 
			
		||||
                        asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
 | 
			
		||||
                        asmgen.out("  sta  (+) + 1 |  sty  (+) + 2")
 | 
			
		||||
                        asmgen.out("+\tinc  ${'$'}ffff\t; modified")
 | 
			
		||||
                        asmgen.out($$"+\tinc  $ffff\t; modified")
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
 | 
			
		||||
@@ -992,7 +1480,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                    } else {
 | 
			
		||||
                        asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
 | 
			
		||||
                        asmgen.out("  sta  (+) + 1 |  sty  (+) + 2")
 | 
			
		||||
                        asmgen.out("+\tdec  ${'$'}ffff\t; modified")
 | 
			
		||||
                        asmgen.out($$"+\tdec  $ffff\t; modified")
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
 | 
			
		||||
@@ -1019,7 +1507,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
 | 
			
		||||
                if(value==0)
 | 
			
		||||
                    throw AssemblyError("division by zero")
 | 
			
		||||
                asmgen.out("  ldy  #$value |  jsr  prog8_math.divmod_ub_asm")
 | 
			
		||||
                asmgen.out("  ldy  #$value |  jsr  prog8_math.remainder_ub_asm")
 | 
			
		||||
                asmgen.storeAIntoZpPointerVar(sourceName, false)
 | 
			
		||||
            }
 | 
			
		||||
            "<<" -> {
 | 
			
		||||
@@ -1074,7 +1562,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
 | 
			
		||||
                asmgen.storeAIntoZpPointerVar(sourceName, false)
 | 
			
		||||
            }
 | 
			
		||||
            // pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
 | 
			
		||||
            "<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons")
 | 
			
		||||
            "<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons ${pointervar.position}")
 | 
			
		||||
            else -> throw AssemblyError("invalid operator for in-place modification $operator")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -1125,11 +1613,18 @@ $shortcutLabel:""")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(value is PtArrayIndexer && value.isSimple()) {
 | 
			
		||||
 | 
			
		||||
            val valueVar = value.variable
 | 
			
		||||
            if(valueVar==null) {
 | 
			
		||||
                TODO("inplace modification on pointer ${value.position}")
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // use the already existing optimized codegen for regular assignments  x += array[index]
 | 
			
		||||
            val binexpr = PtBinaryExpression(operator, dt, value.position)
 | 
			
		||||
            binexpr.add(PtIdentifier(name, dt, value.position))
 | 
			
		||||
            val arrayValue = PtArrayIndexer(value.type, value.position)
 | 
			
		||||
            arrayValue.add(value.variable)
 | 
			
		||||
            arrayValue.add(valueVar)
 | 
			
		||||
            arrayValue.add(value.index)
 | 
			
		||||
            binexpr.add(arrayValue)
 | 
			
		||||
            binexpr.parent = value
 | 
			
		||||
@@ -1190,7 +1685,7 @@ $shortcutLabel:""")
 | 
			
		||||
            "%" -> {
 | 
			
		||||
                if(signed)
 | 
			
		||||
                    throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
 | 
			
		||||
                asmgen.out("  ldy  $variable  |  jsr  prog8_math.divmod_ub_asm")
 | 
			
		||||
                asmgen.out("  ldy  $variable  |  jsr  prog8_math.remainder_ub_asm")
 | 
			
		||||
            }
 | 
			
		||||
            "<<" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
@@ -1363,7 +1858,7 @@ $shortcutLabel:""")
 | 
			
		||||
            "%" -> {
 | 
			
		||||
                if(signed)
 | 
			
		||||
                    throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
 | 
			
		||||
                asmgen.out("  tay |  lda  $variable  |  jsr  prog8_math.divmod_ub_asm")
 | 
			
		||||
                asmgen.out("  tay |  lda  $variable  |  jsr  prog8_math.remainder_ub_asm")
 | 
			
		||||
            }
 | 
			
		||||
            "<<" -> {
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
@@ -1534,7 +2029,7 @@ $shortcutLabel:""")
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    lda  $name
 | 
			
		||||
                    ldy  #$value
 | 
			
		||||
                    jsr  prog8_math.divmod_ub_asm
 | 
			
		||||
                    jsr  prog8_math.remainder_ub_asm
 | 
			
		||||
                    sta  $name""")
 | 
			
		||||
            }
 | 
			
		||||
            "<<" -> {
 | 
			
		||||
@@ -1829,7 +2324,7 @@ $shortcutLabel:""")
 | 
			
		||||
                if(value in asmgen.optimizedWordMultiplications) {
 | 
			
		||||
                    asmgen.out("  lda  $lsb |  ldy  $msb |  jsr  prog8_math.mul_word_$value |  sta  $lsb |  sty  $msb")
 | 
			
		||||
                } else {
 | 
			
		||||
                    if(block?.options?.veraFxMuls==true)
 | 
			
		||||
                    if(block?.options?.veraFxMuls==true) {
 | 
			
		||||
                        // cx16 verafx hardware mul
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            lda  $lsb
 | 
			
		||||
@@ -1840,9 +2335,10 @@ $shortcutLabel:""")
 | 
			
		||||
                            ldy  #>$value
 | 
			
		||||
                            sta  cx16.r1
 | 
			
		||||
                            sty  cx16.r1+1
 | 
			
		||||
                            jsr  verafx.muls
 | 
			
		||||
                            jsr  verafx.muls16
 | 
			
		||||
                            sta  $lsb
 | 
			
		||||
                            sty  $msb""")
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            lda  $lsb
 | 
			
		||||
@@ -1921,18 +2417,16 @@ $shortcutLabel:""")
 | 
			
		||||
                            asmgen.out("  lda  #0 |  sta  $lsb")
 | 
			
		||||
                    }
 | 
			
		||||
                    value==7 -> {
 | 
			
		||||
                        // optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift
 | 
			
		||||
                        // optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
 | 
			
		||||
                        asmgen.out("""
 | 
			
		||||
                            ; shift left 7
 | 
			
		||||
                            lsr  $msb
 | 
			
		||||
                            php     ; save carry
 | 
			
		||||
                            lda  $lsb
 | 
			
		||||
                            ror  a
 | 
			
		||||
                            sta  $msb
 | 
			
		||||
                            lda  #0
 | 
			
		||||
                            sta  $lsb
 | 
			
		||||
                            plp     ; restore carry
 | 
			
		||||
                            ror  $msb
 | 
			
		||||
                            ror  $lsb""")
 | 
			
		||||
                            ror  a
 | 
			
		||||
                            sta  $lsb""")
 | 
			
		||||
                    }
 | 
			
		||||
                    value>3 -> asmgen.out("""
 | 
			
		||||
                        ldy  #$value
 | 
			
		||||
@@ -2258,7 +2752,7 @@ $shortcutLabel:""")
 | 
			
		||||
 | 
			
		||||
    private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) {
 | 
			
		||||
        require(dt.isWord)
 | 
			
		||||
        require(valueDt.isInteger)
 | 
			
		||||
        require(valueDt.isInteger || valueDt.isPointer)
 | 
			
		||||
        when {
 | 
			
		||||
            valueDt.isByte -> {
 | 
			
		||||
                // the other variable is a BYTE type so optimize for that
 | 
			
		||||
@@ -2329,9 +2823,10 @@ $shortcutLabel:""")
 | 
			
		||||
                                ldy  $name+1
 | 
			
		||||
                                sta  cx16.r0
 | 
			
		||||
                                sty  cx16.r0+1
 | 
			
		||||
                                jsr  verafx.muls
 | 
			
		||||
                                jsr  verafx.muls16
 | 
			
		||||
                                sta  $name
 | 
			
		||||
                                sty  $name+1""")
 | 
			
		||||
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if(valueDt.isUnsignedByte) {
 | 
			
		||||
                                asmgen.out("  lda  $otherName |  sta  prog8_math.multiply_words.multiplier")
 | 
			
		||||
@@ -2468,13 +2963,13 @@ $shortcutLabel:""")
 | 
			
		||||
                    else -> throw AssemblyError("invalid operator for in-place modification $operator")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            valueDt.isWord -> {
 | 
			
		||||
            valueDt.isWord || valueDt.isPointer -> {
 | 
			
		||||
                // the value is a proper 16-bit word, so use both bytes of it.
 | 
			
		||||
                when (operator) {
 | 
			
		||||
                    "+" -> asmgen.out("  lda  $name |  clc |  adc  $otherName |  sta  $name |  lda  $name+1 |  adc  $otherName+1 |  sta  $name+1")
 | 
			
		||||
                    "-" -> asmgen.out("  lda  $name |  sec |  sbc  $otherName |  sta  $name |  lda  $name+1 |  sbc  $otherName+1 |  sta  $name+1")
 | 
			
		||||
                    "*" -> {
 | 
			
		||||
                        if(block?.options?.veraFxMuls==true)
 | 
			
		||||
                        if(block?.options?.veraFxMuls==true) {
 | 
			
		||||
                            // cx16 verafx hardware muls
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                lda  $name
 | 
			
		||||
@@ -2485,9 +2980,10 @@ $shortcutLabel:""")
 | 
			
		||||
                                ldy  $otherName+1
 | 
			
		||||
                                sta  cx16.r1
 | 
			
		||||
                                sty  cx16.r1+1
 | 
			
		||||
                                jsr  verafx.muls
 | 
			
		||||
                                jsr  verafx.muls16
 | 
			
		||||
                                sta  $name
 | 
			
		||||
                                sty  $name+1""")
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                            asmgen.out("""
 | 
			
		||||
                                lda  $otherName
 | 
			
		||||
@@ -2678,7 +3174,7 @@ $shortcutLabel:""")
 | 
			
		||||
    private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression, block: PtBlock?) {
 | 
			
		||||
        require(dt.isWord)
 | 
			
		||||
        fun multiplyVarByWordInAX() {
 | 
			
		||||
            if(block?.options?.veraFxMuls==true)
 | 
			
		||||
            if(block?.options?.veraFxMuls==true) {
 | 
			
		||||
                // cx16 verafx hardware muls
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    sta  cx16.r1
 | 
			
		||||
@@ -2687,9 +3183,10 @@ $shortcutLabel:""")
 | 
			
		||||
                    ldx  $name+1
 | 
			
		||||
                    sta  cx16.r0
 | 
			
		||||
                    stx  cx16.r0+1
 | 
			
		||||
                    jsr  verafx.muls
 | 
			
		||||
                    jsr  verafx.muls16
 | 
			
		||||
                    sta  $name
 | 
			
		||||
                    sty  $name+1""")
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                asmgen.out("""
 | 
			
		||||
                    sta  prog8_math.multiply_words.multiplier
 | 
			
		||||
@@ -2890,7 +3387,7 @@ $shortcutLabel:""")
 | 
			
		||||
                    else -> throw AssemblyError("invalid operator for in-place modification $operator")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            valueDt.isWord -> {
 | 
			
		||||
            valueDt.isWord || valueDt.isPointer -> {
 | 
			
		||||
                // the value is a proper 16-bit word, so use both bytes of it.
 | 
			
		||||
 | 
			
		||||
                if(value is PtArrayIndexer && value.isSimple()) {
 | 
			
		||||
@@ -2905,7 +3402,12 @@ $shortcutLabel:""")
 | 
			
		||||
                        "-" -> {
 | 
			
		||||
                            if(value.index.type.isByte) {
 | 
			
		||||
                                // it's an array indexed by a byte so we can use sbc array,y
 | 
			
		||||
                                val arrayname = value.variable.name
 | 
			
		||||
                                val valueVar = value.variable
 | 
			
		||||
                                if(valueVar==null) {
 | 
			
		||||
                                    TODO("inplace modification on pointer ${value.position}")
 | 
			
		||||
                                    return
 | 
			
		||||
                                }
 | 
			
		||||
                                val arrayname = valueVar.name
 | 
			
		||||
                                asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
 | 
			
		||||
                                if(value.splitWords) {
 | 
			
		||||
                                    asmgen.out("""
 | 
			
		||||
@@ -2978,7 +3480,7 @@ $shortcutLabel:""")
 | 
			
		||||
                        if(value is PtNumber && value.number<=255) {
 | 
			
		||||
                            TODO("shift a word var by ${value.number}")
 | 
			
		||||
                        } else {
 | 
			
		||||
                            throw AssemblyError("shift by a word value not supported, max is a byte")
 | 
			
		||||
                            throw AssemblyError("bit shift value can not be larger than a byte")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    "&" -> {
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -5,17 +5,21 @@ import prog8.code.core.*
 | 
			
		||||
 | 
			
		||||
internal object DummyMemsizer : IMemSizer {
 | 
			
		||||
    override fun memorySize(dt: DataType, numElements: Int?): Int {
 | 
			
		||||
        if(dt.isArray) {
 | 
			
		||||
        if(dt.isPointerArray)
 | 
			
		||||
            return 2 * numElements!!
 | 
			
		||||
        else if(dt.isArray) {
 | 
			
		||||
            require(numElements != null)
 | 
			
		||||
            return when(dt.sub) {
 | 
			
		||||
                BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
 | 
			
		||||
                BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
 | 
			
		||||
                BaseDataType.LONG -> numElements*4
 | 
			
		||||
                BaseDataType.FLOAT -> numElements*5
 | 
			
		||||
                else -> throw IllegalArgumentException("invalid sub type")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return when {
 | 
			
		||||
            dt.isByteOrBool -> 1 * (numElements ?: 1)
 | 
			
		||||
            dt.isLong -> 4 * (numElements ?: 1)
 | 
			
		||||
            dt.isFloat -> 5 * (numElements ?: 1)
 | 
			
		||||
            else -> 2 * (numElements ?: 1)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ class TestCodegen: FunSpec({
 | 
			
		||||
            noSysInit = false,
 | 
			
		||||
            romable = false,
 | 
			
		||||
            compTarget = target,
 | 
			
		||||
            compilerVersion="99.99",
 | 
			
		||||
            loadAddress = target.PROGRAM_LOAD_ADDRESS,
 | 
			
		||||
            memtopAddress = 0xffffu
 | 
			
		||||
        )
 | 
			
		||||
@@ -58,6 +59,7 @@ class TestCodegen: FunSpec({
 | 
			
		||||
            DataType.UBYTE,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -67,6 +69,7 @@ class TestCodegen: FunSpec({
 | 
			
		||||
            DataType.arrayFor(BaseDataType.UBYTE),
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            3u,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -76,6 +79,7 @@ class TestCodegen: FunSpec({
 | 
			
		||||
            DataType.arrayFor(BaseDataType.UBYTE),
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            3u,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -85,6 +89,7 @@ class TestCodegen: FunSpec({
 | 
			
		||||
            DataType.WORD,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +9,7 @@ dependencies {
 | 
			
		||||
    implementation(project(":codeGenIntermediate"))
 | 
			
		||||
    // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
			
		||||
    // implementation "org.jetbrains.kotlin:kotlin-reflect"
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sourceSets {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
}
 | 
			
		||||
@@ -11,12 +9,10 @@ dependencies {
 | 
			
		||||
    implementation(project(":intermediate"))
 | 
			
		||||
    // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
			
		||||
    // implementation "org.jetbrains.kotlin:kotlin-reflect"
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
 | 
			
		||||
 | 
			
		||||
    testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
 | 
			
		||||
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
 | 
			
		||||
    testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
 | 
			
		||||
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sourceSets {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,8 @@
 | 
			
		||||
    <orderEntry type="module" module-name="codeCore" />
 | 
			
		||||
    <orderEntry type="module" module-name="simpleAst" />
 | 
			
		||||
    <orderEntry type="module" module-name="intermediate" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
 | 
			
		||||
  </component>
 | 
			
		||||
</module>
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,8 +1,12 @@
 | 
			
		||||
package prog8.codegen.intermediate
 | 
			
		||||
 | 
			
		||||
import prog8.code.StMemorySlabBlockName
 | 
			
		||||
import prog8.code.StStructInstanceBlockName
 | 
			
		||||
import prog8.code.SymbolTable
 | 
			
		||||
import prog8.code.ast.*
 | 
			
		||||
import prog8.code.core.AssemblyError
 | 
			
		||||
import prog8.code.core.BaseDataType
 | 
			
		||||
import prog8.code.core.DataType
 | 
			
		||||
import prog8.intermediate.*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -10,7 +14,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
 | 
			
		||||
    fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        return when(call.name) {
 | 
			
		||||
            "abs__byte", "abs__word", "abs__float" -> funcAbs(call)
 | 
			
		||||
            "abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call)
 | 
			
		||||
            "cmp" -> funcCmp(call)
 | 
			
		||||
            "sgn" -> funcSgn(call)
 | 
			
		||||
            "sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
 | 
			
		||||
@@ -20,22 +24,30 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
            "callfar" -> funcCallfar(call)
 | 
			
		||||
            "callfar2" -> funcCallfar2(call)
 | 
			
		||||
            "call" -> funcCall(call)
 | 
			
		||||
            "lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
 | 
			
		||||
            "msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
 | 
			
		||||
            "msb" -> funcMsb(call)
 | 
			
		||||
            "lsb" -> funcLsb(call)
 | 
			
		||||
            "msw" -> funcMsw(call)
 | 
			
		||||
            "lsw" -> funcLsw(call)
 | 
			
		||||
            "msb" -> funcMsb(call, false)
 | 
			
		||||
            "msb__long" -> funcMsb(call, true)
 | 
			
		||||
            "lsb" -> funcLsb(call, false)
 | 
			
		||||
            "lsb__long" -> funcLsb(call, true)
 | 
			
		||||
            "memory" -> funcMemory(call)
 | 
			
		||||
            "peek" -> funcPeek(call, IRDataType.BYTE)
 | 
			
		||||
            "peekbool" -> funcPeek(call, IRDataType.BYTE)
 | 
			
		||||
            "peekw" -> funcPeek(call, IRDataType.WORD)
 | 
			
		||||
            "peekl" -> funcPeek(call, IRDataType.LONG)
 | 
			
		||||
            "peekf" -> funcPeek(call, IRDataType.FLOAT)
 | 
			
		||||
            "poke" -> funcPoke(call, IRDataType.BYTE)
 | 
			
		||||
            "pokebool" -> funcPoke(call, IRDataType.BYTE)
 | 
			
		||||
            "pokebowl" -> funcPoke(call, IRDataType.BYTE)
 | 
			
		||||
            "pokew" -> funcPoke(call, IRDataType.WORD)
 | 
			
		||||
            "pokel" -> funcPoke(call, IRDataType.LONG)
 | 
			
		||||
            "pokef" -> funcPoke(call, IRDataType.FLOAT)
 | 
			
		||||
            "pokemon" -> funcPokemon(call)
 | 
			
		||||
            "mkword" -> funcMkword(call)
 | 
			
		||||
            "clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
 | 
			
		||||
            "min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
 | 
			
		||||
            "max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
 | 
			
		||||
            "mklong", "mklong2" -> funcMklong(call)
 | 
			
		||||
            "clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword", "clamp__long" -> funcClamp(call)
 | 
			
		||||
            "min__byte", "min__ubyte", "min__word", "min__uword", "min__long" -> funcMin(call)
 | 
			
		||||
            "max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(call)
 | 
			
		||||
            "setlsb" -> funcSetLsbMsb(call, false)
 | 
			
		||||
            "setmsb" -> funcSetLsbMsb(call, true)
 | 
			
		||||
            "rol" -> funcRolRor(call)
 | 
			
		||||
@@ -45,6 +57,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
            "prog8_lib_stringcompare" -> funcStringCompare(call)
 | 
			
		||||
            "prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
 | 
			
		||||
            "prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
 | 
			
		||||
            "prog8_lib_structalloc" -> funcStructAlloc(call)
 | 
			
		||||
            "sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
 | 
			
		||||
            "offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
 | 
			
		||||
            else -> throw AssemblyError("missing builtinfunc for ${call.name}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -194,6 +209,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
                result += IRCodeChunk(notNegativeLabel, null)
 | 
			
		||||
                return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                val notNegativeLabel = codeGen.createLabelName()
 | 
			
		||||
                val compareReg = codeGen.registers.next(IRDataType.LONG)
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.LOADR, IRDataType.LONG, reg1=compareReg, reg2=tr.resultReg)
 | 
			
		||||
                    it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
 | 
			
		||||
                    it += IRInstruction(Opcode.NEG, IRDataType.LONG, reg1=tr.resultReg)
 | 
			
		||||
                }
 | 
			
		||||
                result += IRCodeChunk(notNegativeLabel, null)
 | 
			
		||||
                return ExpressionCodeResult(result, IRDataType.LONG, tr.resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.FLOAT -> {
 | 
			
		||||
                val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
 | 
			
		||||
@@ -206,7 +232,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
    private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val tr = exprGen.translateExpression(call.args.single())
 | 
			
		||||
        val resultReg = codeGen.registers.next(tr.dt)
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
 | 
			
		||||
        if(tr.dt==IRDataType.FLOAT) {
 | 
			
		||||
            addToResult(result, tr, -1, tr.resultFpReg)
 | 
			
		||||
@@ -243,6 +269,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
                }
 | 
			
		||||
                return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
                val resultReg = codeGen.registers.next(IRDataType.WORD)     // sqrt of a long still produces just a word result
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.SQRT, IRDataType.LONG, reg1=resultReg, reg2=tr.resultReg)
 | 
			
		||||
                }
 | 
			
		||||
                return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.FLOAT -> {
 | 
			
		||||
                addToResult(result, tr, -1, tr.resultFpReg)
 | 
			
		||||
                val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
@@ -273,6 +307,52 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
        return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMklong(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.LONG)
 | 
			
		||||
        if(call.args.size==2) {
 | 
			
		||||
            // mklong2(word, word)
 | 
			
		||||
            if((call.args[0] as? PtNumber)?.number == 0.0) {
 | 
			
		||||
                // msw is 0, use EXT
 | 
			
		||||
                val lswTr = exprGen.translateExpression(call.args[1])
 | 
			
		||||
                addToResult(result, lswTr, lswTr.resultReg, -1)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.EXT, IRDataType.WORD, reg1=resultReg, reg2 = lswTr.resultReg), null)
 | 
			
		||||
            } else {
 | 
			
		||||
                val mswTr = exprGen.translateExpression(call.args[0])
 | 
			
		||||
                addToResult(result, mswTr, mswTr.resultReg, -1)
 | 
			
		||||
                val lswTr = exprGen.translateExpression(call.args[1])
 | 
			
		||||
                addToResult(result, lswTr, lswTr.resultReg, -1)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.WORD, reg1=resultReg, reg2 = mswTr.resultReg, reg3 = lswTr.resultReg), null)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // mklong(msb, b3, b2, lsb)
 | 
			
		||||
            if((call.args[0] as? PtNumber)?.number == 0.0 && (call.args[1] as? PtNumber)?.number == 0.0 && (call.args[2] as? PtNumber)?.number == 0.0) {
 | 
			
		||||
                // use EXT.b + EXT.w
 | 
			
		||||
                val lsbTr = exprGen.translateExpression(call.args[3])
 | 
			
		||||
                addToResult(result, lsbTr, lsbTr.resultReg, -1)
 | 
			
		||||
                val wordReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=wordReg, reg2 = lsbTr.resultReg), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.EXT, IRDataType.WORD, reg1=resultReg, reg2 = wordReg), null)
 | 
			
		||||
            } else {
 | 
			
		||||
                val msbTr = exprGen.translateExpression(call.args[0])
 | 
			
		||||
                val b2Tr = exprGen.translateExpression(call.args[1])
 | 
			
		||||
                val b1Tr = exprGen.translateExpression(call.args[2])
 | 
			
		||||
                val lsbTr = exprGen.translateExpression(call.args[3])
 | 
			
		||||
                addToResult(result, msbTr, msbTr.resultReg, -1)
 | 
			
		||||
                addToResult(result, b2Tr, b2Tr.resultReg, -1)
 | 
			
		||||
                addToResult(result, b1Tr, b1Tr.resultReg, -1)
 | 
			
		||||
                addToResult(result, lsbTr, lsbTr.resultReg, -1)
 | 
			
		||||
                val lswReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                val mswReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=mswReg, reg2 = msbTr.resultReg, reg3 = b2Tr.resultReg), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=lswReg, reg2 = b1Tr.resultReg, reg3 = lsbTr.resultReg), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.WORD, reg1=resultReg, reg2 = mswReg, reg3 = lswReg), null)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return ExpressionCodeResult(result, IRDataType.LONG, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val type = irType(call.type)
 | 
			
		||||
@@ -297,6 +377,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
                BaseDataType.BYTE -> IMSyscall.CLAMP_BYTE
 | 
			
		||||
                BaseDataType.UWORD -> IMSyscall.CLAMP_UWORD
 | 
			
		||||
                BaseDataType.WORD -> IMSyscall.CLAMP_WORD
 | 
			
		||||
                BaseDataType.LONG -> IMSyscall.CLAMP_LONG
 | 
			
		||||
                else -> throw AssemblyError("invalid dt")
 | 
			
		||||
            }
 | 
			
		||||
            result += codeGen.makeSyscall(syscall, listOf(
 | 
			
		||||
@@ -493,30 +574,62 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
        val name = (call.args[0] as PtString).value
 | 
			
		||||
        val code = IRCodeChunk(null, null)
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
        code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
 | 
			
		||||
        return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
 | 
			
		||||
        code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabBlockName.memory_$name")
 | 
			
		||||
        return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
    private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val code = IRCodeChunk(null, null)
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
        val labelname = SymbolTable.labelnameForStructInstance(call)
 | 
			
		||||
        code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "${StStructInstanceBlockName}.$labelname")
 | 
			
		||||
        return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcLsb(call: PtBuiltinFunctionCall, fromLong: Boolean): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val tr = exprGen.translateExpression(call.args.single())
 | 
			
		||||
        addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
        addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        if(fromLong)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        else
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
 | 
			
		||||
        return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
    private fun funcLsw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val tr = exprGen.translateExpression(call.args.single())
 | 
			
		||||
        addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
        addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMsb(call: PtBuiltinFunctionCall, fromLong: Boolean): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val tr = exprGen.translateExpression(call.args.single())
 | 
			
		||||
        addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
        addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        if(fromLong)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.MSIGB, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        else
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.MSIGB, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
 | 
			
		||||
        return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcMsw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val tr = exprGen.translateExpression(call.args.single())
 | 
			
		||||
        addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
        addInstr(result, IRInstruction(Opcode.MSIGW, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
 | 
			
		||||
        return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val arg = call.args[0]
 | 
			
		||||
@@ -544,7 +657,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
        val arr = (arg as? PtArrayIndexer)
 | 
			
		||||
        val index = arr?.index?.asConstInteger()
 | 
			
		||||
        if(arr!=null && index!=null) {
 | 
			
		||||
            val variable = arr.variable.name
 | 
			
		||||
            if(arr.variable==null)
 | 
			
		||||
                TODO("support for ptr indexing ${arr.position}")
 | 
			
		||||
            val variable = arr.variable!!.name
 | 
			
		||||
            if(arr.splitWords) {
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    when(opcodeMemAndReg.first) {
 | 
			
		||||
@@ -613,7 +728,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
                if(target.splitWords) {
 | 
			
		||||
                    // lsb/msb in split arrays, element index 'size' is always 1
 | 
			
		||||
                    val constIndex = target.index.asConstInteger()
 | 
			
		||||
                    val varName = target.variable.name + if(msb) "_msb" else "_lsb"
 | 
			
		||||
                    if(target.variable==null)
 | 
			
		||||
                        TODO("support for ptr indexing ${target.position}")
 | 
			
		||||
                    val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
 | 
			
		||||
                    if(isConstZeroValue) {
 | 
			
		||||
                        if(constIndex!=null) {
 | 
			
		||||
                            val offsetReg = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
@@ -647,6 +764,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    val targetVariable = target.variable ?: TODO("support for ptr indexing ${target.position}")
 | 
			
		||||
 | 
			
		||||
                    val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
 | 
			
		||||
                    val constIndex = target.index.asConstInteger()
 | 
			
		||||
                    if(isConstZeroValue) {
 | 
			
		||||
@@ -655,17 +774,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
                            val offset = eltSize*constIndex + if(msb) 1 else 0
 | 
			
		||||
                            result += IRCodeChunk(null, null).also {
 | 
			
		||||
                                it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            val indexTr = exprGen.translateExpression(target.index)
 | 
			
		||||
                            addToResult(result, indexTr, indexTr.resultReg, -1)
 | 
			
		||||
                            result += IRCodeChunk(null, null).also {
 | 
			
		||||
                                if(eltSize>1)
 | 
			
		||||
                                    it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
 | 
			
		||||
                                    it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
 | 
			
		||||
                                if(msb)
 | 
			
		||||
                                    it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
@@ -676,17 +795,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
 | 
			
		||||
                            val offset = eltSize*constIndex + if(msb) 1 else 0
 | 
			
		||||
                            result += IRCodeChunk(null, null).also {
 | 
			
		||||
                                it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            val indexTr = exprGen.translateExpression(target.index)
 | 
			
		||||
                            addToResult(result, indexTr, indexTr.resultReg, -1)
 | 
			
		||||
                            result += IRCodeChunk(null, null).also {
 | 
			
		||||
                                if(eltSize>1)
 | 
			
		||||
                                    it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
 | 
			
		||||
                                    it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
 | 
			
		||||
                                if(msb)
 | 
			
		||||
                                    it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
 | 
			
		||||
                                it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,8 @@
 | 
			
		||||
package prog8.codegen.intermediate
 | 
			
		||||
 | 
			
		||||
import prog8.code.StExtSub
 | 
			
		||||
import prog8.code.StNode
 | 
			
		||||
import prog8.code.StNodeType
 | 
			
		||||
import prog8.code.StSub
 | 
			
		||||
import prog8.code.*
 | 
			
		||||
import prog8.code.ast.*
 | 
			
		||||
import prog8.code.core.AssemblyError
 | 
			
		||||
import prog8.code.core.BaseDataType
 | 
			
		||||
import prog8.code.core.Cx16VirtualRegisters
 | 
			
		||||
import prog8.code.core.Statusflag
 | 
			
		||||
import prog8.code.core.*
 | 
			
		||||
import prog8.intermediate.*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -89,15 +83,111 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
            is PtArrayIndexer -> translate(expr)
 | 
			
		||||
            is PtBinaryExpression -> translate(expr)
 | 
			
		||||
            is PtIfExpression -> translate(expr)
 | 
			
		||||
            is PtBranchCondExpression -> translate(expr)
 | 
			
		||||
            is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
 | 
			
		||||
            is PtFunctionCall -> translate(expr)
 | 
			
		||||
            is PtContainmentCheck -> translate(expr)
 | 
			
		||||
            is PtPointerDeref -> translate(expr)
 | 
			
		||||
            is PtRange,
 | 
			
		||||
            is PtArray,
 | 
			
		||||
            is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(deref: PtPointerDeref): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        var pointerReg: Int
 | 
			
		||||
 | 
			
		||||
        if(deref.startpointer.type.isStructInstance) {
 | 
			
		||||
            TODO("translate structinstance deref??? ${deref.position}")
 | 
			
		||||
/*
 | 
			
		||||
            val arrayIndexer = deref.startpointer as? PtArrayIndexer
 | 
			
		||||
            if(arrayIndexer==null)
 | 
			
		||||
                throw AssemblyError("when start pointer is struct instance, array indexer is expected PTR[x]")
 | 
			
		||||
            // first evaluate the adress of POINTER[x].a
 | 
			
		||||
            // which is: value in pointer + x*sizeof(struct) + offsetof(a)
 | 
			
		||||
            // then use traverseDerefChainToCalculateFinalAddress on b.c.d.field
 | 
			
		||||
            val struct = deref.startpointer.type.subType as StStruct
 | 
			
		||||
            val chain = ArrayDeque(deref.chain)
 | 
			
		||||
            val firstField = struct.getField(chain.removeFirst(), codeGen.program.memsizer)
 | 
			
		||||
            val pointerTr = translateExpression(arrayIndexer.variable)
 | 
			
		||||
            result += pointerTr.chunks
 | 
			
		||||
            pointerReg = pointerTr.resultReg
 | 
			
		||||
            val constIndex = arrayIndexer.index.asConstInteger()
 | 
			
		||||
            if(constIndex!=null) {
 | 
			
		||||
                val offset = constIndex * struct.size.toInt()
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
 | 
			
		||||
            } else {
 | 
			
		||||
                val indexTr = translateExpression(arrayIndexer.index)
 | 
			
		||||
                result += indexTr.chunks
 | 
			
		||||
                // multiply the index by the size of the struct and add that to the pointer, then add the offset of the field,
 | 
			
		||||
                // and retrieve the pointer value that is stored there, or the actual value if it's a pointer to simple type
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    val indexReg: Int
 | 
			
		||||
                    if(arrayIndexer.index.type.isByte) {
 | 
			
		||||
                        // extend array index to word
 | 
			
		||||
                        indexReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                        it += IRInstruction(Opcode.EXT, IRDataType.BYTE, indexReg, indexTr.resultReg)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        indexReg = indexTr.resultReg
 | 
			
		||||
                    }
 | 
			
		||||
                    it += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
 | 
			
		||||
                    it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result += IRCodeChunk(null, null).also {
 | 
			
		||||
                it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = firstField.second.toInt())
 | 
			
		||||
                if (firstField.first.isPointer) {
 | 
			
		||||
                    // get the address stored in the pointer and use that for the rest of the chain
 | 
			
		||||
                    // LOADI has an exception to allo reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
 | 
			
		||||
                    it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
 | 
			
		||||
                } else {
 | 
			
		||||
                    require(chain.isEmpty())
 | 
			
		||||
                    // it's a pointer to a simple value, so keep the pointer as-is
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // now use traverseDerefChainToCalculateFinalAddress on b.c.d and finally field  or on b.c.d.field if field isn't a field.
 | 
			
		||||
            val derefField = if(deref.type.isPointer) null else chain.removeLastOrNull()
 | 
			
		||||
            actualDeref = PtPointerDeref(deref.type, chain, derefField, deref.position)
 | 
			
		||||
            actualDeref.add(arrayIndexer.variable)
 | 
			
		||||
*/
 | 
			
		||||
        } else {
 | 
			
		||||
            val tr = translateExpression(deref.startpointer)
 | 
			
		||||
            result += tr.chunks
 | 
			
		||||
            pointerReg = tr.resultReg
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
 | 
			
		||||
        result += instructions
 | 
			
		||||
        if(offset<=0u) {
 | 
			
		||||
            val irdt = irType(deref.type)
 | 
			
		||||
            return if(deref.type.isFloat) {
 | 
			
		||||
                val resultReg = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
 | 
			
		||||
                ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
 | 
			
		||||
            } else {
 | 
			
		||||
                val resultReg = codeGen.registers.next(irdt)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADI, irdt, reg1 = resultReg, reg2 = pointerReg), null)
 | 
			
		||||
                ExpressionCodeResult(result, irdt, resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // load field with offset
 | 
			
		||||
        return if(deref.type.isFloat) {
 | 
			
		||||
            val resultReg = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.LOADFIELD, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg, immediate = offset.toInt()), null)
 | 
			
		||||
            ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
 | 
			
		||||
        } else {
 | 
			
		||||
            val irdt = irType(deref.type)
 | 
			
		||||
            val resultReg = codeGen.registers.next(irdt)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.LOADFIELD, irdt, reg1 = resultReg, reg2 = pointerReg, immediate = offset.toInt()), null)
 | 
			
		||||
            ExpressionCodeResult(result, irdt, resultReg, -1)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
 | 
			
		||||
 | 
			
		||||
        if((ifExpr.condition as? PtPrefix)?.operator=="not")
 | 
			
		||||
@@ -164,48 +254,142 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(branchExpr: PtBranchCondExpression): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val trueTr = translateExpression(branchExpr.truevalue)
 | 
			
		||||
        val falseTr = translateExpression(branchExpr.falsevalue)
 | 
			
		||||
        val trueLabel = codeGen.createLabelName()
 | 
			
		||||
        val endLabel = codeGen.createLabelName()
 | 
			
		||||
        val irDt = irType(branchExpr.type)
 | 
			
		||||
 | 
			
		||||
        if(branchExpr.condition==BranchCondition.CC && irDt==IRDataType.BYTE) {
 | 
			
		||||
            if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
 | 
			
		||||
                result.add(IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
 | 
			
		||||
                    it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
 | 
			
		||||
                })
 | 
			
		||||
                return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
            else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
 | 
			
		||||
                result.add(IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
 | 
			
		||||
                    it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
 | 
			
		||||
                    it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
 | 
			
		||||
                })
 | 
			
		||||
                return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if(branchExpr.condition==BranchCondition.CS) {
 | 
			
		||||
            if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
 | 
			
		||||
                result.add(IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
 | 
			
		||||
                    it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
 | 
			
		||||
                    it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
 | 
			
		||||
                })
 | 
			
		||||
                return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
            else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
 | 
			
		||||
                result.add(IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
 | 
			
		||||
                    it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
 | 
			
		||||
                })
 | 
			
		||||
                return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val branchInstr = codeGen.IRBranchInstr(branchExpr.condition, trueLabel)
 | 
			
		||||
        addInstr(result, branchInstr, null)
 | 
			
		||||
 | 
			
		||||
        if (irDt != IRDataType.FLOAT) {
 | 
			
		||||
            addToResult(result, falseTr, trueTr.resultReg, -1)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
 | 
			
		||||
            result += IRCodeChunk(trueLabel, null)
 | 
			
		||||
            addToResult(result, trueTr, trueTr.resultReg, -1)
 | 
			
		||||
            result += IRCodeChunk(endLabel, null)
 | 
			
		||||
            return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
 | 
			
		||||
        } else {
 | 
			
		||||
            addToResult(result, falseTr, -1, trueTr.resultFpReg)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
 | 
			
		||||
            result += IRCodeChunk(trueLabel, null)
 | 
			
		||||
            addToResult(result, trueTr, -1, trueTr.resultFpReg)
 | 
			
		||||
            result += IRCodeChunk(endLabel, null)
 | 
			
		||||
            return ExpressionCodeResult(result, irDt, -1, trueTr.resultFpReg)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(expr: PtAddressOf): ExpressionCodeResult {
 | 
			
		||||
        val vmDt = irType(expr.type)
 | 
			
		||||
        val symbol = expr.identifier.name
 | 
			
		||||
        // note: LOAD <symbol>  gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val resultRegister = codeGen.registers.next(vmDt)
 | 
			
		||||
        val identifier = expr.identifier
 | 
			
		||||
 | 
			
		||||
        fun loadAddressOfArrayLabel(reg: Int) {
 | 
			
		||||
            if (expr.isMsbForSplitArray) {
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null)
 | 
			
		||||
            } else if (expr.identifier.type.isSplitWordArray) {
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
 | 
			
		||||
            } else if (identifier!!.type.isSplitWordArray) {
 | 
			
		||||
                // the _lsb split array comes first in memory
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
 | 
			
		||||
            } else
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(expr.isFromArrayElement) {
 | 
			
		||||
            val indexTr = translateExpression(expr.arrayIndexExpr!!)
 | 
			
		||||
            addToResult(result, indexTr, indexTr.resultReg, -1)
 | 
			
		||||
            val indexWordReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null)
 | 
			
		||||
            if(expr.identifier.type.isUnsignedWord) {
 | 
			
		||||
            val indexWordReg = if(indexTr.dt==IRDataType.BYTE) {
 | 
			
		||||
                val ixWord = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
 | 
			
		||||
                ixWord
 | 
			
		||||
            } else indexTr.resultReg
 | 
			
		||||
            val resultRegister = codeGen.registers.next(vmDt)
 | 
			
		||||
            if(identifier!!.type.isUnsignedWord) {
 | 
			
		||||
                require(!expr.isMsbForSplitArray)
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
 | 
			
		||||
                    val ptr = codeGen.symbolTable.lookup(identifier.name)
 | 
			
		||||
                    it += if(ptr is StConstant)
 | 
			
		||||
                        IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt())
 | 
			
		||||
                    else
 | 
			
		||||
                        IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
 | 
			
		||||
                    it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
 | 
			
		||||
                }
 | 
			
		||||
            } else if(identifier.type.isPointer) {
 | 
			
		||||
                // apply pointer arithmetic for the array indexing
 | 
			
		||||
                val eltSize = if(identifier.type.sub!=null)
 | 
			
		||||
                        codeGen.program.memsizer.memorySize(identifier.type.sub!!)
 | 
			
		||||
                    else
 | 
			
		||||
                        identifier.type.subType!!.memsize(codeGen.program.memsizer)
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name), null)
 | 
			
		||||
                    if (eltSize > 1) {
 | 
			
		||||
                        it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
 | 
			
		||||
                    }
 | 
			
		||||
                    it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
 | 
			
		||||
                // regular array indexing
 | 
			
		||||
                val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    loadAddressOfArrayLabel(resultRegister)
 | 
			
		||||
                    if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
 | 
			
		||||
                        it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
 | 
			
		||||
                    if (eltSize > 1 && !identifier.type.isSplitWordArray) {
 | 
			
		||||
                        it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
 | 
			
		||||
                    }
 | 
			
		||||
                    it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
 | 
			
		||||
                    it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            loadAddressOfArrayLabel(resultRegister)
 | 
			
		||||
        }
 | 
			
		||||
            return ExpressionCodeResult(result, vmDt, resultRegister, -1)
 | 
			
		||||
        } else if(expr.identifier!=null ) {
 | 
			
		||||
            val resultRegister = codeGen.registers.next(vmDt)
 | 
			
		||||
            loadAddressOfArrayLabel(resultRegister)
 | 
			
		||||
            return ExpressionCodeResult(result, vmDt, resultRegister, -1)
 | 
			
		||||
        } else {
 | 
			
		||||
            require(vmDt==IRDataType.WORD)
 | 
			
		||||
            val pointerTr = translateExpression(expr.dereference!!.startpointer)
 | 
			
		||||
            result += pointerTr.chunks
 | 
			
		||||
            val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
 | 
			
		||||
            result += instructions
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerTr.resultReg, immediate = offset.toInt()), null)
 | 
			
		||||
            return ExpressionCodeResult(result, vmDt, pointerTr.resultReg, -1)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
 | 
			
		||||
@@ -219,21 +403,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val ptrWithOffset = mem.address as? PtBinaryExpression
 | 
			
		||||
        if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
 | 
			
		||||
            if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
 | 
			
		||||
                // LOADIX only works with byte index.
 | 
			
		||||
        if(ptrWithOffset!=null) {
 | 
			
		||||
            if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
 | 
			
		||||
                val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
 | 
			
		||||
                if(constOffset in 0..255) {
 | 
			
		||||
                    val ptrName = (ptrWithOffset.left as PtIdentifier).name
 | 
			
		||||
                val offsetReg = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
                    val pointerReg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                    result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
 | 
			
		||||
                    it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
 | 
			
		||||
                        it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = pointerReg, labelSymbol = ptrName)
 | 
			
		||||
                        it += IRInstruction(Opcode.LOADFIELD, IRDataType.BYTE, reg1=resultRegister, reg2=pointerReg,  immediate = constOffset)
 | 
			
		||||
                    }
 | 
			
		||||
                    return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
 | 
			
		||||
        if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
 | 
			
		||||
            && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
 | 
			
		||||
            val offsetTypecast = ptrWithOffset.right as? PtTypeCast
 | 
			
		||||
            if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
 | 
			
		||||
                // LOADIX only works with byte index.
 | 
			
		||||
                val tr = if(offsetTypecast?.value?.type?.isByte==true)
 | 
			
		||||
                    translateExpression(offsetTypecast.value)
 | 
			
		||||
@@ -244,6 +428,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
 | 
			
		||||
                return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val tr = translateExpression(mem.address)
 | 
			
		||||
        addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
@@ -261,7 +446,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                else (it as PtNumber).number.toInt()
 | 
			
		||||
            }
 | 
			
		||||
            when {
 | 
			
		||||
                elementDt.isIntegerOrBool -> {
 | 
			
		||||
                elementDt.isWordOrByteOrBool -> {
 | 
			
		||||
                    if (elementDt.isByteOrBool) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
 | 
			
		||||
                    if (elementDt.isWord) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
 | 
			
		||||
                    val gottemLabel = codeGen.createLabelName()
 | 
			
		||||
@@ -342,11 +527,30 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
 | 
			
		||||
        if(arrayIx.type.isStructInstance)
 | 
			
		||||
            throw AssemblyError("cannot translate POINTER[x] resulting in a struct instance; this is likely part of a larger expression POINTER[x].field and that has to be translated earlier as a whole")
 | 
			
		||||
 | 
			
		||||
        val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
 | 
			
		||||
        val vmDt = irType(arrayIx.type)
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val arrayVarSymbol = arrayIx.variable.name
 | 
			
		||||
        val arrayVar = arrayIx.variable
 | 
			
		||||
        if(arrayVar==null) {
 | 
			
		||||
            val pointerTr = translateExpression(arrayIx.pointerderef!!)
 | 
			
		||||
            result += pointerTr.chunks
 | 
			
		||||
            val pointerReg = pointerTr.resultReg
 | 
			
		||||
            return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
 | 
			
		||||
        }
 | 
			
		||||
        if(arrayVar.type.isPointer) {
 | 
			
		||||
            val pointerTr = translateExpression(arrayVar)
 | 
			
		||||
            result += pointerTr.chunks
 | 
			
		||||
            val pointerReg = pointerTr.resultReg
 | 
			
		||||
            return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        require(!arrayVar.type.isPointer) { "only regular array indexing here ${arrayIx.position}" }
 | 
			
		||||
        var resultRegister = -1
 | 
			
		||||
        var resultFpRegister = -1
 | 
			
		||||
        val arrayVarSymbol = arrayVar.name
 | 
			
		||||
 | 
			
		||||
        if(arrayIx.splitWords) {
 | 
			
		||||
            require(vmDt==IRDataType.WORD)
 | 
			
		||||
@@ -373,9 +577,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
            return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var resultFpRegister = -1
 | 
			
		||||
        if(arrayIx.index is PtNumber) {
 | 
			
		||||
            val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
 | 
			
		||||
        fun indexByNumber(index: Int) {
 | 
			
		||||
            val memOffset = index * eltSize
 | 
			
		||||
            if(vmDt==IRDataType.FLOAT) {
 | 
			
		||||
                resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
 | 
			
		||||
@@ -384,23 +587,61 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                resultRegister = codeGen.registers.next(vmDt)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            val tr = translateExpression(arrayIx.index)
 | 
			
		||||
            addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
            if(eltSize>1)
 | 
			
		||||
                result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun indexByExpression() {
 | 
			
		||||
            val (code, indexByteReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, false, arrayIx.splitWords)
 | 
			
		||||
            result += code
 | 
			
		||||
            if(vmDt==IRDataType.FLOAT) {
 | 
			
		||||
                resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteReg, labelSymbol = arrayVarSymbol), null)
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                resultRegister = codeGen.registers.next(vmDt)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteReg, labelSymbol = arrayVarSymbol), null)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(arrayIx.index is PtNumber)
 | 
			
		||||
            indexByNumber((arrayIx.index as PtNumber).number.toInt())
 | 
			
		||||
        else
 | 
			
		||||
            indexByExpression()
 | 
			
		||||
        return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translatePointerIndexing(
 | 
			
		||||
        result: MutableList<IRCodeChunkBase>,
 | 
			
		||||
        pointerReg: Int,
 | 
			
		||||
        index: PtExpression,
 | 
			
		||||
        eltSize: Int,
 | 
			
		||||
        resultDt: IRDataType
 | 
			
		||||
    ): ExpressionCodeResult {
 | 
			
		||||
        var resultRegister = -1
 | 
			
		||||
        var resultFpRegister = -1
 | 
			
		||||
 | 
			
		||||
        if(index is PtNumber) {
 | 
			
		||||
            val memOffset = eltSize * index.number.toInt()
 | 
			
		||||
            if(memOffset>0)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = memOffset), null)
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            val (code, indexWordReg) = codeGen.loadIndexReg(index, eltSize, true, false)
 | 
			
		||||
            result += code
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexWordReg), null)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(resultDt==IRDataType.FLOAT) {
 | 
			
		||||
            resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            resultRegister = codeGen.registers.next(resultDt)
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.LOADI, resultDt, reg1=resultRegister, reg2=pointerReg), null)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ExpressionCodeResult(result, resultDt, resultRegister, resultFpRegister)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(expr: PtPrefix): ExpressionCodeResult {
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val tr = translateExpression(expr.value)
 | 
			
		||||
@@ -441,9 +682,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                        actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
 | 
			
		||||
                    }
 | 
			
		||||
                    valueDt.isWord -> {
 | 
			
		||||
                    valueDt.isWord || valueDt.isPointer -> {
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                        actualResultReg2 =loadStatusAsBooleanResult(Opcode.BSTNE, result)
 | 
			
		||||
                        actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
 | 
			
		||||
                    }
 | 
			
		||||
                    valueDt.isLong -> {
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.LONG, reg1=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                        actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
 | 
			
		||||
                    }
 | 
			
		||||
                    valueDt.isFloat -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
@@ -452,7 +697,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                            it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type")
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type ${cast.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.UBYTE -> {
 | 
			
		||||
@@ -462,13 +707,17 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.UWORD, BaseDataType.WORD -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.LONG -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.FLOAT -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type")
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type ${cast.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.BYTE -> {
 | 
			
		||||
@@ -478,13 +727,17 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.UWORD, BaseDataType.WORD -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.LONG -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.FLOAT -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type")
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type ${cast.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.UWORD -> {
 | 
			
		||||
@@ -502,11 +755,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    BaseDataType.WORD -> {
 | 
			
		||||
                        actualResultReg2 = tr.resultReg
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.LONG -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.FLOAT -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type")
 | 
			
		||||
                    BaseDataType.POINTER -> {
 | 
			
		||||
                        actualResultReg2 = tr.resultReg
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type ${cast.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.WORD -> {
 | 
			
		||||
@@ -524,11 +784,42 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    BaseDataType.UWORD -> {
 | 
			
		||||
                        actualResultReg2 = tr.resultReg
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.LONG -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.FLOAT -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type")
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type ${cast.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.LONG -> {
 | 
			
		||||
                when(valueDt.base) {
 | 
			
		||||
                    BaseDataType.UBYTE, BaseDataType.BOOL -> {
 | 
			
		||||
                        // ubyte to long: double sign extend
 | 
			
		||||
                        val wordreg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = wordreg, reg2=tr.resultReg), null)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=wordreg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.BYTE -> {
 | 
			
		||||
                        // byte to long: double sign extend
 | 
			
		||||
                        val wordreg = codeGen.registers.next(IRDataType.WORD)
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = wordreg, reg2=tr.resultReg), null)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=wordreg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.UWORD, BaseDataType.POINTER -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    BaseDataType.WORD -> {
 | 
			
		||||
                        actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast $valueDt to long ${cast.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.FLOAT -> {
 | 
			
		||||
@@ -546,23 +837,34 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    BaseDataType.WORD -> {
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
 | 
			
		||||
                    }
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type")
 | 
			
		||||
                    else -> throw AssemblyError("weird cast value type ${cast.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird cast type")
 | 
			
		||||
            BaseDataType.POINTER -> {
 | 
			
		||||
                require(valueDt.isUnsignedWord || valueDt.isPointer)
 | 
			
		||||
                actualResultReg2 = tr.resultReg
 | 
			
		||||
                // no further conversion required, pointers are all just uwords
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.ARRAY_POINTER -> {
 | 
			
		||||
                TODO("typecast to array of pointers $valueDt -> ${cast.type}  ${cast.position}")
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird cast value type ${cast.position}")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
 | 
			
		||||
        val vmDt = irType(binExpr.left.type)
 | 
			
		||||
        val signed = binExpr.left.type.isSigned
 | 
			
		||||
        return when(binExpr.operator) {
 | 
			
		||||
        if(binExpr.operator==".") {
 | 
			
		||||
            return operatorDereference(binExpr)       // eww, nasty, would rather not have any such expressions anymore
 | 
			
		||||
        } else {
 | 
			
		||||
            val vmDt = irType(binExpr.left.type)
 | 
			
		||||
            return when (binExpr.operator) {
 | 
			
		||||
                "+" -> operatorPlus(binExpr, vmDt)
 | 
			
		||||
                "-" -> operatorMinus(binExpr, vmDt)
 | 
			
		||||
            "*" -> operatorMultiply(binExpr, vmDt)
 | 
			
		||||
            "/" -> operatorDivide(binExpr, vmDt, signed)
 | 
			
		||||
                "*" -> operatorMultiply(binExpr, binExpr.left.type)
 | 
			
		||||
                "/" -> operatorDivide(binExpr, binExpr.left.type)
 | 
			
		||||
                "%" -> operatorModulo(binExpr, vmDt)
 | 
			
		||||
                "|" -> operatorOr(binExpr, vmDt, true)
 | 
			
		||||
                "&" -> operatorAnd(binExpr, vmDt, true)
 | 
			
		||||
@@ -580,15 +882,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                else -> throw AssemblyError("weird operator ${binExpr.operator}")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
 | 
			
		||||
        val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
 | 
			
		||||
 | 
			
		||||
        if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
 | 
			
		||||
        if(callTarget.scopedNameString in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
 | 
			
		||||
            // special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
 | 
			
		||||
            return translateStackFunctions(fcall, callTarget)
 | 
			
		||||
        }
 | 
			
		||||
        when(callTarget.scopedName) {
 | 
			
		||||
        when(callTarget.scopedNameString) {
 | 
			
		||||
            "sys.clear_carry" -> {
 | 
			
		||||
                val chunk = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
                addInstr(chunk, IRInstruction(Opcode.CLC), null)
 | 
			
		||||
@@ -637,7 +940,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // return value(s)
 | 
			
		||||
                // TODO: for current implemenation of the call convention in case of multiple return values,
 | 
			
		||||
                // TODO: for current implementation of the call convention in case of multiple return values,
 | 
			
		||||
                // a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
 | 
			
		||||
                // So we use an empty list to avoid confusion here.   This may change in a future version.
 | 
			
		||||
                val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
 | 
			
		||||
@@ -768,6 +1071,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                else
 | 
			
		||||
                    ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
 | 
			
		||||
            }
 | 
			
		||||
            is StStruct -> {
 | 
			
		||||
                throw AssemblyError("stray struct constructor should have been removed (normally it can only occur as initialization expression for a pointer variable)")
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                if(callTarget.type == StNodeType.LABEL) {
 | 
			
		||||
                    require(fcall.void)
 | 
			
		||||
@@ -777,7 +1083,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    throw AssemblyError("invalid node type")
 | 
			
		||||
                    throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -810,7 +1116,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
 | 
			
		||||
    private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
 | 
			
		||||
        val chunk = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        when(callTarget.scopedName) {
 | 
			
		||||
        when(callTarget.scopedNameString) {
 | 
			
		||||
            "sys.push" -> {
 | 
			
		||||
                // push byte
 | 
			
		||||
                val tr = translateExpression(fcall.args.single())
 | 
			
		||||
@@ -878,7 +1184,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                        fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                else TODO("extsub with banked address got called ${callTarget.name}")
 | 
			
		||||
                else TODO("extsub with banked address got called ${callTarget.name}  ${fcall.position}")
 | 
			
		||||
            }
 | 
			
		||||
        addInstr(result, call, null)
 | 
			
		||||
        val resultRegs = returnRegisters.filter{it.dt!=IRDataType.FLOAT}.map{it.registerNum}
 | 
			
		||||
@@ -1045,7 +1351,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
 | 
			
		||||
        // TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
 | 
			
		||||
        // TODO this used to be a single instruction like SCC, SCS, SZ etc
 | 
			
		||||
        val other = codeGen.createLabelName()
 | 
			
		||||
        val after = codeGen.createLabelName()
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
@@ -1060,7 +1366,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun compareRegisterAsBooleanResult(branchForTrue: Opcode, dt: IRDataType, reg1: Int, reg2: Int, result: MutableList<IRCodeChunkBase>): Int {
 | 
			
		||||
        // TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic
 | 
			
		||||
        // TODO this used to be a single instruction like SCC, SCS, SZ etc
 | 
			
		||||
        val other = codeGen.createLabelName()
 | 
			
		||||
        val after = codeGen.createLabelName()
 | 
			
		||||
        val resultReg = codeGen.registers.next(IRDataType.BYTE)
 | 
			
		||||
@@ -1220,7 +1526,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun operatorDivide(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult {
 | 
			
		||||
    private fun operatorDivide(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
 | 
			
		||||
        val vmDt = irType(dt)
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val constFactorRight = binExpr.right as? PtNumber
 | 
			
		||||
        if(vmDt==IRDataType.FLOAT) {
 | 
			
		||||
@@ -1235,7 +1542,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                addToResult(result, leftTr, -1, leftTr.resultFpReg)
 | 
			
		||||
                val rightTr = translateExpression(binExpr.right)
 | 
			
		||||
                addToResult(result, rightTr, -1, rightTr.resultFpReg)
 | 
			
		||||
                addInstr(result, if(signed)
 | 
			
		||||
                addInstr(result, if(dt.isSigned)
 | 
			
		||||
                    IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
 | 
			
		||||
                else
 | 
			
		||||
                    IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
 | 
			
		||||
@@ -1247,13 +1554,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                val tr = translateExpression(binExpr.left)
 | 
			
		||||
                addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
                val factor = constFactorRight.number.toInt()
 | 
			
		||||
                result += codeGen.divideByConst(vmDt, tr.resultReg, factor, signed)
 | 
			
		||||
                result += codeGen.divideByConst(vmDt, tr.resultReg, factor, dt.isSigned)
 | 
			
		||||
                ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
 | 
			
		||||
            } else {
 | 
			
		||||
                if(binExpr.right is PtNumber) {
 | 
			
		||||
                    val leftTr = translateExpression(binExpr.left)
 | 
			
		||||
                    addToResult(result, leftTr, leftTr.resultReg, -1)
 | 
			
		||||
                    addInstr(result, if (signed)
 | 
			
		||||
                    addInstr(result, if (dt.isSigned)
 | 
			
		||||
                        IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
 | 
			
		||||
                    else
 | 
			
		||||
                        IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
 | 
			
		||||
@@ -1264,7 +1571,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                    addToResult(result, leftTr, leftTr.resultReg, -1)
 | 
			
		||||
                    val rightTr = translateExpression(binExpr.right)
 | 
			
		||||
                    addToResult(result, rightTr, rightTr.resultReg, -1)
 | 
			
		||||
                    addInstr(result, if (signed)
 | 
			
		||||
                    addInstr(result, if (dt.isSigned)
 | 
			
		||||
                        IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
 | 
			
		||||
                    else
 | 
			
		||||
                        IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
 | 
			
		||||
@@ -1275,7 +1582,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
 | 
			
		||||
    private fun operatorMultiply(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
 | 
			
		||||
        val vmDt = irType(dt)
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val constFactorLeft = binExpr.left as? PtNumber
 | 
			
		||||
        val constFactorRight = binExpr.right as? PtNumber
 | 
			
		||||
@@ -1297,7 +1605,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                addToResult(result, leftTr, -1, leftTr.resultFpReg)
 | 
			
		||||
                val rightTr = translateExpression(binExpr.right)
 | 
			
		||||
                addToResult(result, rightTr, -1, rightTr.resultFpReg)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.MULR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.MULSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
 | 
			
		||||
                ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -1305,20 +1613,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
                val tr = translateExpression(binExpr.right)
 | 
			
		||||
                addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
                val factor = constFactorLeft.number.toInt()
 | 
			
		||||
                result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
 | 
			
		||||
                result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
 | 
			
		||||
                ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
 | 
			
		||||
            } else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
 | 
			
		||||
                val tr = translateExpression(binExpr.left)
 | 
			
		||||
                addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
                val factor = constFactorRight.number.toInt()
 | 
			
		||||
                result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
 | 
			
		||||
                result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
 | 
			
		||||
                ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
 | 
			
		||||
            } else {
 | 
			
		||||
                val leftTr = translateExpression(binExpr.left)
 | 
			
		||||
                addToResult(result, leftTr, leftTr.resultReg, -1)
 | 
			
		||||
                val rightTr = translateExpression(binExpr.right)
 | 
			
		||||
                addToResult(result, rightTr, rightTr.resultReg, -1)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.MULR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
 | 
			
		||||
                val opcode = if(dt.isSigned) Opcode.MULSR else Opcode.MULR
 | 
			
		||||
                addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
 | 
			
		||||
                ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -1434,6 +1743,136 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun operatorDereference(binExpr: PtBinaryExpression): ExpressionCodeResult {
 | 
			
		||||
        // the only case we support here is:   a.b.c[i] . value
 | 
			
		||||
        val left = binExpr.left as? PtArrayIndexer
 | 
			
		||||
        val right = binExpr.right as? PtIdentifier
 | 
			
		||||
        require(binExpr.operator=="." && left!=null && right!=null) {"invalid dereference expression ${binExpr.position}"}
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
        val field: Pair<DataType, UByte>
 | 
			
		||||
        val pointerReg: Int
 | 
			
		||||
        var extraFieldOffset = 0
 | 
			
		||||
 | 
			
		||||
        if(left.type.isStructInstance) {
 | 
			
		||||
 | 
			
		||||
            // indexing on a pointer directly
 | 
			
		||||
            // fetch pointer address, determine struct and field, add index * structsize
 | 
			
		||||
            if(left.variable!=null) {
 | 
			
		||||
                val pointerTr = translateExpression(left.variable!!)
 | 
			
		||||
                result += pointerTr.chunks
 | 
			
		||||
                pointerReg = pointerTr.resultReg
 | 
			
		||||
            } else if(left.pointerderef!=null) {
 | 
			
		||||
                TODO("get pointer from deref $left  ${left.position}")
 | 
			
		||||
            } else {
 | 
			
		||||
                throw AssemblyError("weird arrayindexer $left  ${left.position}")
 | 
			
		||||
            }
 | 
			
		||||
            val struct = left.type.subType!! as StStruct
 | 
			
		||||
            val constindex = left.index as? PtNumber
 | 
			
		||||
            if(constindex!=null) {
 | 
			
		||||
                extraFieldOffset = struct.size.toInt() * constindex.number.toInt()
 | 
			
		||||
            } else {
 | 
			
		||||
                val (chunks, indexReg) = codeGen.loadIndexReg(left.index, struct.size.toInt(), true, false)
 | 
			
		||||
                result += chunks
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg), null)
 | 
			
		||||
            }
 | 
			
		||||
            field = struct.getField(right.name, codeGen.program.memsizer)
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            // indexing on an array with pointers
 | 
			
		||||
            // fetch the pointer from the array, determine the struct & field
 | 
			
		||||
            val indexedTr = translateExpression(left)
 | 
			
		||||
            result += indexedTr.chunks
 | 
			
		||||
            pointerReg = indexedTr.resultReg
 | 
			
		||||
            val struct = left.type.dereference().subType as? StStruct
 | 
			
		||||
            require(indexedTr.dt == IRDataType.WORD && struct != null)
 | 
			
		||||
            field = struct.getField(right.name, codeGen.program.memsizer)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // add field offset to pointer and load the value into the result register
 | 
			
		||||
        val fieldVmDt = irType(field.first)
 | 
			
		||||
        val fieldOffset = field.second.toInt() + extraFieldOffset
 | 
			
		||||
        var resultFpReg = -1
 | 
			
		||||
        var resultReg = -1
 | 
			
		||||
        if (fieldVmDt == IRDataType.FLOAT)
 | 
			
		||||
            resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
 | 
			
		||||
        else
 | 
			
		||||
            resultReg = codeGen.registers.next(fieldVmDt)
 | 
			
		||||
        if(fieldOffset==0) {
 | 
			
		||||
            // no offset, can use current pointer directly
 | 
			
		||||
            result += IRCodeChunk(null, null).also {
 | 
			
		||||
                it += if (fieldVmDt == IRDataType.FLOAT)
 | 
			
		||||
                    IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
 | 
			
		||||
                else
 | 
			
		||||
                    IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // actually have to add an offset
 | 
			
		||||
            if(fieldOffset<=255) {
 | 
			
		||||
                if(fieldVmDt==IRDataType.FLOAT)
 | 
			
		||||
                    addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, fpReg1 = resultFpReg, reg1 = pointerReg, immediate = fieldOffset), null)
 | 
			
		||||
                else
 | 
			
		||||
                    addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, reg1 = resultReg, reg2 = pointerReg, immediate = fieldOffset), null)
 | 
			
		||||
            } else {
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldOffset)
 | 
			
		||||
                    it += if (fieldVmDt == IRDataType.FLOAT)
 | 
			
		||||
                        IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
 | 
			
		||||
                    else
 | 
			
		||||
                        IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return ExpressionCodeResult(result, fieldVmDt, resultReg, resultFpReg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UByte> {
 | 
			
		||||
        // returns instructions to calculate the pointer address, and the offset into the struct it points to
 | 
			
		||||
        // so that LOADFIELD and STOREFIELD opcodes can be used instead of having an explicit extra ADD
 | 
			
		||||
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
 | 
			
		||||
        if(targetPointerDeref.chain.isEmpty())
 | 
			
		||||
            return result to 0u   // nothing to do; there's no deref chain
 | 
			
		||||
 | 
			
		||||
        var struct: StStruct? = null
 | 
			
		||||
        if(targetPointerDeref.startpointer.type.subType!=null)
 | 
			
		||||
            struct = targetPointerDeref.startpointer.type.subType as StStruct
 | 
			
		||||
        if(targetPointerDeref.chain.isNotEmpty()) {
 | 
			
		||||
            // traverse deref chain
 | 
			
		||||
            for(deref in targetPointerDeref.chain.dropLast(1)) {
 | 
			
		||||
                val fieldinfo = struct!!.getField(deref, codeGen.program.memsizer)
 | 
			
		||||
                struct = fieldinfo.first.subType as StStruct
 | 
			
		||||
                // get new pointer from field
 | 
			
		||||
                result += IRCodeChunk(null, null).also {
 | 
			
		||||
                    it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt())
 | 
			
		||||
                    // LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
 | 
			
		||||
                    it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val field = targetPointerDeref.chain.last()
 | 
			
		||||
        val fieldinfo = struct!!.getField(field, codeGen.program.memsizer)
 | 
			
		||||
        if(fieldinfo.second>0u) {
 | 
			
		||||
            if(targetPointerDeref.derefLast) {
 | 
			
		||||
                require(fieldinfo.first.isPointer)
 | 
			
		||||
                // add the field offset
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
 | 
			
		||||
                // LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
 | 
			
		||||
                return result to 0u
 | 
			
		||||
            } else {
 | 
			
		||||
                return result to fieldinfo.second
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if(targetPointerDeref.derefLast) {
 | 
			
		||||
            require(fieldinfo.first.isPointer)
 | 
			
		||||
            // LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
 | 
			
		||||
        }
 | 
			
		||||
        return result to 0u
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ class IRCodeGen(
 | 
			
		||||
        ensureFirstChunkLabels(irProg)
 | 
			
		||||
        irProg.linkChunks()
 | 
			
		||||
        irProg.convertAsmChunks()
 | 
			
		||||
        irProg.splitSSAchunks()
 | 
			
		||||
 | 
			
		||||
        // the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
 | 
			
		||||
        val optimizer = IRPeepholeOptimizer(irProg)
 | 
			
		||||
@@ -56,6 +57,8 @@ class IRCodeGen(
 | 
			
		||||
        return irProg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
 | 
			
		||||
 | 
			
		||||
    private fun changeGlobalVarInits(symbolTable: SymbolTable) {
 | 
			
		||||
        // Normally, block level (global) variables that have a numeric initialization value
 | 
			
		||||
        // are initialized via an assignment statement.
 | 
			
		||||
@@ -65,17 +68,16 @@ class IRCodeGen(
 | 
			
		||||
            if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
 | 
			
		||||
                val block = variable.parent.astNode as PtBlock
 | 
			
		||||
                val initialization = (block.children.firstOrNull {
 | 
			
		||||
                    it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName
 | 
			
		||||
                    it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
 | 
			
		||||
                } as PtAssignment?)
 | 
			
		||||
                val initValue = initialization?.value
 | 
			
		||||
                when(initValue){
 | 
			
		||||
                when(val initValue = initialization?.value){
 | 
			
		||||
                    is PtBool -> {
 | 
			
		||||
                        require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" }
 | 
			
		||||
                        require(initValue.asInt()!=0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
 | 
			
		||||
                        variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
 | 
			
		||||
                        initsToRemove += block to initialization
 | 
			
		||||
                    }
 | 
			
		||||
                    is PtNumber -> {
 | 
			
		||||
                        require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
 | 
			
		||||
                        require(initValue.number!=0.0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
 | 
			
		||||
                        variable.setOnetimeInitNumeric(initValue.number)
 | 
			
		||||
                        initsToRemove += block to initialization
 | 
			
		||||
                    }
 | 
			
		||||
@@ -104,6 +106,15 @@ class IRCodeGen(
 | 
			
		||||
                is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
 | 
			
		||||
                is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
 | 
			
		||||
                is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
 | 
			
		||||
                is PtPointerDeref -> require('.' in node.startpointer.name) { "node $node name is not scoped: ${node.startpointer.name}" }
 | 
			
		||||
                is PtIdentifier -> {
 | 
			
		||||
                    if('.' !in node.name) {
 | 
			
		||||
                        // there is 1 case where the identifier is not scoped: if it's the value field name after a pointer array indexing.
 | 
			
		||||
                        val expr = node.parent as? PtBinaryExpression
 | 
			
		||||
                        if (expr?.operator != "." || expr.right !== node || expr.left !is PtArrayIndexer || (!expr.left.type.isPointer && !expr.left.type.isStructInstance))
 | 
			
		||||
                            require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else -> { /* node has no name or is ok to have no dots in the name */ }
 | 
			
		||||
            }
 | 
			
		||||
            node.children.forEach { verifyPtNode(it) }
 | 
			
		||||
@@ -256,10 +267,12 @@ class IRCodeGen(
 | 
			
		||||
            is PtBool,
 | 
			
		||||
            is PtArray,
 | 
			
		||||
            is PtBlock,
 | 
			
		||||
            is PtDefer -> throw AssemblyError("should have been transformed")
 | 
			
		||||
            is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
 | 
			
		||||
            is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
 | 
			
		||||
            else -> TODO("missing codegen for $node")
 | 
			
		||||
            is PtDefer -> throw AssemblyError("defer should have been transformed")
 | 
			
		||||
            is PtString -> throw AssemblyError("string should not occur as separate statement node $node")
 | 
			
		||||
            is PtSub -> throw AssemblyError("nested subroutines should have been flattened $node")
 | 
			
		||||
            is PtStructDecl -> emptyList()
 | 
			
		||||
            is PtSubSignature -> emptyList()
 | 
			
		||||
            else -> TODO("missing codegen for $node  ${node.position}")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val nonEmptyChunks = chunks.filter { it.isNotEmpty() || it.label != null }
 | 
			
		||||
@@ -324,7 +337,7 @@ class IRCodeGen(
 | 
			
		||||
        return result
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
 | 
			
		||||
    internal fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
 | 
			
		||||
        if(label!=null)
 | 
			
		||||
            return when(condition) {
 | 
			
		||||
                BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
 | 
			
		||||
@@ -420,8 +433,9 @@ class IRCodeGen(
 | 
			
		||||
        whenStmt.choices.children.forEach {
 | 
			
		||||
            val choice = it as PtWhenChoice
 | 
			
		||||
            if(choice.isElse) {
 | 
			
		||||
                require(choice.parent.children.last() === choice)
 | 
			
		||||
                result += translateNode(choice.statements)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
 | 
			
		||||
                // is always the last node so can fall through
 | 
			
		||||
            } else {
 | 
			
		||||
                if(choice.statements.children.isEmpty()) {
 | 
			
		||||
                    // no statements for this choice value, jump to the end immediately
 | 
			
		||||
@@ -433,22 +447,30 @@ class IRCodeGen(
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    val choiceLabel = createLabelName()
 | 
			
		||||
                    val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
 | 
			
		||||
                    val branchLabel: String
 | 
			
		||||
                    if(onlyJumpLabel==null) {
 | 
			
		||||
                        choices.add(choiceLabel to choice)
 | 
			
		||||
                        branchLabel = choiceLabel
 | 
			
		||||
                    } else {
 | 
			
		||||
                        branchLabel = onlyJumpLabel
 | 
			
		||||
                    }
 | 
			
		||||
                    choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
 | 
			
		||||
                        result += IRCodeChunk(null, null).also { chunk ->
 | 
			
		||||
                            chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
 | 
			
		||||
                            chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
 | 
			
		||||
                            chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = branchLabel)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(choices.isNotEmpty())
 | 
			
		||||
            addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
 | 
			
		||||
 | 
			
		||||
        choices.forEach { (label, choice) ->
 | 
			
		||||
            result += labelFirstChunk(translateNode(choice.statements), label)
 | 
			
		||||
            val lastStatement = choice.statements.children.last()
 | 
			
		||||
            if(lastStatement !is PtReturn && lastStatement !is PtJump)
 | 
			
		||||
            if(!choice.isOnlyGotoOrReturn())
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -468,11 +490,12 @@ class IRCodeGen(
 | 
			
		||||
                    translateForInNonConstantRange(forLoop, loopvar)
 | 
			
		||||
            }
 | 
			
		||||
            is PtIdentifier -> {
 | 
			
		||||
                require(forLoop.variable.name == loopvar.scopedName)
 | 
			
		||||
                require(forLoop.variable.name == loopvar.scopedNameString)
 | 
			
		||||
                val elementDt = irType(iterable.type.elementType())
 | 
			
		||||
                val iterableLength = symbolTable.getLength(iterable.name)
 | 
			
		||||
                val loopvarSymbol = forLoop.variable.name
 | 
			
		||||
                val indexReg = registers.next(IRDataType.BYTE)
 | 
			
		||||
                val tmpReg = registers.next(IRDataType.BYTE)
 | 
			
		||||
                val tmpReg = registers.next(elementDt)
 | 
			
		||||
                val loopLabel = createLabelName()
 | 
			
		||||
                val endLabel = createLabelName()
 | 
			
		||||
                when {
 | 
			
		||||
@@ -480,9 +503,9 @@ class IRCodeGen(
 | 
			
		||||
                        // iterate over a zero-terminated string
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
 | 
			
		||||
                        result += IRCodeChunk(loopLabel, null).also {
 | 
			
		||||
                            it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
 | 
			
		||||
                            it += IRInstruction(Opcode.LOADX, elementDt, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
 | 
			
		||||
                            it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
 | 
			
		||||
                            it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
 | 
			
		||||
                            it += IRInstruction(Opcode.STOREM, elementDt, reg1 = tmpReg, labelSymbol = loopvarSymbol)
 | 
			
		||||
                        }
 | 
			
		||||
                        result += translateNode(forLoop.statements)
 | 
			
		||||
                        val jumpChunk = IRCodeChunk(null, null)
 | 
			
		||||
@@ -491,10 +514,9 @@ class IRCodeGen(
 | 
			
		||||
                        result += jumpChunk
 | 
			
		||||
                        result += IRCodeChunk(endLabel, null)
 | 
			
		||||
                    }
 | 
			
		||||
                    iterable.type.isSplitWordArray -> {
 | 
			
		||||
                    iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
 | 
			
		||||
                        // iterate over lsb/msb split word array
 | 
			
		||||
                        val elementDt = iterable.type.elementType()
 | 
			
		||||
                        if(!elementDt.isWord)
 | 
			
		||||
                        if(elementDt!=IRDataType.WORD)
 | 
			
		||||
                            throw AssemblyError("weird dt")
 | 
			
		||||
                        addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
 | 
			
		||||
                        result += IRCodeChunk(loopLabel, null).also {
 | 
			
		||||
@@ -504,7 +526,7 @@ class IRCodeGen(
 | 
			
		||||
                            it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
 | 
			
		||||
                            it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
 | 
			
		||||
                            it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
 | 
			
		||||
                            it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
 | 
			
		||||
                            it += IRInstruction(Opcode.STOREM, elementDt, reg1=concatReg, labelSymbol = loopvarSymbol)
 | 
			
		||||
                        }
 | 
			
		||||
                        result += translateNode(forLoop.statements)
 | 
			
		||||
                        result += IRCodeChunk(null, null).also {
 | 
			
		||||
@@ -548,7 +570,7 @@ class IRCodeGen(
 | 
			
		||||
        val step = iterable.step.number.toInt()
 | 
			
		||||
        if (step==0)
 | 
			
		||||
            throw AssemblyError("step 0")
 | 
			
		||||
        require(forLoop.variable.name == loopvar.scopedName)
 | 
			
		||||
        require(forLoop.variable.name == loopvar.scopedNameString)
 | 
			
		||||
        val loopvarSymbol = forLoop.variable.name
 | 
			
		||||
        val loopvarDt = when(loopvar) {
 | 
			
		||||
            is StMemVar -> loopvar.dt
 | 
			
		||||
@@ -634,7 +656,7 @@ class IRCodeGen(
 | 
			
		||||
 | 
			
		||||
    private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
 | 
			
		||||
        val loopLabel = createLabelName()
 | 
			
		||||
        require(forLoop.variable.name == loopvar.scopedName)
 | 
			
		||||
        require(forLoop.variable.name == loopvar.scopedNameString)
 | 
			
		||||
        val loopvarSymbol = forLoop.variable.name
 | 
			
		||||
        val loopvarDt = when(loopvar) {
 | 
			
		||||
            is StMemVar -> loopvar.dt
 | 
			
		||||
@@ -771,7 +793,7 @@ class IRCodeGen(
 | 
			
		||||
        code += if(factor==0.0) {
 | 
			
		||||
            IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
 | 
			
		||||
        } else {
 | 
			
		||||
            IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
 | 
			
		||||
            IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
 | 
			
		||||
        }
 | 
			
		||||
        return code
 | 
			
		||||
    }
 | 
			
		||||
@@ -789,38 +811,40 @@ class IRCodeGen(
 | 
			
		||||
            val factorReg = registers.next(IRDataType.FLOAT)
 | 
			
		||||
            code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
 | 
			
		||||
            code += if(knownAddress!=null)
 | 
			
		||||
                IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
 | 
			
		||||
                IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
 | 
			
		||||
            else
 | 
			
		||||
                IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
 | 
			
		||||
                IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
 | 
			
		||||
        }
 | 
			
		||||
        return code
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun multiplyByConst(dt: IRDataType, reg: Int, factor: Int): IRCodeChunk {
 | 
			
		||||
    internal fun multiplyByConst(dt: DataType, reg: Int, factor: Int): IRCodeChunk {
 | 
			
		||||
        val irdt = irType(dt)
 | 
			
		||||
        val code = IRCodeChunk(null, null)
 | 
			
		||||
        if(factor==1)
 | 
			
		||||
            return code
 | 
			
		||||
        val pow2 = powersOfTwoInt.indexOf(factor)
 | 
			
		||||
        if(pow2==1) {
 | 
			
		||||
            // just shift 1 bit
 | 
			
		||||
            code += IRInstruction(Opcode.LSL, dt, reg1=reg)
 | 
			
		||||
            code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
 | 
			
		||||
        }
 | 
			
		||||
        else if(pow2>=1) {
 | 
			
		||||
            // just shift multiple bits
 | 
			
		||||
            val pow2reg = registers.next(IRDataType.BYTE)
 | 
			
		||||
            code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
 | 
			
		||||
            code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
 | 
			
		||||
            code += IRInstruction(Opcode.LSLN, irdt, reg1=reg, reg2=pow2reg)
 | 
			
		||||
        } else {
 | 
			
		||||
            code += if (factor == 0) {
 | 
			
		||||
                IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
 | 
			
		||||
                IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
 | 
			
		||||
            } else {
 | 
			
		||||
                IRInstruction(Opcode.MUL, dt, reg1=reg, immediate = factor)
 | 
			
		||||
                val opcode = if(dt.isSigned) Opcode.MULS else Opcode.MUL
 | 
			
		||||
                IRInstruction(opcode, irdt, reg1=reg, immediate = factor)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return code
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
 | 
			
		||||
    internal fun multiplyByConstInplace(dt: IRDataType, signed: Boolean, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
 | 
			
		||||
        val code = IRCodeChunk(null, null)
 | 
			
		||||
        if(factor==1)
 | 
			
		||||
            return code
 | 
			
		||||
@@ -850,10 +874,11 @@ class IRCodeGen(
 | 
			
		||||
            else {
 | 
			
		||||
                val factorReg = registers.next(dt)
 | 
			
		||||
                code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
 | 
			
		||||
                val opcode = if(signed) Opcode.MULSM else Opcode.MULM
 | 
			
		||||
                code += if(knownAddress!=null)
 | 
			
		||||
                    IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
 | 
			
		||||
                    IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
 | 
			
		||||
                else
 | 
			
		||||
                    IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
 | 
			
		||||
                    IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return code
 | 
			
		||||
@@ -1117,7 +1142,7 @@ class IRCodeGen(
 | 
			
		||||
            if(identifier!=null && !isIndirectJump(goto))
 | 
			
		||||
                IRInstruction(branchOpcode, labelSymbol = identifier.name)
 | 
			
		||||
            else
 | 
			
		||||
                TODO("JUMP to expression address ${goto.target}")
 | 
			
		||||
                TODO("JUMP to expression address ${goto.target}  ${goto.position}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1653,7 +1678,7 @@ class IRCodeGen(
 | 
			
		||||
                translateSimple(cond, Opcode.BSTEQ, false)
 | 
			
		||||
            }
 | 
			
		||||
            is PtTypeCast -> {
 | 
			
		||||
                require(cond.type.isBool && cond.value.type.isNumeric)
 | 
			
		||||
                require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
 | 
			
		||||
                translateSimple(cond, Opcode.BSTEQ, false)
 | 
			
		||||
            }
 | 
			
		||||
            is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
 | 
			
		||||
@@ -1669,6 +1694,9 @@ class IRCodeGen(
 | 
			
		||||
            is PtBinaryExpression -> {
 | 
			
		||||
                translateBinExpr(cond)
 | 
			
		||||
            }
 | 
			
		||||
            is PtPointerDeref -> {
 | 
			
		||||
                translateSimple(cond, Opcode.BSTEQ, false)
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("weird if condition ${ifElse.condition}")
 | 
			
		||||
        }
 | 
			
		||||
        return result
 | 
			
		||||
@@ -1743,9 +1771,7 @@ class IRCodeGen(
 | 
			
		||||
    private fun isIndirectJump(jump: PtJump): Boolean {
 | 
			
		||||
        if(jump.target.asConstInteger()!=null)
 | 
			
		||||
            return false
 | 
			
		||||
        val identifier = jump.target as? PtIdentifier
 | 
			
		||||
        if(identifier==null)
 | 
			
		||||
            return true
 | 
			
		||||
        val identifier = jump.target as? PtIdentifier ?: return true
 | 
			
		||||
        val symbol = symbolTable.lookup(identifier.name)
 | 
			
		||||
        return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
 | 
			
		||||
    }
 | 
			
		||||
@@ -1834,9 +1860,9 @@ class IRCodeGen(
 | 
			
		||||
                is PtNop -> { /* nothing */ }
 | 
			
		||||
                is PtAssignment, is PtAugmentedAssign -> { /* global variable initialization is done elsewhere */ }
 | 
			
		||||
                is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
 | 
			
		||||
                is PtAlign -> TODO("ir support for inline %align")
 | 
			
		||||
                is PtAlign -> TODO("ir support for inline %align  ${child.position}")
 | 
			
		||||
                is PtSub -> {
 | 
			
		||||
                    val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position)
 | 
			
		||||
                    val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
 | 
			
		||||
                    for (subchild in child.children) {
 | 
			
		||||
                        translateNode(subchild).forEach { sub += it }
 | 
			
		||||
                    }
 | 
			
		||||
@@ -1885,21 +1911,22 @@ class IRCodeGen(
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else -> TODO("weird block child node $child")
 | 
			
		||||
                is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
 | 
			
		||||
                else -> TODO("weird block child node $child  ${child.position}")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return irBlock
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
 | 
			
		||||
    private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
 | 
			
		||||
        val result = mutableListOf<IRSubroutine.IRParam>()
 | 
			
		||||
        parameters.forEach {
 | 
			
		||||
            it as PtSubroutineParameter
 | 
			
		||||
            if(it.register==null) {
 | 
			
		||||
                val flattenedName = it.definingISub()!!.name + "." + it.name
 | 
			
		||||
                if (symbolTable.lookup(flattenedName) == null)
 | 
			
		||||
                    TODO("fix missing lookup for: $flattenedName   parameter")
 | 
			
		||||
                val orig = symbolTable.lookup(flattenedName) as StStaticVariable
 | 
			
		||||
                result += IRSubroutine.IRParam(flattenedName, orig.dt)
 | 
			
		||||
                require('.' in it.name) { "even parameter names should have been made fully scoped by now" }
 | 
			
		||||
                val orig = symbolTable.lookup(it.name) as? StStaticVariable
 | 
			
		||||
                    ?: TODO("fix missing lookup for: ${it.name}   parameter")
 | 
			
		||||
                result += IRSubroutine.IRParam(it.name, orig.dt)
 | 
			
		||||
            } else {
 | 
			
		||||
                val reg = it.register
 | 
			
		||||
                require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
 | 
			
		||||
@@ -1924,7 +1951,7 @@ class IRCodeGen(
 | 
			
		||||
 | 
			
		||||
    internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
 | 
			
		||||
 | 
			
		||||
    fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
 | 
			
		||||
    internal fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
 | 
			
		||||
        return IRCodeChunk(label, null).also {
 | 
			
		||||
            val args = params.map { (dt, reg)->
 | 
			
		||||
                FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
 | 
			
		||||
@@ -1935,9 +1962,7 @@ class IRCodeGen(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
 | 
			
		||||
 | 
			
		||||
    fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
 | 
			
		||||
    internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
 | 
			
		||||
        val chunk = IRCodeChunk(null, null)
 | 
			
		||||
        when(registerOrFlag.registerOrPair) {
 | 
			
		||||
            RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
 | 
			
		||||
@@ -1951,13 +1976,91 @@ class IRCodeGen(
 | 
			
		||||
            in Cx16VirtualRegisters -> {
 | 
			
		||||
                chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}")
 | 
			
		||||
            }
 | 
			
		||||
            in combinedLongRegisters -> {
 | 
			
		||||
                require(paramDt==IRDataType.LONG)
 | 
			
		||||
                val startreg = registerOrFlag.registerOrPair!!.startregname()
 | 
			
		||||
                chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${startreg}")
 | 
			
		||||
            }
 | 
			
		||||
            null -> when(registerOrFlag.statusflag) {
 | 
			
		||||
                // TODO: do the statusflag argument as last
 | 
			
		||||
                Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
 | 
			
		||||
                else -> throw AssemblyError("weird statusflag as param")
 | 
			
		||||
                else -> throw AssemblyError("unsupported statusflag as param")
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw AssemblyError("unsupported register arg")
 | 
			
		||||
            else -> throw AssemblyError("unsupported register arg $registerOrFlag")
 | 
			
		||||
        }
 | 
			
		||||
        return chunk
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UByte> {
 | 
			
		||||
        // calculates the pointer address and returns the register it's in + remaining offset into the struct  (so that LOADFIELD/STOREFIELD instructions can be used)
 | 
			
		||||
        val pointerTr = expressionEval.translateExpression(deref.startpointer)
 | 
			
		||||
        result += pointerTr.chunks
 | 
			
		||||
        val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
 | 
			
		||||
        result += instructions
 | 
			
		||||
        return pointerTr.resultReg to offset
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UByte, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
 | 
			
		||||
        if(offset<=0u) {
 | 
			
		||||
            val irdt = irType(type)
 | 
			
		||||
            val instr = if(type.isFloat) {
 | 
			
		||||
                if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
 | 
			
		||||
                else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
 | 
			
		||||
            } else {
 | 
			
		||||
                if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
 | 
			
		||||
                else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
 | 
			
		||||
            }
 | 
			
		||||
            addInstr(result, instr, null)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // store with field offset
 | 
			
		||||
        var valueRegister = existingValueRegister
 | 
			
		||||
        val irdt = irType(type)
 | 
			
		||||
        if(valueIsZero && valueRegister<0) {
 | 
			
		||||
            if(type.isFloat) {
 | 
			
		||||
                valueRegister = registers.next(IRDataType.FLOAT)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
 | 
			
		||||
            } else {
 | 
			
		||||
                valueRegister = registers.next(irdt)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        val instr = if (type.isFloat)
 | 
			
		||||
            IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
 | 
			
		||||
        else
 | 
			
		||||
            IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
 | 
			
		||||
        addInstr(result, instr, null)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal fun loadIndexReg(index: PtExpression, itemsize: Int, wordIndex: Boolean, arrayIsSplitWords: Boolean): Pair<IRCodeChunks, Int> {
 | 
			
		||||
        // returns the code to load the Index into the register, which is also returned.
 | 
			
		||||
 | 
			
		||||
        require(index !is PtNumber) { "index should not be a constant number here, calling code should handle that in a more efficient way" }
 | 
			
		||||
 | 
			
		||||
        val result = mutableListOf<IRCodeChunkBase>()
 | 
			
		||||
 | 
			
		||||
        if(wordIndex) {
 | 
			
		||||
            val tr = expressionEval.translateExpression(index)
 | 
			
		||||
            addToResult(result, tr, tr.resultReg, -1)
 | 
			
		||||
            var indexReg = tr.resultReg
 | 
			
		||||
            if(tr.dt==IRDataType.BYTE) {
 | 
			
		||||
                indexReg = registers.next(IRDataType.WORD)
 | 
			
		||||
                addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=tr.resultReg), null)
 | 
			
		||||
            }
 | 
			
		||||
            result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
 | 
			
		||||
            return Pair(result, indexReg)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // regular byte size index value.
 | 
			
		||||
        val byteIndexTr = expressionEval.translateExpression(index)
 | 
			
		||||
        addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1)
 | 
			
		||||
 | 
			
		||||
        if(itemsize==1 || arrayIsSplitWords)
 | 
			
		||||
            return Pair(result, byteIndexTr.resultReg)
 | 
			
		||||
 | 
			
		||||
        result += multiplyByConst(DataType.UBYTE, byteIndexTr.resultReg, itemsize)
 | 
			
		||||
        return Pair(result, byteIndexTr.resultReg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
 | 
			
		||||
                                || removeWeirdBranches(chunk1, chunk2, indexedInstructions)
 | 
			
		||||
                                || removeDoubleSecClc(chunk1, indexedInstructions)
 | 
			
		||||
                                || cleanupPushPop(chunk1, indexedInstructions)
 | 
			
		||||
                                || simplifyConstantReturns(chunk1, indexedInstructions)
 | 
			
		||||
                                || removeNeedlessLoads(chunk1, indexedInstructions)
 | 
			
		||||
                    } while (changed)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -159,10 +161,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
 | 
			
		||||
            if(chunk.label!=null)
 | 
			
		||||
                return false
 | 
			
		||||
            if(previous is IRCodeChunk && chunk is IRCodeChunk) {
 | 
			
		||||
                // if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
 | 
			
		||||
                // if the previous chunk doesn't end in a SSA branching instruction, flow continues into the next chunk, so they may be joined
 | 
			
		||||
                val lastInstruction = previous.instructions.lastOrNull()
 | 
			
		||||
                if(lastInstruction!=null)
 | 
			
		||||
                    return lastInstruction.opcode !in OpcodesThatJump
 | 
			
		||||
                    return lastInstruction.opcode !in OpcodesThatEndSSAblock
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            return false
 | 
			
		||||
@@ -298,16 +300,16 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
 | 
			
		||||
            // remove useless RETURN
 | 
			
		||||
            if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR || ins.opcode==Opcode.RETURNI)) {
 | 
			
		||||
                val previous = chunk.instructions[idx-1]
 | 
			
		||||
                if(previous.opcode in OpcodesThatJump) {
 | 
			
		||||
                if(previous.opcode in OpcodesThatBranchUnconditionally) {
 | 
			
		||||
                    chunk.instructions.removeAt(idx)
 | 
			
		||||
                    changed = true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // replace subsequent opcodes that jump by just the first
 | 
			
		||||
            if(idx>0 && (ins.opcode in OpcodesThatJump)) {
 | 
			
		||||
            if(idx>0 && (ins.opcode in OpcodesThatBranchUnconditionally)) {
 | 
			
		||||
                val previous = chunk.instructions[idx-1]
 | 
			
		||||
                if(previous.opcode in OpcodesThatJump) {
 | 
			
		||||
                if(previous.opcode in OpcodesThatBranchUnconditionally) {
 | 
			
		||||
                    chunk.instructions.removeAt(idx)
 | 
			
		||||
                    changed = true
 | 
			
		||||
                }
 | 
			
		||||
@@ -352,12 +354,35 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
 | 
			
		||||
        return changed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun removeNeedlessLoads(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
 | 
			
		||||
        /*
 | 
			
		||||
load.b r2,#2
 | 
			
		||||
loadr.b r1,r2
 | 
			
		||||
jump p8_label_gen_2
 | 
			
		||||
         */
 | 
			
		||||
        var changed=false
 | 
			
		||||
        indexedInstructions.reversed().forEach { (idx, ins) ->
 | 
			
		||||
            if(idx>=2 && ins.opcode in OpcodesThatBranchUnconditionally) {
 | 
			
		||||
                val previous = indexedInstructions[idx-1].value
 | 
			
		||||
                val previous2 = indexedInstructions[idx-2].value
 | 
			
		||||
                if(previous.opcode==Opcode.LOADR && previous2.opcode in OpcodesThatLoad) {
 | 
			
		||||
                    if(previous.reg2==previous2.reg1) {
 | 
			
		||||
                        chunk.instructions[idx-2] = previous2.copy(reg1=previous.reg1)
 | 
			
		||||
                        chunk.instructions.removeAt(idx-1)
 | 
			
		||||
                        changed=true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return changed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
 | 
			
		||||
        // note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
 | 
			
		||||
        var changed = false
 | 
			
		||||
        indexedInstructions.reversed().forEach { (idx, ins) ->
 | 
			
		||||
            when (ins.opcode) {
 | 
			
		||||
                Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
 | 
			
		||||
                Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MULS, Opcode.MOD -> {
 | 
			
		||||
                    if (ins.immediate == 1) {
 | 
			
		||||
                        chunk.instructions.removeAt(idx)
 | 
			
		||||
                        changed = true
 | 
			
		||||
@@ -390,13 +415,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
 | 
			
		||||
                            chunk.instructions.removeAt(idx)
 | 
			
		||||
                            changed = true
 | 
			
		||||
                        }
 | 
			
		||||
                        -1 if ins.type == IRDataType.LONG -> {
 | 
			
		||||
                            chunk.instructions.removeAt(idx)
 | 
			
		||||
                            changed = true
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Opcode.OR -> {
 | 
			
		||||
                    if (ins.immediate == 0) {
 | 
			
		||||
                        chunk.instructions.removeAt(idx)
 | 
			
		||||
                        changed = true
 | 
			
		||||
                    } else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) || (ins.immediate == 65535 && ins.type == IRDataType.WORD)) {
 | 
			
		||||
                    } else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) ||
 | 
			
		||||
                               (ins.immediate == 65535 && ins.type == IRDataType.WORD) ||
 | 
			
		||||
                               (ins.immediate == -1 && ins.type == IRDataType.LONG)) {
 | 
			
		||||
                        chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
 | 
			
		||||
                        changed = true
 | 
			
		||||
                    }
 | 
			
		||||
@@ -455,4 +486,23 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
 | 
			
		||||
        }
 | 
			
		||||
        return changed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun simplifyConstantReturns(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
 | 
			
		||||
        //  use a RETURNI when a RETURNR is just returning a constant that was loaded into a register just before
 | 
			
		||||
        var changed = false
 | 
			
		||||
        indexedInstructions.reversed().forEach { (idx, ins) ->
 | 
			
		||||
            if(ins.opcode==Opcode.RETURNR) {
 | 
			
		||||
                if(idx>0) {
 | 
			
		||||
                    val insBefore = chunk.instructions[idx-1]
 | 
			
		||||
                    if(insBefore.opcode == Opcode.LOAD && insBefore.immediate!=null) {
 | 
			
		||||
                        val constvalue = insBefore.immediate!!
 | 
			
		||||
                        chunk.instructions[idx] = IRInstruction(Opcode.RETURNI, ins.type, immediate = constvalue)
 | 
			
		||||
                        chunk.instructions.removeAt(idx-1)
 | 
			
		||||
                        changed = true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return changed
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -27,6 +27,9 @@ class IRUnusedCodeRemover(
 | 
			
		||||
        // we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
 | 
			
		||||
        val prefix = "$blockLabel."
 | 
			
		||||
        val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
 | 
			
		||||
 | 
			
		||||
        // check if there are symbols referenced elsewhere that we should not prune (even though the rest of the block is empty)
 | 
			
		||||
 | 
			
		||||
        blockVars.forEach { stVar ->
 | 
			
		||||
            irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
 | 
			
		||||
                chunk.instructions.forEach { ins ->
 | 
			
		||||
@@ -47,7 +50,50 @@ class IRUnusedCodeRemover(
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val blockStructs = irprog.st.allStructDefs().filter { it.name.startsWith(prefix) }
 | 
			
		||||
        blockStructs.forEach { struct ->
 | 
			
		||||
            irprog.st.allStructInstances().forEach { instance ->
 | 
			
		||||
                if(instance.structName == struct.name)
 | 
			
		||||
                    return  // a struct instance is declared using this struct type
 | 
			
		||||
            }
 | 
			
		||||
            irprog.st.allVariables().forEach { variable ->
 | 
			
		||||
                if(variable.dt.isPointer || variable.dt.isStructInstance)
 | 
			
		||||
                    if(struct.name == variable.dt.subType!!.scopedNameString)
 | 
			
		||||
                        return   // a variable exists with the struct as (pointer) type
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        irprog.st.removeTree(blockLabel)
 | 
			
		||||
        removeBlockInits(irprog, blockLabel)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun removeBlockInits(code: IRProgram, blockLabel: String) {
 | 
			
		||||
        val instructions = code.globalInits.instructions
 | 
			
		||||
        instructions.toTypedArray().forEach {ins ->
 | 
			
		||||
            if(ins.labelSymbol?.startsWith(blockLabel)==true) {
 | 
			
		||||
                instructions.remove(ins)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // remove stray loads
 | 
			
		||||
        instructions.toTypedArray().forEach { ins ->
 | 
			
		||||
            if(ins.opcode in arrayOf(Opcode.LOAD, Opcode.LOADR, Opcode.LOADM)) {
 | 
			
		||||
                if(ins.reg1!=0) {
 | 
			
		||||
                    if(instructions.count { it.reg1==ins.reg1 || it.reg2==ins.reg1 } <2) {
 | 
			
		||||
                        if(ins.labelSymbol!=null)
 | 
			
		||||
                            code.st.removeIfExists(ins.labelSymbol!!)
 | 
			
		||||
                        instructions.remove(ins)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if(ins.fpReg1!=0) {
 | 
			
		||||
                    if (instructions.count { it.fpReg1 == ins.fpReg1 || it.fpReg2 == ins.fpReg1 } < 2) {
 | 
			
		||||
                        if(ins.labelSymbol!=null)
 | 
			
		||||
                            code.st.removeIfExists(ins.labelSymbol!!)
 | 
			
		||||
                        instructions.remove(ins)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun removeUnusedSubroutines(): Int {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ internal class RegisterPool {
 | 
			
		||||
    // everything from 99000 onwards is reserved for special purposes:
 | 
			
		||||
    // 99000 - 99099 : WORD registers for syscall arguments and response value(s)
 | 
			
		||||
    // 99100 - 99199 : BYTE registers for syscall arguments and response value(s)
 | 
			
		||||
    // 99200 - 99299 : LONG registers for syscall arguments and response value(s)
 | 
			
		||||
 | 
			
		||||
    private var nextRegister: Int=1
 | 
			
		||||
    private val registerTypes: MutableMap<Int, IRDataType> = mutableMapOf()
 | 
			
		||||
@@ -18,6 +19,8 @@ internal class RegisterPool {
 | 
			
		||||
            registerTypes[i] = IRDataType.WORD
 | 
			
		||||
        for(i in 99100..99199)
 | 
			
		||||
            registerTypes[i] = IRDataType.BYTE
 | 
			
		||||
        for(i in 99200..99299)
 | 
			
		||||
            registerTypes[i] = IRDataType.LONG
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun next(type: IRDataType): Int {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,12 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
 | 
			
		||||
                StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
 | 
			
		||||
                StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
 | 
			
		||||
                StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
 | 
			
		||||
                StNodeType.STRUCTINSTANCE -> {
 | 
			
		||||
                    val instance = it.value as StStructInstance
 | 
			
		||||
                    val struct = sourceSt.lookup(instance.structName) as StStruct
 | 
			
		||||
                    st.add(convert(instance, struct.fields))
 | 
			
		||||
                }
 | 
			
		||||
                StNodeType.STRUCT -> st.add(convert(it.value as StStruct))
 | 
			
		||||
                else -> { }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -37,12 +43,23 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
 | 
			
		||||
private fun convert(struct: StStruct): IRStStructDef =
 | 
			
		||||
    IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
 | 
			
		||||
 | 
			
		||||
    fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
 | 
			
		||||
        IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
 | 
			
		||||
    else
 | 
			
		||||
        IRStArrayElement(null, elt.number, elt.addressOfSymbol)
 | 
			
		||||
 | 
			
		||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement {
 | 
			
		||||
    return if (elt.boolean != null)
 | 
			
		||||
        IRStArrayElement(elt.boolean, null, null)
 | 
			
		||||
    else if(elt.number!=null)
 | 
			
		||||
        IRStArrayElement(null, elt.number, null)
 | 
			
		||||
    else {
 | 
			
		||||
        val symbol = elt.addressOfSymbol ?: (StStructInstanceBlockName + "." + (elt.structInstance ?: elt.structInstanceUninitialized))
 | 
			
		||||
        IRStArrayElement(null, null, symbol)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
 | 
			
		||||
 | 
			
		||||
    if('.' in variable.name) {
 | 
			
		||||
        return IRStStaticVariable(variable.name,
 | 
			
		||||
@@ -52,7 +69,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
 | 
			
		||||
            variable.initializationArrayValue?.map { convertArrayElt(it) },
 | 
			
		||||
            variable.length,
 | 
			
		||||
            variable.zpwish,
 | 
			
		||||
            variable.align)
 | 
			
		||||
            variable.align,
 | 
			
		||||
            variable.dirty)
 | 
			
		||||
    } else {
 | 
			
		||||
        fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
 | 
			
		||||
            if(array==null)
 | 
			
		||||
@@ -60,15 +78,16 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
 | 
			
		||||
            val newArray = mutableListOf<IRStArrayElement>()
 | 
			
		||||
            array.forEach {
 | 
			
		||||
                if(it.addressOfSymbol!=null) {
 | 
			
		||||
                    val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
 | 
			
		||||
                    newArray.add(IRStArrayElement(null, null, target.scopedName))
 | 
			
		||||
                    val target = variable.lookup(it.addressOfSymbol!!) ?:
 | 
			
		||||
                        throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
 | 
			
		||||
                    newArray.add(IRStArrayElement(null, null, target.scopedNameString))
 | 
			
		||||
                } else {
 | 
			
		||||
                    newArray.add(convertArrayElt(it))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return newArray
 | 
			
		||||
        }
 | 
			
		||||
        val scopedName = variable.scopedName
 | 
			
		||||
        val scopedName = variable.scopedNameString
 | 
			
		||||
        return IRStStaticVariable(scopedName,
 | 
			
		||||
            variable.dt,
 | 
			
		||||
            variable.initializationNumericValue,
 | 
			
		||||
@@ -76,7 +95,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
 | 
			
		||||
            fixupAddressOfInArray(variable.initializationArrayValue),
 | 
			
		||||
            variable.length,
 | 
			
		||||
            variable.zpwish,
 | 
			
		||||
            variable.align
 | 
			
		||||
            variable.align,
 | 
			
		||||
            variable.dirty
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -92,7 +112,7 @@ private fun convert(variable: StMemVar): IRStMemVar {
 | 
			
		||||
        )
 | 
			
		||||
    } else {
 | 
			
		||||
        val scopedName = try {
 | 
			
		||||
            variable.scopedName
 | 
			
		||||
            variable.scopedNameString
 | 
			
		||||
        } catch (_: UninitializedPropertyAccessException) {
 | 
			
		||||
            variable.name
 | 
			
		||||
        }
 | 
			
		||||
@@ -107,7 +127,7 @@ private fun convert(constant: StConstant): IRStConstant {
 | 
			
		||||
        constant.name
 | 
			
		||||
    } else {
 | 
			
		||||
        try {
 | 
			
		||||
            constant.scopedName
 | 
			
		||||
            constant.scopedNameString
 | 
			
		||||
        } catch (_: UninitializedPropertyAccessException) {
 | 
			
		||||
            constant.name
 | 
			
		||||
        }
 | 
			
		||||
@@ -116,16 +136,21 @@ private fun convert(constant: StConstant): IRStConstant {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private fun convert(variable: StMemorySlab): IRStMemorySlab {
 | 
			
		||||
    return if('.' in variable.name)
 | 
			
		||||
        IRStMemorySlab(variable.name, variable.size, variable.align)
 | 
			
		||||
private fun convert(mem: StMemorySlab): IRStMemorySlab {
 | 
			
		||||
    return if('.' in mem.name)
 | 
			
		||||
        IRStMemorySlab(mem.name, mem.size, mem.align)
 | 
			
		||||
    else
 | 
			
		||||
        IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
 | 
			
		||||
        IRStMemorySlab("$StMemorySlabBlockName.${mem.name}", mem.size, mem.align)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType, String>>): IRStStructInstance {
 | 
			
		||||
    val values = fields.zip(instance.initialValues).map { (field, value) ->
 | 
			
		||||
        val elt = convertArrayElt(value)
 | 
			
		||||
        IRStructInitValue(field.first.base, elt)
 | 
			
		||||
    }
 | 
			
		||||
    return if('.' in instance.name)
 | 
			
		||||
        IRStStructInstance(instance.name, instance.structName, values, instance.size)
 | 
			
		||||
    else
 | 
			
		||||
        IRStStructInstance("${StStructInstanceBlockName}.${instance.name}", instance.structName, values, instance.size)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,17 +3,21 @@ import prog8.code.core.*
 | 
			
		||||
 | 
			
		||||
internal object DummyMemsizer : IMemSizer {
 | 
			
		||||
    override fun memorySize(dt: DataType, numElements: Int?): Int {
 | 
			
		||||
        if(dt.isArray || dt.isSplitWordArray) {
 | 
			
		||||
        if(dt.isPointerArray)
 | 
			
		||||
            return 2 * numElements!!
 | 
			
		||||
        else if(dt.isArray || dt.isSplitWordArray) {
 | 
			
		||||
            require(numElements!=null)
 | 
			
		||||
            return when(dt.sub) {
 | 
			
		||||
                BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
 | 
			
		||||
                BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
 | 
			
		||||
                BaseDataType.LONG -> numElements*4
 | 
			
		||||
                BaseDataType.FLOAT -> numElements*5
 | 
			
		||||
                else -> throw IllegalArgumentException("invalid sub type")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return when {
 | 
			
		||||
            dt.isByteOrBool -> 1 * (numElements ?: 1)
 | 
			
		||||
            dt.isLong -> 4 * (numElements ?: 1)
 | 
			
		||||
            dt.isFloat -> 5 * (numElements ?: 1)
 | 
			
		||||
            else -> 2 * (numElements ?: 1)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ class TestIRPeepholeOpt: FunSpec({
 | 
			
		||||
            noSysInit = true,
 | 
			
		||||
            romable = false,
 | 
			
		||||
            compTarget = target,
 | 
			
		||||
            compilerVersion="99.99",
 | 
			
		||||
            loadAddress = target.PROGRAM_LOAD_ADDRESS,
 | 
			
		||||
            memtopAddress = 0xffffu
 | 
			
		||||
        )
 | 
			
		||||
@@ -204,21 +205,27 @@ class TestIRPeepholeOpt: FunSpec({
 | 
			
		||||
        val irProg = makeIRProgram(listOf(
 | 
			
		||||
            IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 0),
 | 
			
		||||
            IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 0),
 | 
			
		||||
            IRInstruction(Opcode.AND, IRDataType.LONG, reg1=42, immediate = 0),
 | 
			
		||||
            IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 255),
 | 
			
		||||
            IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535)
 | 
			
		||||
            IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535),
 | 
			
		||||
            IRInstruction(Opcode.OR, IRDataType.LONG, reg1=42, immediate = -1)
 | 
			
		||||
        ))
 | 
			
		||||
        irProg.chunks().single().instructions.size shouldBe 4
 | 
			
		||||
        irProg.chunks().single().instructions.size shouldBe 6
 | 
			
		||||
        val opt = IRPeepholeOptimizer(irProg)
 | 
			
		||||
        opt.optimize(true, ErrorReporterForTests())
 | 
			
		||||
        val instr = irProg.chunks().single().instructions
 | 
			
		||||
        instr.size shouldBe 4
 | 
			
		||||
        instr.size shouldBe 6
 | 
			
		||||
        instr[0].opcode shouldBe Opcode.LOAD
 | 
			
		||||
        instr[1].opcode shouldBe Opcode.LOAD
 | 
			
		||||
        instr[2].opcode shouldBe Opcode.LOAD
 | 
			
		||||
        instr[3].opcode shouldBe Opcode.LOAD
 | 
			
		||||
        instr[4].opcode shouldBe Opcode.LOAD
 | 
			
		||||
        instr[5].opcode shouldBe Opcode.LOAD
 | 
			
		||||
        instr[0].immediate shouldBe 0
 | 
			
		||||
        instr[1].immediate shouldBe 0
 | 
			
		||||
        instr[2].immediate shouldBe 255
 | 
			
		||||
        instr[3].immediate shouldBe 65535
 | 
			
		||||
        instr[2].immediate shouldBe 0
 | 
			
		||||
        instr[3].immediate shouldBe 255
 | 
			
		||||
        instr[4].immediate shouldBe 65535
 | 
			
		||||
        instr[5].immediate shouldBe -1
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
@@ -25,6 +25,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            noSysInit = false,
 | 
			
		||||
            romable = false,
 | 
			
		||||
            compTarget = target,
 | 
			
		||||
            compilerVersion="99.99",
 | 
			
		||||
            loadAddress = target.PROGRAM_LOAD_ADDRESS,
 | 
			
		||||
            memtopAddress = 0xffffu
 | 
			
		||||
        )
 | 
			
		||||
@@ -52,6 +53,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.UBYTE,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -61,6 +63,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.arrayFor(BaseDataType.UBYTE),
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            3u,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -70,6 +73,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.arrayFor(BaseDataType.UBYTE),
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            3u,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -79,6 +83,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.WORD,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -167,6 +172,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.FLOAT,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -238,6 +244,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.FLOAT,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -305,6 +312,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.FLOAT,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -333,7 +341,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
        val errors = ErrorReporterForTests()
 | 
			
		||||
        val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
 | 
			
		||||
        val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
 | 
			
		||||
        irChunks.size shouldBe 1
 | 
			
		||||
        irChunks.size shouldBe 2
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    test("integer comparison expressions against zero") {
 | 
			
		||||
@@ -360,6 +368,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.BYTE,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -431,6 +440,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.BYTE,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -498,6 +508,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
            DataType.BYTE,
 | 
			
		||||
            ZeropageWish.DONTCARE,
 | 
			
		||||
            0u,
 | 
			
		||||
            false,
 | 
			
		||||
            null,
 | 
			
		||||
            null,
 | 
			
		||||
            Position.DUMMY
 | 
			
		||||
@@ -526,7 +537,7 @@ class TestVmCodeGen: FunSpec({
 | 
			
		||||
        val errors = ErrorReporterForTests()
 | 
			
		||||
        val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
 | 
			
		||||
        val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
 | 
			
		||||
        irChunks.size shouldBe 1
 | 
			
		||||
        irChunks.size shouldBe 2
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    test("extsub allowed in ir-codegen") {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
}
 | 
			
		||||
@@ -8,7 +6,7 @@ dependencies {
 | 
			
		||||
    implementation(project(":codeCore"))
 | 
			
		||||
    implementation(project(":compilerAst"))
 | 
			
		||||
    // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
 | 
			
		||||
    // implementation "org.jetbrains.kotlin:kotlin-reflect"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,8 +27,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
 | 
			
		||||
        // @( &thing )  -->  thing  (but only if thing is a byte type!)
 | 
			
		||||
        val addrOf = memread.addressExpression as? AddressOf
 | 
			
		||||
        if(addrOf!=null) {
 | 
			
		||||
            if(addrOf.identifier.inferType(program).isBytes)
 | 
			
		||||
                return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
 | 
			
		||||
            if(addrOf.identifier?.inferType(program)?.isBytes==true)
 | 
			
		||||
                return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
 | 
			
		||||
        }
 | 
			
		||||
        return noModifications
 | 
			
		||||
    }
 | 
			
		||||
@@ -39,9 +39,12 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
 | 
			
		||||
            // see if LONG values may be reduced to something smaller
 | 
			
		||||
            val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
 | 
			
		||||
            if(smaller.type!=BaseDataType.LONG) {
 | 
			
		||||
                if(parent !is Assignment || !parent.target.inferType(program).isLong) {
 | 
			
		||||
                    // do NOT reduce the type if the target of the assignment is a long
 | 
			
		||||
                    return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(parent is Assignment) {
 | 
			
		||||
            val iDt = parent.target.inferType(program)
 | 
			
		||||
@@ -85,6 +88,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
 | 
			
		||||
     *        (X + c1) - c2  ->  X + (c1-c2)
 | 
			
		||||
     */
 | 
			
		||||
    override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
 | 
			
		||||
        if(expr.operator==".")
 | 
			
		||||
            return noModifications
 | 
			
		||||
        val modifications = mutableListOf<IAstModification>()
 | 
			
		||||
        val leftconst = expr.left.constValue(program)
 | 
			
		||||
        val rightconst = expr.right.constValue(program)
 | 
			
		||||
@@ -332,7 +337,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
 | 
			
		||||
 | 
			
		||||
            val constIndex = arrayIndexedExpression.indexer.constIndex()
 | 
			
		||||
            if (constIndex != null) {
 | 
			
		||||
                val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
 | 
			
		||||
                if(arrayIndexedExpression.plainarrayvar!=null) {
 | 
			
		||||
                    val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
 | 
			
		||||
                    if(arrayVar!=null) {
 | 
			
		||||
                        val array =arrayVar.value as? ArrayLiteral
 | 
			
		||||
                        if(array!=null) {
 | 
			
		||||
@@ -342,6 +348,9 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else if(arrayIndexedExpression.pointerderef!=null) {
 | 
			
		||||
                    TODO("constant fold pointer[i]  ${arrayIndexedExpression.position}")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return noModifications
 | 
			
		||||
@@ -390,9 +399,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
 | 
			
		||||
        val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
 | 
			
		||||
 | 
			
		||||
        val stepLiteral = iterableRange.step as? NumericLiteral
 | 
			
		||||
        require(loopvar.datatype.sub == null)
 | 
			
		||||
        val loopvarSimpleDt = loopvar.datatype.base
 | 
			
		||||
        when(loopvarSimpleDt) {
 | 
			
		||||
        require(loopvar.datatype.isBasic)
 | 
			
		||||
        when(val loopvarSimpleDt = loopvar.datatype.base) {
 | 
			
		||||
            BaseDataType.UBYTE -> {
 | 
			
		||||
                if(rangeFrom.type != BaseDataType.UBYTE) {
 | 
			
		||||
                    // attempt to translate the iterable into ubyte values
 | 
			
		||||
@@ -435,7 +443,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
 | 
			
		||||
        val numval = decl.value as? NumericLiteral
 | 
			
		||||
        if(decl.type== VarDeclType.CONST && numval!=null) {
 | 
			
		||||
            val valueDt = numval.inferType(program)
 | 
			
		||||
            if(valueDt issimpletype BaseDataType.LONG) {
 | 
			
		||||
            if(valueDt issimpletype BaseDataType.LONG || decl.datatype.isLong) {
 | 
			
		||||
                return noModifications  // this is handled in the numericalvalue case
 | 
			
		||||
            }
 | 
			
		||||
            if(!(valueDt istype decl.datatype)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,8 @@ class VarConstantValueTypeAdjuster(
 | 
			
		||||
    override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
 | 
			
		||||
 | 
			
		||||
        if(decl.parent is AnonymousScope)
 | 
			
		||||
            throw FatalAstException("vardecl may no longer occur in anonymousscope")
 | 
			
		||||
            throw FatalAstException("vardecl may no longer occur in anonymousscope ${decl.position}")
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
        val declConstValue = decl.value?.constValue(program)
 | 
			
		||||
        if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
 | 
			
		||||
            && declConstValue.type != decl.datatype.base) {
 | 
			
		||||
@@ -37,9 +36,6 @@ class VarConstantValueTypeAdjuster(
 | 
			
		||||
                errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        } catch (x: UndefinedSymbolError) {
 | 
			
		||||
            errors.err(x.message, x.position)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // replace variables by constants, if possible
 | 
			
		||||
        if(options.optimize) {
 | 
			
		||||
@@ -173,7 +169,12 @@ class VarConstantValueTypeAdjuster(
 | 
			
		||||
                val replaceFunc = if(t1.isBytes) {
 | 
			
		||||
                    if(t1 issimpletype BaseDataType.BYTE) "clamp__byte" else "clamp__ubyte"
 | 
			
		||||
                } else if(t1.isInteger) {
 | 
			
		||||
                    if(t1 issimpletype BaseDataType.WORD) "clamp__word" else "clamp__uword"
 | 
			
		||||
                    when {
 | 
			
		||||
                        t1 issimpletype BaseDataType.WORD -> "clamp__word"
 | 
			
		||||
                        t1 issimpletype BaseDataType.UWORD -> "clamp__uword"
 | 
			
		||||
                        t1 issimpletype BaseDataType.LONG -> "clamp__long"
 | 
			
		||||
                        else -> throw FatalAstException("clamp type")
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
 | 
			
		||||
                    return noModifications
 | 
			
		||||
@@ -195,10 +196,12 @@ class VarConstantValueTypeAdjuster(
 | 
			
		||||
                    else
 | 
			
		||||
                        "${funcName}__ubyte"
 | 
			
		||||
                } else if(t1.isInteger && t2.isInteger) {
 | 
			
		||||
                    replaceFunc = if(t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD)
 | 
			
		||||
                        "${funcName}__word"
 | 
			
		||||
                    else
 | 
			
		||||
                        "${funcName}__uword"
 | 
			
		||||
                    replaceFunc = when {
 | 
			
		||||
                        t1 issimpletype BaseDataType.LONG || t2 issimpletype BaseDataType.LONG -> "${funcName}__long"
 | 
			
		||||
                        t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD -> "${funcName}__word"
 | 
			
		||||
                        t1 issimpletype BaseDataType.UWORD || t2 issimpletype BaseDataType.UWORD -> "${funcName}__uword"
 | 
			
		||||
                        else -> throw FatalAstException("min/max type")
 | 
			
		||||
                    }
 | 
			
		||||
                } else if(t1.isNumeric && t2.isNumeric) {
 | 
			
		||||
                    errors.err("min/max not supported for floats", functionCallExpr.position)
 | 
			
		||||
                    return noModifications
 | 
			
		||||
@@ -218,6 +221,7 @@ class VarConstantValueTypeAdjuster(
 | 
			
		||||
                val replaceFunc = when {
 | 
			
		||||
                    dt.isSignedByte -> "abs__byte"
 | 
			
		||||
                    dt.isSignedWord -> "abs__word"
 | 
			
		||||
                    dt.isLong -> "abs__long"
 | 
			
		||||
                    dt.isFloat -> "abs__float"
 | 
			
		||||
                    dt.isUnsignedByte || dt.isUnsignedWord -> {
 | 
			
		||||
                        return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
 | 
			
		||||
@@ -250,6 +254,15 @@ class VarConstantValueTypeAdjuster(
 | 
			
		||||
                    functionCallExpr))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if(func==listOf("lsb") || func==listOf("msb")) {
 | 
			
		||||
            val t1 = functionCallExpr.args[0].inferType(program)
 | 
			
		||||
            if(t1.isLong) {
 | 
			
		||||
                val replaceFunc = func[0]+"__long"
 | 
			
		||||
                return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
 | 
			
		||||
                    IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
 | 
			
		||||
                    functionCallExpr))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return noModifications
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -313,7 +326,6 @@ internal class ConstantIdentifierReplacer(
 | 
			
		||||
        if(!dt.isKnown || !dt.isNumeric && !dt.isBool)
 | 
			
		||||
            return noModifications
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
        val cval = identifier.constValue(program) ?: return noModifications
 | 
			
		||||
        val arrayIdx = identifier.parent as? ArrayIndexedExpression
 | 
			
		||||
        if(arrayIdx!=null && cval.type.isNumeric) {
 | 
			
		||||
@@ -323,7 +335,7 @@ internal class ConstantIdentifierReplacer(
 | 
			
		||||
            val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
 | 
			
		||||
            return if(arrayIdx.parent is AssignTarget) {
 | 
			
		||||
                val memwrite = DirectMemoryWrite(add, identifier.position)
 | 
			
		||||
                    val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position)
 | 
			
		||||
                val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
 | 
			
		||||
                listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
 | 
			
		||||
            } else {
 | 
			
		||||
                val memread = DirectMemoryRead(add, identifier.position)
 | 
			
		||||
@@ -345,10 +357,6 @@ internal class ConstantIdentifierReplacer(
 | 
			
		||||
            cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
 | 
			
		||||
            else -> return noModifications
 | 
			
		||||
        }
 | 
			
		||||
        } catch (x: UndefinedSymbolError) {
 | 
			
		||||
            errors.err(x.message, x.position)
 | 
			
		||||
            return noModifications
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import prog8.ast.walk.AstWalker
 | 
			
		||||
import prog8.ast.walk.IAstModification
 | 
			
		||||
import prog8.code.core.*
 | 
			
		||||
import kotlin.math.abs
 | 
			
		||||
import kotlin.math.floor
 | 
			
		||||
import kotlin.math.log2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -18,8 +19,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
 | 
			
		||||
        // try to statically convert a literal value into one of the desired type
 | 
			
		||||
        val literal = typecast.expression as? NumericLiteral
 | 
			
		||||
        if (literal != null) {
 | 
			
		||||
            val newLiteral = literal.cast(typecast.type, typecast.implicit)
 | 
			
		||||
        if (literal != null && typecast.type.isBasic) {
 | 
			
		||||
            val newLiteral = literal.cast(typecast.type.base, typecast.implicit)
 | 
			
		||||
            if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
 | 
			
		||||
                mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
 | 
			
		||||
            }
 | 
			
		||||
@@ -33,7 +34,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (typecast.expression.inferType(program) issimpletype typecast.type) {
 | 
			
		||||
            if (typecast.expression.inferType(program) istype typecast.type) {
 | 
			
		||||
                // remove duplicate cast
 | 
			
		||||
                mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
 | 
			
		||||
            }
 | 
			
		||||
@@ -49,7 +50,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
            if(truepart.statements.singleOrNull() is Jump) {
 | 
			
		||||
                return listOf(
 | 
			
		||||
                    IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer),
 | 
			
		||||
                    IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse)
 | 
			
		||||
                    IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse)
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            if(elsepart.statements.singleOrNull() is Jump) {
 | 
			
		||||
@@ -57,7 +58,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                return listOf(
 | 
			
		||||
                    IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
 | 
			
		||||
                    IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
 | 
			
		||||
                    IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
 | 
			
		||||
                    IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse),
 | 
			
		||||
                    IAstModification.ReplaceNode(truepart, elsepart, ifElse)
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
@@ -90,6 +91,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
 | 
			
		||||
        if(expr.operator==".")
 | 
			
		||||
            return noModifications
 | 
			
		||||
        val newExpr = applyAbsorptionLaws(expr)
 | 
			
		||||
        if(newExpr!=null)
 | 
			
		||||
            return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
 | 
			
		||||
@@ -242,18 +245,24 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (rightVal?.number == 1.0) {
 | 
			
		||||
                if (rightDt != leftDt) {
 | 
			
		||||
                    val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
 | 
			
		||||
                if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
 | 
			
		||||
                    val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
 | 
			
		||||
                    if(!dt.isLong) {
 | 
			
		||||
                        val right = NumericLiteral(dt, rightVal.number, rightVal.position)
 | 
			
		||||
                        return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (rightVal?.number == 0.0) {
 | 
			
		||||
                if (rightDt != leftDt) {
 | 
			
		||||
                    val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
 | 
			
		||||
                if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
 | 
			
		||||
                    val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
 | 
			
		||||
                    if(!dt.isLong) {
 | 
			
		||||
                        val right = NumericLiteral(dt, rightVal.number, rightVal.position)
 | 
			
		||||
                        return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (expr.operator=="!=") {
 | 
			
		||||
            if(rightDt.isBool && leftDt.isBool) {
 | 
			
		||||
                val rightConstBool = rightVal?.asBooleanValue
 | 
			
		||||
@@ -262,18 +271,24 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (rightVal?.number == 1.0) {
 | 
			
		||||
                if(rightDt!=leftDt) {
 | 
			
		||||
                    val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
 | 
			
		||||
                if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
 | 
			
		||||
                    val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
 | 
			
		||||
                    if(!dt.isLong) {
 | 
			
		||||
                        val right = NumericLiteral(dt, rightVal.number, rightVal.position)
 | 
			
		||||
                        return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (rightVal?.number == 0.0) {
 | 
			
		||||
                if(rightDt!=leftDt) {
 | 
			
		||||
                    val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
 | 
			
		||||
                if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
 | 
			
		||||
                    val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
 | 
			
		||||
                    if(!dt.isLong) {
 | 
			
		||||
                        val right = NumericLiteral(dt, rightVal.number, rightVal.position)
 | 
			
		||||
                        return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(expr.operator in arrayOf("and", "or", "xor")) {
 | 
			
		||||
            if(leftVal!=null) {
 | 
			
		||||
@@ -420,6 +435,104 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
//        fun isPowerOfTwo(number: Double): Boolean {
 | 
			
		||||
//            val intValue = number.toInt()
 | 
			
		||||
//            return intValue > 0 && (intValue and (intValue - 1)) == 0
 | 
			
		||||
//        }
 | 
			
		||||
 | 
			
		||||
        fun isFactorOf256(number: Double): Boolean {
 | 
			
		||||
            val intValue = number.toInt()
 | 
			
		||||
            return intValue >= 256 && (intValue and 0xFF) == 0
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (leftDt.isUnsignedWord && rightVal!=null) {
 | 
			
		||||
            if (expr.operator == ">" && rightVal.number == 255.0 || expr.operator == ">=" && rightVal.number == 256.0) {
 | 
			
		||||
                // uword > 255  -->  msb(value)!=0
 | 
			
		||||
                // uword >= 256 -->  msb(value)!=0
 | 
			
		||||
                expr.operator = "!="
 | 
			
		||||
                expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
 | 
			
		||||
                expr.right = NumericLiteral(BaseDataType.UBYTE, 0.0, expr.right.position)
 | 
			
		||||
                expr.linkParents(parent)
 | 
			
		||||
            }
 | 
			
		||||
            else if(expr.operator==">=" && isFactorOf256(rightVal.number)) {
 | 
			
		||||
                // uword >= $xx00 --> msb(value)>=xx
 | 
			
		||||
                val msbFraction = floor(rightVal.number / 256.0)
 | 
			
		||||
                expr.operator = ">="
 | 
			
		||||
                expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
 | 
			
		||||
                expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
 | 
			
		||||
                expr.linkParents(parent)
 | 
			
		||||
            }
 | 
			
		||||
            else if(expr.operator==">" && isFactorOf256(rightVal.number+1)) {
 | 
			
		||||
                // uword > $xxFF --> msb(value)>xx
 | 
			
		||||
                val msbFraction = floor((rightVal.number) / 256.0)
 | 
			
		||||
                expr.operator = ">"
 | 
			
		||||
                expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
 | 
			
		||||
                expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
 | 
			
		||||
                expr.linkParents(parent)
 | 
			
		||||
            }
 | 
			
		||||
            else if(expr.operator == "<" && rightVal.number == 256.0 || expr.operator == "<=" && rightVal.number == 255.0) {
 | 
			
		||||
                // uword < 256  -->  msb(value)==0
 | 
			
		||||
                // uword <= 255 -->  msb(value)==0
 | 
			
		||||
                expr.operator = "=="
 | 
			
		||||
                expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
 | 
			
		||||
                expr.right = NumericLiteral(BaseDataType.UBYTE, 0.0, expr.right.position)
 | 
			
		||||
                expr.linkParents(parent)
 | 
			
		||||
            }
 | 
			
		||||
            else if(expr.operator == "<" && isFactorOf256(rightVal.number)) {
 | 
			
		||||
                // uword < $xx00 --> msb(value)<xx
 | 
			
		||||
                val msbFraction = floor(rightVal.number / 256.0)
 | 
			
		||||
                expr.operator = "<"
 | 
			
		||||
                expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
 | 
			
		||||
                expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
 | 
			
		||||
                expr.linkParents(parent)
 | 
			
		||||
            }
 | 
			
		||||
            else if(expr.operator=="<=" && isFactorOf256(rightVal.number+1)) {
 | 
			
		||||
                // uword <= $xxFF --> msb(value)<=xx
 | 
			
		||||
                val msbFraction = floor((rightVal.number) / 256.0)
 | 
			
		||||
                expr.operator = "<="
 | 
			
		||||
                expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
 | 
			
		||||
                expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
 | 
			
		||||
                expr.linkParents(parent)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return noModifications
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
 | 
			
		||||
        if(arrayIndexedExpression.indexer.constIndex()==0) {
 | 
			
		||||
            if(arrayIndexedExpression.plainarrayvar!=null) {
 | 
			
		||||
                val binexprParent = arrayIndexedExpression.parent as? BinaryExpression
 | 
			
		||||
                if(binexprParent?.operator!=".") {
 | 
			
		||||
                    val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
 | 
			
		||||
                    if(dt.isPointer) {
 | 
			
		||||
                        // pointer[0]  -->   pointer^^
 | 
			
		||||
                        val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
 | 
			
		||||
                        return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
 | 
			
		||||
                    }
 | 
			
		||||
                } else if(arrayIndexedExpression.pointerderef==null) {
 | 
			
		||||
                    // possibly     pointer[0].field   -->  pointer.field
 | 
			
		||||
                    val target = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
 | 
			
		||||
                    if(target?.datatype?.isPointer==true) {
 | 
			
		||||
                        val field = (binexprParent.right as? IdentifierReference)?.nameInSource
 | 
			
		||||
                        if(field!=null) {
 | 
			
		||||
                            val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource + field, false, arrayIndexedExpression.plainarrayvar!!.position)
 | 
			
		||||
                            return listOf(IAstModification.ReplaceNode(arrayIndexedExpression.parent, deref, arrayIndexedExpression.parent.parent))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            val ptrDeref = arrayIndexedExpression.pointerderef
 | 
			
		||||
            if(ptrDeref!=null) {
 | 
			
		||||
                val dt = ptrDeref.inferType(program).getOrUndef()
 | 
			
		||||
                if(dt.isPointer) {
 | 
			
		||||
                    // ptr1.ptr2[0] --> ptr1.ptr2^^
 | 
			
		||||
                    val deref = PtrDereference(ptrDeref.chain, true, ptrDeref.position)
 | 
			
		||||
                    return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return noModifications
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -499,7 +612,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                }
 | 
			
		||||
                else if (valueDt issimpletype BaseDataType.BYTE) {
 | 
			
		||||
                    // useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
 | 
			
		||||
                    val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
 | 
			
		||||
                    val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
 | 
			
		||||
                    return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -516,7 +629,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                }
 | 
			
		||||
                else if (argDt issimpletype BaseDataType.BYTE) {
 | 
			
		||||
                    // useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
 | 
			
		||||
                    val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
 | 
			
		||||
                    val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
 | 
			
		||||
                    return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -555,7 +668,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
        if(functionCallExpr.target.nameInSource == listOf("mkword")) {
 | 
			
		||||
            if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
 | 
			
		||||
                // just cast the lsb to uword
 | 
			
		||||
                val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
 | 
			
		||||
                val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
 | 
			
		||||
                return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -711,9 +824,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                            // just use:  msb(value) as type
 | 
			
		||||
                            val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
 | 
			
		||||
                            return if(leftDt.isSignedWord)
 | 
			
		||||
                                TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
 | 
			
		||||
                                TypecastExpression(msb, DataType.BYTE, true, expr.position)
 | 
			
		||||
                            else
 | 
			
		||||
                                TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
 | 
			
		||||
                                TypecastExpression(msb, DataType.UWORD, true, expr.position)
 | 
			
		||||
                        }
 | 
			
		||||
                        else -> return null
 | 
			
		||||
                    }
 | 
			
		||||
@@ -839,14 +952,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                    // shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
 | 
			
		||||
                    val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
 | 
			
		||||
                    val mkword =  FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
 | 
			
		||||
                    return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
 | 
			
		||||
                    return TypecastExpression(mkword, DataType.WORD, true, expr.position)
 | 
			
		||||
                }
 | 
			
		||||
                else if (amount > 8) {
 | 
			
		||||
                    // same as above but with residual shifts.
 | 
			
		||||
                    val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
 | 
			
		||||
                    val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
 | 
			
		||||
                    val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
 | 
			
		||||
                    return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
 | 
			
		||||
                    return TypecastExpression(mkword, DataType.WORD, true, expr.position)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
@@ -887,12 +1000,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                else if(amount==8) {
 | 
			
		||||
                    // shift right by 8 bits is just a byte operation: msb(X) as uword
 | 
			
		||||
                    val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
 | 
			
		||||
                    return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
 | 
			
		||||
                    return TypecastExpression(msb, DataType.UWORD, true, expr.position)
 | 
			
		||||
                }
 | 
			
		||||
                else if (amount > 8) {
 | 
			
		||||
                    // same as above but with residual shifts.
 | 
			
		||||
                    val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
 | 
			
		||||
                    return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
 | 
			
		||||
                    return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BaseDataType.WORD -> {
 | 
			
		||||
@@ -903,12 +1016,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
 | 
			
		||||
                else if(amount == 8) {
 | 
			
		||||
                    // shift right by 8 bits is just a byte operation: msb(X) as byte  (will get converted to word later)
 | 
			
		||||
                    val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
 | 
			
		||||
                    return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
 | 
			
		||||
                    return TypecastExpression(msb, DataType.BYTE, true, expr.position)
 | 
			
		||||
                }
 | 
			
		||||
                else if(amount > 8) {
 | 
			
		||||
                    // same as above but with residual shifts. Take care to do signed shift.
 | 
			
		||||
                    val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
 | 
			
		||||
                    val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
 | 
			
		||||
                    val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
 | 
			
		||||
                    return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -113,7 +113,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private fun makeFullyScoped(identifier: IdentifierReference) {
 | 
			
		||||
            identifier.targetStatement(program)?.let { target ->
 | 
			
		||||
            identifier.targetStatement()?.let { target ->
 | 
			
		||||
                val scoped = (target as INamedStatement).scopedName
 | 
			
		||||
                val scopedIdent = IdentifierReference(scoped, identifier.position)
 | 
			
		||||
                modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
 | 
			
		||||
@@ -149,7 +149,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
 | 
			
		||||
                when (it) {
 | 
			
		||||
                    is NumericLiteral -> it.copy()
 | 
			
		||||
                    is IdentifierReference -> {
 | 
			
		||||
                        val target = it.targetStatement(program) ?: return emptyList()
 | 
			
		||||
                        val target = it.targetStatement() ?: return emptyList()
 | 
			
		||||
                        val scoped = (target as INamedStatement).scopedName
 | 
			
		||||
                        IdentifierReference(scoped, it.position)
 | 
			
		||||
                    }
 | 
			
		||||
@@ -205,7 +205,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification>  {
 | 
			
		||||
        val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
 | 
			
		||||
        val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) as? Subroutine
 | 
			
		||||
        return if(sub==null || !canInline(sub, functionCallStatement))
 | 
			
		||||
            noModifications
 | 
			
		||||
        else
 | 
			
		||||
@@ -213,7 +213,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
 | 
			
		||||
        val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
 | 
			
		||||
        val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) as? Subroutine
 | 
			
		||||
        if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
 | 
			
		||||
            require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
 | 
			
		||||
                "invalid inline sub at ${sub.position}"
 | 
			
		||||
@@ -243,13 +243,13 @@ class Inliner(private val program: Program, private val options: CompilationOpti
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean {
 | 
			
		||||
        if(!sub.inline)
 | 
			
		||||
        if (!sub.inline)
 | 
			
		||||
            return false
 | 
			
		||||
        if(options.compTarget.name!=VMTarget.NAME) {
 | 
			
		||||
        if (options.compTarget.name != VMTarget.NAME) {
 | 
			
		||||
            val stmt = sub.statements.single()
 | 
			
		||||
            if (stmt is IFunctionCall) {
 | 
			
		||||
                val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
 | 
			
		||||
                return existing !is VarDecl
 | 
			
		||||
                return existing !is VarDecl && existing !is StructFieldRef
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
            val functionName = functionCallStatement.target.nameInSource[0]
 | 
			
		||||
            if (functionName in functions.purefunctionNames) {
 | 
			
		||||
                if("ignore_unused" !in parent.definingBlock.options())
 | 
			
		||||
                    errors.info("statement has no effect (function return value is discarded)", functionCallStatement.position)
 | 
			
		||||
                    errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
 | 
			
		||||
                return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -29,7 +29,14 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
        if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
 | 
			
		||||
            val arg = functionCallStatement.args.single()
 | 
			
		||||
            val stringVar: IdentifierReference? = if(arg is AddressOf) {
 | 
			
		||||
                if(arg.arrayIndex==null) arg.identifier else null
 | 
			
		||||
                if(arg.arrayIndex==null) {
 | 
			
		||||
                    if(arg.identifier!=null)
 | 
			
		||||
                        arg.identifier
 | 
			
		||||
                    else
 | 
			
		||||
                        null // struct can't have string fields so nothing to look at here
 | 
			
		||||
                } else {
 | 
			
		||||
                    null
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                arg as? IdentifierReference
 | 
			
		||||
            }
 | 
			
		||||
@@ -82,12 +89,11 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
        // empty true part? switch with the else part
 | 
			
		||||
        if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
 | 
			
		||||
            val invertedCondition = invertCondition(ifElse.condition, program)
 | 
			
		||||
            val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
 | 
			
		||||
            val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
 | 
			
		||||
            return listOf(
 | 
			
		||||
                    IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
 | 
			
		||||
                    IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse),
 | 
			
		||||
                    IAstModification.ReplaceNode(ifElse.elsepart, emptyscope, ifElse)
 | 
			
		||||
                    IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +112,7 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
            if(ifElse.truepart.statements.singleOrNull() is Return) {
 | 
			
		||||
                val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
 | 
			
		||||
                return listOf(
 | 
			
		||||
                    IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
 | 
			
		||||
                    IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse),
 | 
			
		||||
                    IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
@@ -146,8 +152,8 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
            if (range.size() == 1) {
 | 
			
		||||
                // for loop over a (constant) range of just a single value-- optimize the loop away
 | 
			
		||||
                // loopvar/reg = range value , follow by block
 | 
			
		||||
                val scope = AnonymousScope(mutableListOf(), forLoop.position)
 | 
			
		||||
                scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
 | 
			
		||||
                val scope = AnonymousScope.empty(forLoop.position)
 | 
			
		||||
                scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
 | 
			
		||||
                scope.statements.addAll(forLoop.body.statements)
 | 
			
		||||
                return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
 | 
			
		||||
            }
 | 
			
		||||
@@ -161,8 +167,8 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
                    // loop over string of length 1 -> just assign the single character
 | 
			
		||||
                    val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
 | 
			
		||||
                    val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
 | 
			
		||||
                    val scope = AnonymousScope(mutableListOf(), forLoop.position)
 | 
			
		||||
                    scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
 | 
			
		||||
                    val scope = AnonymousScope.empty(forLoop.position)
 | 
			
		||||
                    scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
 | 
			
		||||
                    scope.statements.addAll(forLoop.body.statements)
 | 
			
		||||
                    return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
 | 
			
		||||
                }
 | 
			
		||||
@@ -173,9 +179,9 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
                    // loop over array of length 1 -> just assign the single value
 | 
			
		||||
                    val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number
 | 
			
		||||
                    if(av!=null) {
 | 
			
		||||
                        val scope = AnonymousScope(mutableListOf(), forLoop.position)
 | 
			
		||||
                        val scope = AnonymousScope.empty(forLoop.position)
 | 
			
		||||
                        scope.statements.add(Assignment(
 | 
			
		||||
                                AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
 | 
			
		||||
                                AssignTarget(forLoop.loopVar, null, null, null, false, position = forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
 | 
			
		||||
                                AssignmentOrigin.OPTIMIZER, forLoop.position))
 | 
			
		||||
                        scope.statements.addAll(forLoop.body.statements)
 | 
			
		||||
                        return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
 | 
			
		||||
@@ -342,6 +348,39 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // pointer arithmetic for 6502 target
 | 
			
		||||
            if (options.compTarget.cpu != CpuType.VIRTUAL) {
 | 
			
		||||
                if(bexpr.operator=="+" && !bexpr.right.isSimple && !assignment.isAugmentable) {
 | 
			
		||||
                    if(targetIDt.isUnsignedWord || targetIDt.getOrUndef().isPointerToByte) {
 | 
			
		||||
                        val leftDt = bexpr.left.inferType(program).getOrUndef()
 | 
			
		||||
                        val rightDt = bexpr.right.inferType(program).getOrUndef()
 | 
			
		||||
 | 
			
		||||
                        fun setSizedValue(a: Assignment, value: Expression, size: Int) {
 | 
			
		||||
                            val sized = BinaryExpression(value, "*", NumericLiteral.optimalInteger(size, value.position), value.position)
 | 
			
		||||
                            a.value = sized
 | 
			
		||||
                            sized.linkParents(a)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (leftDt.isPointer && !leftDt.isPointerToByte) {
 | 
			
		||||
                            // uword x = pointer + value    -->  x=value * sizeof ,  x += pointer
 | 
			
		||||
                            val size = leftDt.size(options.compTarget)
 | 
			
		||||
                            setSizedValue(assignment, bexpr.right, size)
 | 
			
		||||
                            val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.left, bexpr.position)
 | 
			
		||||
                            val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
 | 
			
		||||
                            return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
 | 
			
		||||
                        } else if (rightDt.isPointer && !rightDt.isPointerToByte) {
 | 
			
		||||
                            // uword x = value + pointer  -->  x=value * sizeof,  x += pointer
 | 
			
		||||
                            val size = rightDt.size(options.compTarget)
 | 
			
		||||
                            setSizedValue(assignment, bexpr.left, size)
 | 
			
		||||
                            assignment.linkParents(parent)
 | 
			
		||||
                            val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.right, bexpr.position)
 | 
			
		||||
                            val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
 | 
			
		||||
                            return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // word = lsb(word)
 | 
			
		||||
@@ -466,7 +505,7 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
        return IfElse(
 | 
			
		||||
            compare,
 | 
			
		||||
            AnonymousScope(mutableListOf(assign), position),
 | 
			
		||||
            AnonymousScope(mutableListOf(), position),
 | 
			
		||||
            AnonymousScope.empty(),
 | 
			
		||||
            position
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
@@ -501,6 +540,27 @@ class StatementOptimizer(private val program: Program,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(whenStmt.betterAsOnGoto(program, options)) {
 | 
			
		||||
            // rewrite when into a on..goto , which is faster and also smaller for ~5+ cases
 | 
			
		||||
            var elseJump: Jump? = null
 | 
			
		||||
            val jumps = mutableListOf<Pair<Int, Jump>>()
 | 
			
		||||
            whenStmt.choices.forEach { choice ->
 | 
			
		||||
                if(choice.values==null) {
 | 
			
		||||
                    elseJump = choice.statements.statements.single() as Jump
 | 
			
		||||
                } else {
 | 
			
		||||
                    choice.values!!.forEach { value ->
 | 
			
		||||
                        jumps.add(value.constValue(program)!!.number.toInt() to choice.statements.statements.single() as Jump)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val jumpLabels = jumps.sortedBy { it.first }.map { it.second.target as IdentifierReference }
 | 
			
		||||
 | 
			
		||||
            val elsePart = if(elseJump==null) null else AnonymousScope(mutableListOf(elseJump), elseJump.position)
 | 
			
		||||
            val onGoto = OnGoto(false, whenStmt.condition, jumpLabels, elsePart, whenStmt.position)
 | 
			
		||||
            return listOf(IAstModification.ReplaceNode(whenStmt, onGoto, parent))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return noModifications
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ import prog8.ast.expressions.TypecastExpression
 | 
			
		||||
import prog8.ast.statements.*
 | 
			
		||||
import prog8.ast.walk.AstWalker
 | 
			
		||||
import prog8.ast.walk.IAstModification
 | 
			
		||||
import prog8.code.INTERNED_STRINGS_MODULENAME
 | 
			
		||||
import prog8.code.PROG8_CONTAINER_MODULES
 | 
			
		||||
import prog8.code.core.ICompilationTarget
 | 
			
		||||
import prog8.code.core.IErrorReporter
 | 
			
		||||
import prog8.compiler.CallGraph
 | 
			
		||||
@@ -93,7 +93,7 @@ class UnusedCodeRemover(private val program: Program,
 | 
			
		||||
    override fun after(block: Block, parent: Node): Iterable<IAstModification> {
 | 
			
		||||
        if("force_output" !in block.options()) {
 | 
			
		||||
            if (block.containsNoCodeNorVars) {
 | 
			
		||||
                if (block.name != INTERNED_STRINGS_MODULENAME && "ignore_unused" !in block.options()) {
 | 
			
		||||
                if (block.name !in PROG8_CONTAINER_MODULES && "ignore_unused" !in block.options()) {
 | 
			
		||||
                    if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
 | 
			
		||||
                        errors.info("removing unused block '${block.name}'", block.position)
 | 
			
		||||
                }
 | 
			
		||||
@@ -181,6 +181,11 @@ class UnusedCodeRemover(private val program: Program,
 | 
			
		||||
                                        val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
 | 
			
		||||
                                        val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
 | 
			
		||||
                                        if(declIndex==singleUseIndex-1) {
 | 
			
		||||
                                            val callStruct = (assignment.value as IFunctionCall).target.targetStructDecl()
 | 
			
		||||
                                            if(callStruct!=null) {
 | 
			
		||||
                                                // don't turn a struct instance allocation call to a void call, instead, remove everything
 | 
			
		||||
                                                return listOf(IAstModification.Remove(assignment, assignment.parent as IStatementContainer))
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                if("ignore_unused" !in decl.definingBlock.options())
 | 
			
		||||
                                                    errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
 | 
			
		||||
                                                val fcall = assignment.value as IFunctionCall
 | 
			
		||||
@@ -191,6 +196,7 @@ class UnusedCodeRemover(private val program: Program,
 | 
			
		||||
                                                )
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    errors.info("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
 | 
			
		||||
                                }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,9 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    id("application")
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
    // id("com.github.johnrengelman.shadow") version "8.1.1"
 | 
			
		||||
    id("io.github.goooler.shadow") version "8.1.8"
 | 
			
		||||
    // id("io.github.goooler.shadow") version "8.1.8"
 | 
			
		||||
    id("com.gradleup.shadow") version "9.2.2"
 | 
			
		||||
    id("com.peterabeles.gversion") version "1.10.3"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -18,18 +17,15 @@ dependencies {
 | 
			
		||||
    implementation(project(":codeGenExperimental"))
 | 
			
		||||
    implementation(project(":virtualmachine"))
 | 
			
		||||
    // implementation(project(":beanshell"))
 | 
			
		||||
    implementation("org.antlr:antlr4-runtime:4.13.2")
 | 
			
		||||
    // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
			
		||||
    // implementation("org.jetbrains.kotlin:kotlin-reflect")
 | 
			
		||||
    implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
 | 
			
		||||
    implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
 | 
			
		||||
 | 
			
		||||
    testImplementation(project(":codeCore"))
 | 
			
		||||
    testImplementation(project(":intermediate"))
 | 
			
		||||
    testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
 | 
			
		||||
    testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
 | 
			
		||||
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
 | 
			
		||||
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
configurations.all {
 | 
			
		||||
@@ -83,6 +79,8 @@ gversion {
 | 
			
		||||
    classPackage = "prog8.buildversion"
 | 
			
		||||
    className = "Version"
 | 
			
		||||
    language = "kotlin"
 | 
			
		||||
    debug = false
 | 
			
		||||
    annotate = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tasks.build {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,7 @@
 | 
			
		||||
    <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
 | 
			
		||||
    <orderEntry type="module" module-name="codeCore" />
 | 
			
		||||
    <orderEntry type="module" module-name="simpleAst" />
 | 
			
		||||
    <orderEntry type="module" module-name="compilerAst" />
 | 
			
		||||
@@ -25,6 +23,6 @@
 | 
			
		||||
    <orderEntry type="module" module-name="codeGenIntermediate" />
 | 
			
		||||
    <orderEntry type="module" module-name="virtualmachine" />
 | 
			
		||||
    <orderEntry type="module" module-name="intermediate" />
 | 
			
		||||
    <orderEntry type="library" name="antlr.antlr4" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
 | 
			
		||||
  </component>
 | 
			
		||||
</module>
 | 
			
		||||
							
								
								
									
										144
									
								
								compiler/res/prog8lib/bcd.p8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								compiler/res/prog8lib/bcd.p8
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
bcd {
 | 
			
		||||
    ; Decimal addition an subtraction routines.
 | 
			
		||||
    ; For CPUs that support BCD mode (binary coded decimal) (not all 6502 variants support this mode...)
 | 
			
		||||
    ; This is useful for example for counting decimal score in a game, to avoid costly conversion to a decimal display string. Just print the hex representation.
 | 
			
		||||
 | 
			
		||||
    sub addb(byte a, byte b) -> byte {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a += b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub addub(ubyte a, ubyte b) -> ubyte {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a += b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub addw(word a, word b) -> word {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a += b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub adduw(uword a, uword b) -> uword {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a += b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub addl(long a, long b) -> long {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a += b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub addtol(^^long a, long b) {
 | 
			
		||||
        ; -- inplace long BCD addition (avoids copying long values by value)
 | 
			
		||||
        setbcd()
 | 
			
		||||
        ;; NOT YET IMPLEMENTED IN PROG8: a^^ += b, so inline asm
 | 
			
		||||
        %asm {{
 | 
			
		||||
            lda  p8v_a
 | 
			
		||||
            ldy  p8v_a+1
 | 
			
		||||
            sta  P8ZP_SCRATCH_W1
 | 
			
		||||
            sty  P8ZP_SCRATCH_W1+1
 | 
			
		||||
            ldy  #0
 | 
			
		||||
            clc
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            adc  p8v_b
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            iny
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            adc  p8v_b+1
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            iny
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            adc  p8v_b+2
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            iny
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            adc  p8v_b+3
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
        }}
 | 
			
		||||
        clearbcd()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub subb(byte a, byte b) -> byte {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a -= b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub subub(ubyte a, ubyte b) -> ubyte {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a -= b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub subuw(uword a, uword b) -> uword {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a -= b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub subl(long a, long b) -> long {
 | 
			
		||||
        setbcd()
 | 
			
		||||
        a -= b
 | 
			
		||||
        clearbcd()
 | 
			
		||||
        return a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub subfroml(^^long a, long b) {
 | 
			
		||||
        ; -- inplace long BCD subtraction (avoids copying long values by value)
 | 
			
		||||
        setbcd()
 | 
			
		||||
        ;; NOT YET IMPLEMENTED IN PROG8: a^^ -= b, so inline asm
 | 
			
		||||
        %asm {{
 | 
			
		||||
            lda  p8v_a
 | 
			
		||||
            ldy  p8v_a+1
 | 
			
		||||
            sta  P8ZP_SCRATCH_W1
 | 
			
		||||
            sty  P8ZP_SCRATCH_W1+1
 | 
			
		||||
            ldy  #0
 | 
			
		||||
            sec
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            sbc  p8v_b
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            iny
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            sbc  p8v_b+1
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            iny
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            sbc  p8v_b+2
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            iny
 | 
			
		||||
            lda  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
            sbc  p8v_b+3
 | 
			
		||||
            sta  (P8ZP_SCRATCH_W1),y
 | 
			
		||||
        }}
 | 
			
		||||
        clearbcd()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    inline asmsub setbcd() {
 | 
			
		||||
        ; be safe and 6502 compatible and prohibit interrupts during BCD mode
 | 
			
		||||
        %asm {{
 | 
			
		||||
            php
 | 
			
		||||
            sei
 | 
			
		||||
            sed
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline asmsub clearbcd() {
 | 
			
		||||
        %asm {{
 | 
			
		||||
            plp
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -133,6 +133,29 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
 | 
			
		||||
    }}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
asmsub SETTIML(long jiffies @R0R1_32) {
 | 
			
		||||
    ; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
 | 
			
		||||
    %asm {{
 | 
			
		||||
        lda  cx16.r0
 | 
			
		||||
        ldx  cx16.r0+1
 | 
			
		||||
        ldy  cx16.r0+2
 | 
			
		||||
        jmp  SETTIM
 | 
			
		||||
    }}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
 | 
			
		||||
    ; --  like RDTIM() and returning the timer value as a 32 bit (lower 24 bits used) value.
 | 
			
		||||
    %asm {{
 | 
			
		||||
        jsr  RDTIM
 | 
			
		||||
        sta  cx16.r0
 | 
			
		||||
        stx  cx16.r0+1
 | 
			
		||||
        sty  cx16.r0+2
 | 
			
		||||
        lda  #0
 | 
			
		||||
        sta  cx16.r0+3
 | 
			
		||||
        rts
 | 
			
		||||
    }}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub CLEARST() {
 | 
			
		||||
    ; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
 | 
			
		||||
    ;    Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
 | 
			
		||||
@@ -317,10 +340,25 @@ c128 {
 | 
			
		||||
    &ubyte  VM4     = $0A2F         ; starting page for VDC attribute mem
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
 | 
			
		||||
 | 
			
		||||
extsub $FF6E = JSRFAR()
 | 
			
		||||
extsub $FF68 = SETBNK(ubyte databank @A, ubyte filenamebank @X)
 | 
			
		||||
extsub $FF47 = SPIN_SPOUT() clobbers(A)                         ; set up serial bus for fast communications mode
 | 
			
		||||
extsub $FF4A = CLOSE_ALL(ubyte device @X)  clobbers(X)          ; close all channels to specific device
 | 
			
		||||
extsub $FF4D = C64_MODE()                                       ; restart machine in C64 mode (does not return)
 | 
			
		||||
extsub $FF50 = DMA_CALL(ubyte bank @X, ubyte command @Y) clobbers(A,X) ; send a command to a DMA device
 | 
			
		||||
extsub $FF53 = BOOT_CALL(ubyte device @X, ubyte drive @A) clobbers(A,X,Y) ; try to autoboot the given disk
 | 
			
		||||
extsub $FF56 = PHOENIX() clobbers(A,X,Y)                        ; search for and autostart ROMs, cartridges, then default disk
 | 
			
		||||
extsub $FF59 = LKUPLA(ubyte lfn @A) -> bool @Pc, ubyte @X       ; look up logical file number to see if it's open; returns device
 | 
			
		||||
extsub $FF5C = LKUPSA(ubyte sa @Y) -> bool @Pc, ubyte @A, ubyte @X ; look up secondary address to see if it's in use; returns lfn and device
 | 
			
		||||
extsub $FF5F = SWAPPER() clobbers(A,X,Y)                        ; swap active screen (between 40- and 80-column)
 | 
			
		||||
extsub $FF62 = DLCHR() clobbers(A,X,Y)                          ; copy character ROM into VDC video RAM
 | 
			
		||||
extsub $FF65 = PFKEY(ubyte zpaddr @A, ubyte key @X, ubyte length @Y) ; redefine programmable function key (string descriptor in zp, addr in A)
 | 
			
		||||
extsub $FF68 = SETBNK(ubyte data_bank @A, ubyte filename_bank @X) ; set memory bank for load/save
 | 
			
		||||
extsub $FF6B = GETCFG(ubyte bank @X) -> ubyte @A                ; translate bank number to MMU configuration register value
 | 
			
		||||
extsub $FF6E = JSRFAR() clobbers(A,X)                           ; call routine in another bank (parameters set in zero page addresses 2-8)
 | 
			
		||||
extsub $FF71 = JMPFAR() clobbers(A,X)                           ; jump without return to another bank (parameters set as for JSRFAR)
 | 
			
		||||
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
 | 
			
		||||
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
 | 
			
		||||
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
 | 
			
		||||
extsub $FF7D = PRIMM()                                          ; print immediate string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
; ---- C128 specific system utility routines: ----
 | 
			
		||||
@@ -419,12 +457,14 @@ sys {
 | 
			
		||||
 | 
			
		||||
    const ubyte target = 128         ;  compilation target specifier.  255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
 | 
			
		||||
 | 
			
		||||
    const ubyte SIZEOF_BOOL  = 1
 | 
			
		||||
    const ubyte SIZEOF_BYTE  = 1
 | 
			
		||||
    const ubyte SIZEOF_UBYTE = 1
 | 
			
		||||
    const ubyte SIZEOF_WORD  = 2
 | 
			
		||||
    const ubyte SIZEOF_UWORD = 2
 | 
			
		||||
    const ubyte SIZEOF_FLOAT = 5
 | 
			
		||||
    const ubyte SIZEOF_BOOL  = sizeof(bool)
 | 
			
		||||
    const ubyte SIZEOF_BYTE  = sizeof(byte)
 | 
			
		||||
    const ubyte SIZEOF_UBYTE = sizeof(ubyte)
 | 
			
		||||
    const ubyte SIZEOF_WORD  = sizeof(word)
 | 
			
		||||
    const ubyte SIZEOF_UWORD = sizeof(uword)
 | 
			
		||||
    const ubyte SIZEOF_LONG  = sizeof(long)
 | 
			
		||||
    const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
 | 
			
		||||
    const ubyte SIZEOF_FLOAT = sizeof(float)
 | 
			
		||||
    const byte  MIN_BYTE     = -128
 | 
			
		||||
    const byte  MAX_BYTE     = 127
 | 
			
		||||
    const ubyte MIN_UBYTE    = 0
 | 
			
		||||
@@ -489,6 +529,7 @@ save_SCRATCH_ZPWORD2	.word  ?
 | 
			
		||||
 | 
			
		||||
asmsub  set_irq(uword handler @AY) clobbers(A)  {
 | 
			
		||||
	%asm {{
 | 
			
		||||
	    php
 | 
			
		||||
		sei
 | 
			
		||||
        sta  _vector
 | 
			
		||||
        sty  _vector+1
 | 
			
		||||
@@ -496,7 +537,7 @@ asmsub  set_irq(uword handler @AY) clobbers(A)  {
 | 
			
		||||
		sta  cbm.CINV
 | 
			
		||||
		lda  #>_irq_handler
 | 
			
		||||
		sta  cbm.CINV+1
 | 
			
		||||
		cli
 | 
			
		||||
		plp
 | 
			
		||||
		rts
 | 
			
		||||
_irq_handler
 | 
			
		||||
        jsr  sys.save_prog8_internals
 | 
			
		||||
@@ -528,6 +569,7 @@ _vector	.word ?
 | 
			
		||||
 | 
			
		||||
asmsub  restore_irq() clobbers(A) {
 | 
			
		||||
	%asm {{
 | 
			
		||||
	    php
 | 
			
		||||
		sei
 | 
			
		||||
		lda  #<cbm.IRQDFRT
 | 
			
		||||
		sta  cbm.CINV
 | 
			
		||||
@@ -537,24 +579,34 @@ asmsub  restore_irq() clobbers(A) {
 | 
			
		||||
		sta  c64.IREQMASK	; enable raster irq
 | 
			
		||||
		lda  #%10000001
 | 
			
		||||
		sta  c64.CIA1ICR	; restore CIA1 irq
 | 
			
		||||
		cli
 | 
			
		||||
		plp
 | 
			
		||||
		rts
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
asmsub  set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
 | 
			
		||||
	%asm {{
 | 
			
		||||
	    php
 | 
			
		||||
        sei
 | 
			
		||||
        sta  _vector
 | 
			
		||||
        sty  _vector+1
 | 
			
		||||
        sta  user_vector
 | 
			
		||||
        sty  user_vector+1
 | 
			
		||||
 | 
			
		||||
        lda  #%01111111
 | 
			
		||||
        sta  c64.CIA1ICR    ; "switch off" interrupts signals from cia-1
 | 
			
		||||
        sta  c64.CIA2ICR    ; "switch off" interrupts signals from cia-2
 | 
			
		||||
        lda  c64.CIA1ICR    ; ack previous irq
 | 
			
		||||
        lda  c64.CIA2ICR    ; ack previous irq
 | 
			
		||||
        lda  cx16.r0
 | 
			
		||||
        ldy  cx16.r0+1
 | 
			
		||||
        jsr  _setup_raster_irq
 | 
			
		||||
        jsr  sys.set_rasterline
 | 
			
		||||
        lda  #%00000001
 | 
			
		||||
        sta  c64.IREQMASK   ; enable raster interrupt signals from vic
 | 
			
		||||
 | 
			
		||||
        lda  #<_raster_irq_handler
 | 
			
		||||
        sta  cbm.CINV
 | 
			
		||||
        lda  #>_raster_irq_handler
 | 
			
		||||
        sta  cbm.CINV+1
 | 
			
		||||
        cli
 | 
			
		||||
        plp
 | 
			
		||||
        rts
 | 
			
		||||
 | 
			
		||||
_raster_irq_handler
 | 
			
		||||
@@ -576,34 +628,46 @@ _raster_irq_handler
 | 
			
		||||
		pla
 | 
			
		||||
		rti
 | 
			
		||||
_run_custom
 | 
			
		||||
		jmp  (_vector)
 | 
			
		||||
		jmp  (user_vector)
 | 
			
		||||
		.section BSS
 | 
			
		||||
_vector	.word ?
 | 
			
		||||
user_vector	.word ?
 | 
			
		||||
		.send BSS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_setup_raster_irq
 | 
			
		||||
		pha
 | 
			
		||||
		lda  #%01111111
 | 
			
		||||
		sta  c64.CIA1ICR    ; "switch off" interrupts signals from cia-1
 | 
			
		||||
		sta  c64.CIA2ICR    ; "switch off" interrupts signals from cia-2
 | 
			
		||||
		and  c64.SCROLY
 | 
			
		||||
		sta  c64.SCROLY     ; clear most significant bit of raster position
 | 
			
		||||
		lda  c64.CIA1ICR    ; ack previous irq
 | 
			
		||||
		lda  c64.CIA2ICR    ; ack previous irq
 | 
			
		||||
		pla
 | 
			
		||||
		sta  c64.RASTER     ; set the raster line number where interrupt should occur
 | 
			
		||||
		cpy  #0
 | 
			
		||||
		beq  +
 | 
			
		||||
		lda  c64.SCROLY
 | 
			
		||||
		ora  #%10000000
 | 
			
		||||
		sta  c64.SCROLY     ; set most significant bit of raster position
 | 
			
		||||
+		lda  #%00000001
 | 
			
		||||
		sta  c64.IREQMASK   ; enable raster interrupt signals from vic
 | 
			
		||||
		rts
 | 
			
		||||
        ; !notreached!
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    asmsub update_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
 | 
			
		||||
        ; -- just update the IRQ handler and raster line position for the raster IRQ
 | 
			
		||||
        ;    this is much more efficient than calling set_rasterirq() again every time.
 | 
			
		||||
        ;    (but you have to call that one initially at least once to setup the prog8 handler itself)
 | 
			
		||||
        %asm {{
 | 
			
		||||
            php
 | 
			
		||||
            sei
 | 
			
		||||
            sta  sys.set_rasterirq.user_vector
 | 
			
		||||
            sty  sys.set_rasterirq.user_vector+1
 | 
			
		||||
            lda  cx16.r0L
 | 
			
		||||
            ldy  cx16.r0H
 | 
			
		||||
            jsr  sys.set_rasterline
 | 
			
		||||
            plp
 | 
			
		||||
            rts
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    asmsub  set_rasterline(uword line @AY) {
 | 
			
		||||
        ; -- only set a new raster line for the raster IRQ
 | 
			
		||||
        %asm {{
 | 
			
		||||
            sta  c64.RASTER     ; set the raster line number where interrupt should occur
 | 
			
		||||
            lda  c64.SCROLY
 | 
			
		||||
            and  #%01111111
 | 
			
		||||
            cpy  #0
 | 
			
		||||
            beq  +
 | 
			
		||||
            ora  #%10000000
 | 
			
		||||
    +       sta  c64.SCROLY     ; clear most significant bit of raster position
 | 
			
		||||
            rts
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    asmsub  reset_system()  {
 | 
			
		||||
        ; Soft-reset the system back to initial power-on Basic prompt.
 | 
			
		||||
        %asm {{
 | 
			
		||||
@@ -663,7 +727,27 @@ _loop       lda  P8ZP_SCRATCH_W1
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
 | 
			
		||||
    asmsub waitrasterline(uword line @AY) {
 | 
			
		||||
        ; -- CPU busy wait until the given raster line is reached
 | 
			
		||||
        %asm {{
 | 
			
		||||
            cpy  #0
 | 
			
		||||
            bne  _larger
 | 
			
		||||
-           cmp  c64.RASTER
 | 
			
		||||
            bne  -
 | 
			
		||||
            bit  c64.SCROLY
 | 
			
		||||
            bmi  -
 | 
			
		||||
            rts
 | 
			
		||||
_larger
 | 
			
		||||
            cmp  c64.RASTER
 | 
			
		||||
            bne  _larger
 | 
			
		||||
            bit  c64.SCROLY
 | 
			
		||||
            bpl  _larger
 | 
			
		||||
            rts
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
 | 
			
		||||
        ; Called when the compiler wants to assign a string value to another string.
 | 
			
		||||
        %asm {{
 | 
			
		||||
		sta  P8ZP_SCRATCH_W1
 | 
			
		||||
@@ -946,6 +1030,18 @@ _no_msb_size
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
 | 
			
		||||
        %asm {{
 | 
			
		||||
            ; return the address like JSR would push onto the stack:  address-1,  MSB first then LSB
 | 
			
		||||
            cpx  #0
 | 
			
		||||
            bne  +
 | 
			
		||||
            dey
 | 
			
		||||
+           dex
 | 
			
		||||
            tya
 | 
			
		||||
            rts
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline asmsub pop() -> ubyte @A {
 | 
			
		||||
        %asm {{
 | 
			
		||||
            pla
 | 
			
		||||
@@ -960,6 +1056,48 @@ _no_msb_size
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline asmsub pushl(long value @R0R1_32) {
 | 
			
		||||
        %asm {{
 | 
			
		||||
            lda  cx16.r0
 | 
			
		||||
            pha
 | 
			
		||||
            lda  cx16.r0+1
 | 
			
		||||
            pha
 | 
			
		||||
            lda  cx16.r0+2
 | 
			
		||||
            pha
 | 
			
		||||
            lda  cx16.r0+3
 | 
			
		||||
            pha
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline asmsub popl() -> long @R0R1_32 {
 | 
			
		||||
        %asm {{
 | 
			
		||||
            pla
 | 
			
		||||
            sta  cx16.r0+3
 | 
			
		||||
            pla
 | 
			
		||||
            sta  cx16.r0+2
 | 
			
		||||
            pla
 | 
			
		||||
            sta  cx16.r0+1
 | 
			
		||||
            pla
 | 
			
		||||
            sta  cx16.r0
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub cpu_is_65816() -> bool {
 | 
			
		||||
        ; Returns true when you have a 65816 cpu, false when it's a 6502.
 | 
			
		||||
        ; The SuperCPU expansion for the C64/C128 contains a 65816.
 | 
			
		||||
        %asm {{
 | 
			
		||||
			php
 | 
			
		||||
			clv
 | 
			
		||||
			.byte $e2, $ea  ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
 | 
			
		||||
			bvc +
 | 
			
		||||
			lda #1
 | 
			
		||||
			plp
 | 
			
		||||
			rts
 | 
			
		||||
+			lda #0
 | 
			
		||||
			plp
 | 
			
		||||
			rts
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cx16 {
 | 
			
		||||
@@ -984,6 +1122,7 @@ cx16 {
 | 
			
		||||
    &uword r14 = $1bfc
 | 
			
		||||
    &uword r15 = $1bfe
 | 
			
		||||
 | 
			
		||||
    ; signed word versions
 | 
			
		||||
    &word r0s  = $1be0
 | 
			
		||||
    &word r1s  = $1be2
 | 
			
		||||
    &word r2s  = $1be4
 | 
			
		||||
@@ -1001,6 +1140,7 @@ cx16 {
 | 
			
		||||
    &word r14s = $1bfc
 | 
			
		||||
    &word r15s = $1bfe
 | 
			
		||||
 | 
			
		||||
    ; ubyte versions (low and high bytes)
 | 
			
		||||
    &ubyte r0L  = $1be0
 | 
			
		||||
    &ubyte r1L  = $1be2
 | 
			
		||||
    &ubyte r2L  = $1be4
 | 
			
		||||
@@ -1035,6 +1175,7 @@ cx16 {
 | 
			
		||||
    &ubyte r14H = $1bfd
 | 
			
		||||
    &ubyte r15H = $1bff
 | 
			
		||||
 | 
			
		||||
    ; signed byte versions (low and high bytes)
 | 
			
		||||
    &byte r0sL  = $1be0
 | 
			
		||||
    &byte r1sL  = $1be2
 | 
			
		||||
    &byte r2sL  = $1be4
 | 
			
		||||
@@ -1069,6 +1210,42 @@ cx16 {
 | 
			
		||||
    &byte r14sH = $1bfd
 | 
			
		||||
    &byte r15sH = $1bff
 | 
			
		||||
 | 
			
		||||
    ; boolean versions (low and high bytes)
 | 
			
		||||
    &bool r0bL  = $1be0
 | 
			
		||||
    &bool r1bL  = $1be2
 | 
			
		||||
    &bool r2bL  = $1be4
 | 
			
		||||
    &bool r3bL  = $1be6
 | 
			
		||||
    &bool r4bL  = $1be8
 | 
			
		||||
    &bool r5bL  = $1bea
 | 
			
		||||
    &bool r6bL  = $1bec
 | 
			
		||||
    &bool r7bL  = $1bee
 | 
			
		||||
    &bool r8bL  = $1bf0
 | 
			
		||||
    &bool r9bL  = $1bf2
 | 
			
		||||
    &bool r10bL = $1bf4
 | 
			
		||||
    &bool r11bL = $1bf6
 | 
			
		||||
    &bool r12bL = $1bf8
 | 
			
		||||
    &bool r13bL = $1bfa
 | 
			
		||||
    &bool r14bL = $1bfc
 | 
			
		||||
    &bool r15bL = $1bfe
 | 
			
		||||
 | 
			
		||||
    &bool r0bH  = $1be1
 | 
			
		||||
    &bool r1bH  = $1be3
 | 
			
		||||
    &bool r2bH  = $1be5
 | 
			
		||||
    &bool r3bH  = $1be7
 | 
			
		||||
    &bool r4bH  = $1be9
 | 
			
		||||
    &bool r5bH  = $1beb
 | 
			
		||||
    &bool r6bH  = $1bed
 | 
			
		||||
    &bool r7bH  = $1bef
 | 
			
		||||
    &bool r8bH  = $1bf1
 | 
			
		||||
    &bool r9bH  = $1bf3
 | 
			
		||||
    &bool r10bH = $1bf5
 | 
			
		||||
    &bool r11bH = $1bf7
 | 
			
		||||
    &bool r12bH = $1bf9
 | 
			
		||||
    &bool r13bH = $1bfb
 | 
			
		||||
    &bool r14bH = $1bfd
 | 
			
		||||
    &bool r15bH = $1bff
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    asmsub save_virtual_registers() clobbers(A,Y) {
 | 
			
		||||
        %asm {{
 | 
			
		||||
            ldy  #31
 | 
			
		||||
@@ -1097,12 +1274,6 @@ cx16 {
 | 
			
		||||
            rts
 | 
			
		||||
        }}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub cpu_is_65816() -> bool {
 | 
			
		||||
        ; Returns true when you have a 65816 cpu, false when it's a 6502.
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p8_sys_startup {
 | 
			
		||||
@@ -1131,6 +1302,11 @@ asmsub  init_system()  {
 | 
			
		||||
        lda  #0
 | 
			
		||||
        sta  c64.BGCOL0
 | 
			
		||||
        jsr  disable_runstop_and_charsetswitch
 | 
			
		||||
        ; basic is not banked in, adjust MEMTOP
 | 
			
		||||
        ldx  #<$c000
 | 
			
		||||
        ldy  #>$c000
 | 
			
		||||
        clc
 | 
			
		||||
        jsr  cbm.MEMTOP
 | 
			
		||||
        cli
 | 
			
		||||
        rts
 | 
			
		||||
    }}
 | 
			
		||||
@@ -1152,6 +1328,10 @@ asmsub  cleanup_at_exit() {
 | 
			
		||||
        sta  $ff00          ; default bank 15
 | 
			
		||||
        jsr  cbm.CLRCHN		; reset i/o channels
 | 
			
		||||
        jsr  enable_runstop_and_charsetswitch
 | 
			
		||||
        ldx  #<$ff00
 | 
			
		||||
        ldy  #>$ff00
 | 
			
		||||
        clc
 | 
			
		||||
        jsr  cbm.MEMTOP     ; adjust MEMTOP to original value again
 | 
			
		||||
        lda  _exitcarry
 | 
			
		||||
        lsr  a
 | 
			
		||||
        lda  _exitcode
 | 
			
		||||
 
 | 
			
		||||
@@ -16,26 +16,6 @@ const ubyte DEFAULT_HEIGHT = 25
 | 
			
		||||
extsub $FFD2 = chrout(ubyte character @ A)    ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sub  clear_screen() {
 | 
			
		||||
    chrout(147)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub  cls() {
 | 
			
		||||
    chrout(147)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub home() {
 | 
			
		||||
    chrout(19)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub nl() {
 | 
			
		||||
    chrout('\n')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub spc() {
 | 
			
		||||
    chrout(' ')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub bell() {
 | 
			
		||||
    chrout(7)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user