mirror of
				https://github.com/irmen/prog8.git
				synced 2025-10-31 15: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) | ||||
|             if(base.isSplitWordArray) | ||||
|                 require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD) | ||||
|         when { | ||||
|             base.isPointerArray -> { | ||||
|                 require(sub!=null || subType!=null || subTypeFromAntlr!=null) | ||||
|             } | ||||
|             base.isArray -> { | ||||
|                 require(sub != null && subType==null && subTypeFromAntlr==null) | ||||
|                 if(base.isSplitWordArray) | ||||
|                     require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD) | ||||
|             } | ||||
|             base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" } | ||||
|             base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"} | ||||
|             else -> { | ||||
|                 require(sub == null || (subType == null && subTypeFromAntlr == null)) { | ||||
|                     "sub and subtype can't both be set" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else if(base==BaseDataType.STR) | ||||
|             require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" } | ||||
|         else | ||||
|             require(sub == null) { "only string and array base types can have a subtype"} | ||||
|     } | ||||
|  | ||||
|     override fun equals(other: Any?): Boolean { | ||||
|         if (this === other) return true | ||||
|         if (other !is DataType) return false | ||||
|         return base == other.base && sub == other.sub | ||||
|         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,16 +74,11 @@ 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") | ||||
|             if(withExt.isReadable()) | ||||
|                 vm.runProgram(withExt.readText(), quiet) | ||||
|             else | ||||
|                 throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file") | ||||
|         } | ||||
|         val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir") | ||||
|         if(withExt.isReadable()) | ||||
|             vm.runProgram(withExt.readText(), quiet) | ||||
|         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() | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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,143 +500,185 @@ $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 -> { | ||||
|                 if(asmgen.options.romable) { | ||||
|                     val indexVar = asmgen.getTempVarName(BaseDataType.UBYTE) | ||||
|                     asmgen.out(""" | ||||
|  | ||||
|         fun iterateStrings() { | ||||
|             if(asmgen.options.romable) { | ||||
|                 val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt) | ||||
|                 asmgen.out(""" | ||||
|                         ldy  #0 | ||||
|                         sty  $indexVar | ||||
| $loopLabel              lda  $iterableName,y | ||||
|                         beq  $endLabel | ||||
|                         sta  ${asmgen.asmVariableName(stmt.variable)}""") | ||||
|                     asmgen.translate(stmt.statements) | ||||
|                     asmgen.out(""" | ||||
|                 asmgen.translate(stmt.statements) | ||||
|                 asmgen.out(""" | ||||
|                         inc  $indexVar | ||||
|                         ldy  $indexVar | ||||
|                         bne  $loopLabel | ||||
| $endLabel""") | ||||
|                 } else { | ||||
|                     val indexVar = asmgen.makeLabel("for_index") | ||||
|                     asmgen.out(""" | ||||
|             } else { | ||||
|                 val indexVar = asmgen.makeLabel("for_index") | ||||
|                 asmgen.out(""" | ||||
|                         ldy  #0 | ||||
|                         sty  $indexVar | ||||
| $loopLabel              lda  $iterableName,y | ||||
|                         beq  $endLabel | ||||
|                         sta  ${asmgen.asmVariableName(stmt.variable)}""") | ||||
|                     asmgen.translate(stmt.statements) | ||||
|                     asmgen.out(""" | ||||
|                 asmgen.translate(stmt.statements) | ||||
|                 asmgen.out(""" | ||||
|                         inc  $indexVar | ||||
|                         ldy  $indexVar | ||||
|                         bne  $loopLabel | ||||
| $indexVar   .byte  0                         | ||||
| $endLabel""") | ||||
|                 } | ||||
|             } | ||||
|             iterableDt.isByteArray || iterableDt.isBoolArray -> { | ||||
|                 val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(iterableDt.elementType().base) else asmgen.makeLabel("for_index") | ||||
|                 asmgen.out(""" | ||||
|         } | ||||
|  | ||||
|         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) { | ||||
|                     asmgen.out(""" | ||||
|             asmgen.translate(stmt.statements) | ||||
|             if(numElements<=255u) { | ||||
|                 asmgen.out(""" | ||||
|                         ldy  $indexVar | ||||
|                         iny | ||||
|                         cpy  #$numElements | ||||
|                         beq  $endLabel | ||||
|                         bne  $loopLabel""") | ||||
|                 } else { | ||||
|                     // length is 256 | ||||
|                     asmgen.out(""" | ||||
|             } else { | ||||
|                 // length is 256 | ||||
|                 asmgen.out(""" | ||||
|                         ldy  $indexVar | ||||
|                         iny | ||||
|                         bne  $loopLabel | ||||
|                         beq  $endLabel""") | ||||
|                 } | ||||
|                 if(!asmgen.options.romable) { | ||||
|                     if(numElements>=16) { | ||||
|                         // allocate index var on ZP if possible, otherwise inline | ||||
|                         val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) | ||||
|                         result.fold( | ||||
|                             success = { (address, _, _)-> asmgen.out("""$indexVar = $address  ; auto zp UBYTE""") }, | ||||
|                             failure = { asmgen.out("$indexVar    .byte  0") } | ||||
|                         ) | ||||
|                     } else { | ||||
|                         asmgen.out("$indexVar    .byte  0") | ||||
|                     } | ||||
|                 } | ||||
|                 asmgen.out(endLabel) | ||||
|             } | ||||
|             iterableDt.isSplitWordArray -> { | ||||
|                 val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index") | ||||
|                 val loopvarName = asmgen.asmVariableName(stmt.variable) | ||||
|                 asmgen.out(""" | ||||
|             if(!asmgen.options.romable) { | ||||
|                 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( | ||||
|                         success = { (address, _, _)-> asmgen.out("""$indexVar = $address  ; auto zp UBYTE""") }, | ||||
|                         failure = { asmgen.out("$indexVar    .byte  0") } | ||||
|                     ) | ||||
|                 } else { | ||||
|                     asmgen.out("$indexVar    .byte  0") | ||||
|                 } | ||||
|             } | ||||
|             asmgen.out(endLabel) | ||||
|         } | ||||
|  | ||||
|         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 | ||||
| $loopLabel          sty  $indexVar | ||||
|                     lda  ${iterableName}_lsb,y | ||||
|                     sta  $loopvarName | ||||
|                     lda  ${iterableName}_msb,y | ||||
|                     sta  $loopvarName+1""") | ||||
|                 asmgen.translate(stmt.statements) | ||||
|                 if(numElements<=255) { | ||||
|                     asmgen.out(""" | ||||
|             asmgen.translate(stmt.statements) | ||||
|             if(numElements<=255u) { | ||||
|                 asmgen.out(""" | ||||
|                         ldy  $indexVar | ||||
|                         iny | ||||
|                         cpy  #$numElements | ||||
|                         beq  $endLabel | ||||
|                         bne  $loopLabel""") | ||||
|                 } else { | ||||
|                     // length is 256 | ||||
|                     asmgen.out(""" | ||||
|             } else { | ||||
|                 // length is 256 | ||||
|                 asmgen.out(""" | ||||
|                         ldy  $indexVar | ||||
|                         iny | ||||
|                         bne  $loopLabel | ||||
|                         beq  $endLabel""") | ||||
|                 } | ||||
|                 if(!asmgen.options.romable) { | ||||
|                     if(numElements>=16) { | ||||
|                         // allocate index var on ZP if possible, otherwise inline | ||||
|                         val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) | ||||
|                         result.fold( | ||||
|                             success = { (address, _, _)-> asmgen.out("""$indexVar = $address  ; auto zp UBYTE""") }, | ||||
|                             failure = { asmgen.out("$indexVar    .byte  0") } | ||||
|                         ) | ||||
|                     } else { | ||||
|                         asmgen.out("$indexVar    .byte  0") | ||||
|                     } | ||||
|                 } | ||||
|                 asmgen.out(endLabel) | ||||
|             } | ||||
|             iterableDt.isWordArray -> { | ||||
|                 val length = numElements * 2 | ||||
|                 val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index") | ||||
|                 val loopvarName = asmgen.asmVariableName(stmt.variable) | ||||
|                 asmgen.out(""" | ||||
|             if(!asmgen.options.romable) { | ||||
|                 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( | ||||
|                         success = { (address, _, _)-> asmgen.out("""$indexVar = $address  ; auto zp UBYTE""") }, | ||||
|                         failure = { asmgen.out("$indexVar    .byte  0") } | ||||
|                     ) | ||||
|                 } else { | ||||
|                     asmgen.out("$indexVar    .byte  0") | ||||
|                 } | ||||
|             } | ||||
|             asmgen.out(endLabel) | ||||
|         } | ||||
|  | ||||
|         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 | ||||
| $loopLabel          sty  $indexVar | ||||
|                     lda  $iterableName,y | ||||
|                     sta  $loopvarName | ||||
|                     lda  $iterableName+1,y | ||||
|                     sta  $loopvarName+1""") | ||||
|                 asmgen.translate(stmt.statements) | ||||
|                 if(length<=127) { | ||||
|             if(actuallyLongs) { | ||||
|                 asmgen.out(""" | ||||
|                     lda  $iterableName+2,y | ||||
|                     sta  $loopvarName+2 | ||||
|                     lda  $iterableName+3,y | ||||
|                     sta  $loopvarName+3""") | ||||
|             } | ||||
|             asmgen.translate(stmt.statements) | ||||
|             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,23 +686,29 @@ $loopLabel          sty  $indexVar | ||||
|                         bne  $loopLabel | ||||
|                         beq  $endLabel""") | ||||
|                 } | ||||
|                 if(!asmgen.options.romable) { | ||||
|                     if(length>=16) { | ||||
|                         // allocate index var on ZP if possible, otherwise inline | ||||
|                         val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) | ||||
|                         result.fold( | ||||
|                             success = { (address, _, _)-> asmgen.out("""$indexVar = $address  ; auto zp UBYTE""") }, | ||||
|                             failure = { asmgen.out("$indexVar    .byte  0") } | ||||
|                         ) | ||||
|                     } else { | ||||
|                         asmgen.out("$indexVar    .byte  0") | ||||
|                     } | ||||
|             } | ||||
|             if(!asmgen.options.romable) { | ||||
|                 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( | ||||
|                         success = { (address, _, _)-> asmgen.out("""$indexVar = $address  ; auto zp UBYTE""") }, | ||||
|                         failure = { asmgen.out("$indexVar    .byte  0") } | ||||
|                     ) | ||||
|                 } else { | ||||
|                     asmgen.out("$indexVar    .byte  0") | ||||
|                 } | ||||
|                 asmgen.out(endLabel) | ||||
|             } | ||||
|             iterableDt.isFloatArray -> { | ||||
|                 throw AssemblyError("for loop with floating point variables is not supported") | ||||
|             } | ||||
|             asmgen.out(endLabel) | ||||
|         } | ||||
|  | ||||
|         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) | ||||
|                 asmgen.out("  lda  $varname+1") | ||||
|                 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 { | ||||
|                         val varname = if(left.identifier!!.type.isSplitWordArray) { | ||||
|                             if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb" | ||||
|                         } else { | ||||
|                             left.identifier.name | ||||
|                             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 { | ||||
|                         val varname = if(left.identifier!!.type.isSplitWordArray) { | ||||
|                             if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb" | ||||
|                         } else { | ||||
|                             left.identifier.name | ||||
|                             left.identifier!!.name | ||||
|                         } | ||||
|                         asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) | ||||
|                         translateAYEquals("#<$varname", "#>$varname") | ||||
|   | ||||
| @@ -9,32 +9,92 @@ 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) | ||||
|  | ||||
|         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) | ||||
|         } 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.truevalue, RegisterOrPair.A) | ||||
|                 asmgen.jmp(endLabel) | ||||
|                 asmgen.out(falseLabel) | ||||
|                 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.truevalue, RegisterOrPair.AY) | ||||
|                 asmgen.jmp(endLabel) | ||||
|                 asmgen.out(falseLabel) | ||||
|                 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.truevalue, RegisterOrPair.FAC1, true) | ||||
|                 asmgen.jmp(endLabel) | ||||
|                 asmgen.out(falseLabel) | ||||
|                 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) | ||||
|             } | ||||
| @@ -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 } | ||||
|             notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach { | ||||
|                 uninitializedVariable2asm(it) | ||||
|             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) } | ||||
|                 asmgen.out("  .send $section") | ||||
|             } | ||||
|             aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach { | ||||
|                 uninitializedVariable2asm(it) | ||||
|  | ||||
|             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,8 +998,15 @@ 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,11 +6,12 @@ import prog8.codegen.cpu6502.AsmGen6502Internal | ||||
|  | ||||
|  | ||||
| internal enum class TargetStorageKind { | ||||
|     VARIABLE, | ||||
|     VARIABLE,       // non-pointer variable | ||||
|     ARRAY, | ||||
|     MEMORY, | ||||
|     REGISTER, | ||||
|     VOID       // assign nothing - used in multi-value assigns for void placeholders | ||||
|     POINTER, | ||||
|     VOID              // assign nothing - used in multi-value assigns for void placeholders | ||||
| } | ||||
|  | ||||
| internal enum class SourceStorageKind { | ||||
| @@ -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 { | ||||
|             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) | ||||
|         } | ||||
|         return ExpressionCodeResult(result, vmDt, resultRegister, -1) | ||||
|     } | ||||
|  | ||||
|     private fun translate(mem: PtMemoryByte): ExpressionCodeResult { | ||||
| @@ -219,31 +403,32 @@ 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. | ||||
|                 val ptrName = (ptrWithOffset.left as PtIdentifier).name | ||||
|                 val offsetReg = codeGen.registers.next(IRDataType.BYTE) | ||||
|                 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) | ||||
|         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 pointerReg = codeGen.registers.next(IRDataType.WORD) | ||||
|                     result += IRCodeChunk(null, null).also { | ||||
|                         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.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) | ||||
|                 else | ||||
|                     translateExpression(ptrWithOffset.right) | ||||
|                 addToResult(result, tr, tr.resultReg, -1) | ||||
|                 val ptrName = (ptrWithOffset.left as PtIdentifier).name | ||||
|                 addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null) | ||||
|                 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)) { | ||||
|             // LOADIX only works with byte index. | ||||
|             val tr = if(offsetTypecast?.value?.type?.isByte==true) | ||||
|                 translateExpression(offsetTypecast.value) | ||||
|             else | ||||
|                 translateExpression(ptrWithOffset.right) | ||||
|             addToResult(result, tr, tr.resultReg, -1) | ||||
|             val ptrName = (ptrWithOffset.left as PtIdentifier).name | ||||
|             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,49 +837,61 @@ 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) { | ||||
|             "+" -> operatorPlus(binExpr, vmDt) | ||||
|             "-" -> operatorMinus(binExpr, vmDt) | ||||
|             "*" -> operatorMultiply(binExpr, vmDt) | ||||
|             "/" -> operatorDivide(binExpr, vmDt, signed) | ||||
|             "%" -> operatorModulo(binExpr, vmDt) | ||||
|             "|" -> operatorOr(binExpr, vmDt, true) | ||||
|             "&" -> operatorAnd(binExpr, vmDt, true) | ||||
|             "^", "xor" -> operatorXor(binExpr, vmDt) | ||||
|             "or" -> operatorOr(binExpr, vmDt, false) | ||||
|             "and" -> operatorAnd(binExpr, vmDt, false) | ||||
|             "<<" -> operatorShiftLeft(binExpr, vmDt) | ||||
|             ">>" -> operatorShiftRight(binExpr, vmDt, signed) | ||||
|             "==" -> operatorEquals(binExpr, vmDt, false) | ||||
|             "!=" -> operatorEquals(binExpr, vmDt, true) | ||||
|             "<" -> operatorLessThan(binExpr, vmDt, signed, false) | ||||
|             ">" -> operatorGreaterThan(binExpr, vmDt, signed, false) | ||||
|             "<=" -> operatorLessThan(binExpr, vmDt, signed, true) | ||||
|             ">=" -> operatorGreaterThan(binExpr, vmDt, signed, true) | ||||
|             else -> throw AssemblyError("weird operator ${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, binExpr.left.type) | ||||
|                 "/" -> operatorDivide(binExpr, binExpr.left.type) | ||||
|                 "%" -> operatorModulo(binExpr, vmDt) | ||||
|                 "|" -> operatorOr(binExpr, vmDt, true) | ||||
|                 "&" -> operatorAnd(binExpr, vmDt, true) | ||||
|                 "^", "xor" -> operatorXor(binExpr, vmDt) | ||||
|                 "or" -> operatorOr(binExpr, vmDt, false) | ||||
|                 "and" -> operatorAnd(binExpr, vmDt, false) | ||||
|                 "<<" -> operatorShiftLeft(binExpr, vmDt) | ||||
|                 ">>" -> operatorShiftRight(binExpr, vmDt, signed) | ||||
|                 "==" -> operatorEquals(binExpr, vmDt, false) | ||||
|                 "!=" -> operatorEquals(binExpr, vmDt, true) | ||||
|                 "<" -> operatorLessThan(binExpr, vmDt, signed, false) | ||||
|                 ">" -> operatorGreaterThan(binExpr, vmDt, signed, false) | ||||
|                 "<=" -> operatorLessThan(binExpr, vmDt, signed, true) | ||||
|                 ">=" -> operatorGreaterThan(binExpr, vmDt, signed, true) | ||||
|                 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,11 +1542,11 @@ 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) | ||||
|                         IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) | ||||
|                     else | ||||
|                         IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) | ||||
|                         , null) | ||||
|                 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) | ||||
|                     , null) | ||||
|                 return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg) | ||||
|             } | ||||
|         } else { | ||||
| @@ -1247,35 +1554,36 @@ 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) | ||||
|                             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()) | ||||
|                             , null) | ||||
|                     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()) | ||||
|                         , null) | ||||
|                     ExpressionCodeResult(result, vmDt, leftTr.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, if (signed) | ||||
|                             IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) | ||||
|                         else | ||||
|                             IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) | ||||
|                             , null) | ||||
|                     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) | ||||
|                         , null) | ||||
|                     ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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() | ||||
|                     choices.add(choiceLabel to choice) | ||||
|                     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) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) | ||||
|  | ||||
|         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,7 +39,10 @@ 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) { | ||||
|                 return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent)) | ||||
|                 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)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -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,15 +337,19 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors: | ||||
|  | ||||
|             val constIndex = arrayIndexedExpression.indexer.constIndex() | ||||
|             if (constIndex != null) { | ||||
|                 val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl() | ||||
|                 if(arrayVar!=null) { | ||||
|                     val array =arrayVar.value as? ArrayLiteral | ||||
|                     if(array!=null) { | ||||
|                         val value = array.value[constIndex].constValue(program) | ||||
|                         if(value!=null) { | ||||
|                             return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent)) | ||||
|                 if(arrayIndexedExpression.plainarrayvar!=null) { | ||||
|                     val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl() | ||||
|                     if(arrayVar!=null) { | ||||
|                         val array =arrayVar.value as? ArrayLiteral | ||||
|                         if(array!=null) { | ||||
|                             val value = array.value[constIndex].constValue(program) | ||||
|                             if(value!=null) { | ||||
|                                 return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent)) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else if(arrayIndexedExpression.pointerderef!=null) { | ||||
|                     TODO("constant fold pointer[i]  ${arrayIndexedExpression.position}") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -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,19 +26,15 @@ 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) { | ||||
|                 if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) { | ||||
|                     // avoid silent float roundings | ||||
|                     errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position) | ||||
|                 } | ||||
|         val declConstValue = decl.value?.constValue(program) | ||||
|         if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST) | ||||
|             && declConstValue.type != decl.datatype.base) { | ||||
|             if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) { | ||||
|                 // avoid silent float roundings | ||||
|                 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 | ||||
| @@ -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,41 +326,36 @@ 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) { | ||||
|                 // special case when the identifier is used as a pointer var | ||||
|                 // var = constpointer[x] --> var = @(constvalue+x) [directmemoryread] | ||||
|                 // constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var | ||||
|                 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) | ||||
|                     listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent)) | ||||
|                 } else { | ||||
|                     val memread = DirectMemoryRead(add, identifier.position) | ||||
|                     listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent)) | ||||
|                 } | ||||
|         val cval = identifier.constValue(program) ?: return noModifications | ||||
|         val arrayIdx = identifier.parent as? ArrayIndexedExpression | ||||
|         if(arrayIdx!=null && cval.type.isNumeric) { | ||||
|             // special case when the identifier is used as a pointer var | ||||
|             // var = constpointer[x] --> var = @(constvalue+x) [directmemoryread] | ||||
|             // constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var | ||||
|             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, position = identifier.position) | ||||
|                 listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent)) | ||||
|             } else { | ||||
|                 val memread = DirectMemoryRead(add, identifier.position) | ||||
|                 listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent)) | ||||
|             } | ||||
|             when { | ||||
|                 cval.type.isNumericOrBool -> { | ||||
|                     if(parent is AddressOf) | ||||
|                         return noModifications      // cannot replace the identifier INSIDE the addr-of here, let's do it later. | ||||
|                     return listOf( | ||||
|                         IAstModification.ReplaceNode( | ||||
|                             identifier, | ||||
|                             NumericLiteral(cval.type, cval.number, identifier.position), | ||||
|                             identifier.parent | ||||
|                         ) | ||||
|         } | ||||
|         when { | ||||
|             cval.type.isNumericOrBool -> { | ||||
|                 if(parent is AddressOf) | ||||
|                     return noModifications      // cannot replace the identifier INSIDE the addr-of here, let's do it later. | ||||
|                 return listOf( | ||||
|                     IAstModification.ReplaceNode( | ||||
|                         identifier, | ||||
|                         NumericLiteral(cval.type, cval.number, identifier.position), | ||||
|                         identifier.parent | ||||
|                     ) | ||||
|                 } | ||||
|                 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 | ||||
|             cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant") | ||||
|             else -> return noModifications | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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,15 +245,21 @@ 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) | ||||
|                     return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) | ||||
|                 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) | ||||
|                     return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) | ||||
|                 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)) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -262,15 +271,21 @@ 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) | ||||
|                     return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) | ||||
|                 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) | ||||
|                     return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) | ||||
|                 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)) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -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,14 +181,20 @@ 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) { | ||||
|                                             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 | ||||
|                                             val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position) | ||||
|                                             return listOf( | ||||
|                                                 IAstModification.ReplaceNode(decl, voidCall, parent), | ||||
|                                                 IAstModification.Remove(assignment, assignment.parent as IStatementContainer) | ||||
|                                             ) | ||||
|                                             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 | ||||
|                                                 val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position) | ||||
|                                                 return listOf( | ||||
|                                                     IAstModification.ReplaceNode(decl, voidCall, parent), | ||||
|                                                     IAstModification.Remove(assignment, assignment.parent as IStatementContainer) | ||||
|                                                 ) | ||||
|                                             } | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } else { | ||||
|   | ||||
| @@ -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