mirror of
				https://github.com/irmen/prog8.git
				synced 2025-10-31 15:16:13 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			v12.0-beta
			...
			v8.14
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e55a675d2e | 
							
								
								
									
										17
									
								
								.aiignore
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								.aiignore
									
									
									
									
									
								
							| @@ -1,17 +0,0 @@ | |||||||
| # 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/ |  | ||||||
							
								
								
									
										14
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,14 +0,0 @@ | |||||||
| # These are supported funding model platforms |  | ||||||
|  |  | ||||||
| #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] |  | ||||||
| #patreon: # Replace with a single Patreon username |  | ||||||
| #open_collective: # Replace with a single Open Collective username |  | ||||||
| ko_fi: irmen |  | ||||||
| #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel |  | ||||||
| #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry |  | ||||||
| #liberapay: # Replace with a single Liberapay username |  | ||||||
| #issuehunt: # Replace with a single IssueHunt username |  | ||||||
| #otechie: # Replace with a single Otechie username |  | ||||||
| #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry |  | ||||||
| #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] |  | ||||||
| custom: ['https://paypal.me/irmendejong'] |  | ||||||
							
								
								
									
										30
									
								
								.github/workflows/all-ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/all-ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -10,32 +10,22 @@ jobs: | |||||||
|  |  | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout code |       - name: Checkout code | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v2 | ||||||
|  |  | ||||||
|       - name: build and install recent 64tass |       - name: Install 64tass | ||||||
|         run: | |         run: sudo apt-get update -y && sudo apt-get install -y 64tass | ||||||
|           sudo apt-get install -y make build-essential |  | ||||||
|           git clone --depth=1 https://github.com/irmen/64tass |  | ||||||
|           cd 64tass |  | ||||||
|           make -j4 |  | ||||||
|           sudo make install           |  | ||||||
|  |  | ||||||
|       - name: Set up JDK 17 |       - name: Set up JDK 11 | ||||||
|         uses: actions/setup-java@v4 |         uses: actions/setup-java@v2 | ||||||
|         with: |         with: | ||||||
|           java-version: 17 |           java-version: 11 | ||||||
|           distribution: temurin |           distribution: adopt | ||||||
|  |  | ||||||
|       - name: Build and test with Gradle |       - name: Build and test with Gradle | ||||||
|         run: | |         run: ./gradlew build shadowJar --no-daemon | ||||||
|           ./gradlew build shadowJar --no-daemon |  | ||||||
|           sha256sum -b compiler/build/libs/*-all.jar > compiler/build/libs/hash.txt |  | ||||||
|  |  | ||||||
|       - name: Create compiler shadowJar artifact |       - name: Create compiler shadowJar artifact | ||||||
|         uses: actions/upload-artifact@v4 |         uses: actions/upload-artifact@v3 | ||||||
|         with: |         with: | ||||||
|           name: prog8-compiler-jar-zipped |           name: prog8-compiler-jar-zipped | ||||||
|           path: | |           path: compiler/build/libs/*-all.jar | ||||||
|             compiler/build/libs/*-all.jar |  | ||||||
|             compiler/build/libs/hash.txt |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,34 +1,26 @@ | |||||||
| .idea/workspace.xml | .idea/workspace.xml | ||||||
| .idea/discord.xml | .idea/discord.xml | ||||||
| .idea/developer-tools.xml |  | ||||||
| .idea/usage.statistics.xml |  | ||||||
| .idea/shelf/ |  | ||||||
| build/ | build/ | ||||||
| dist/ | dist/ | ||||||
| output/ | output/ | ||||||
| out/ |  | ||||||
| out-new/ |  | ||||||
| out-old/ |  | ||||||
| .*cache/ | .*cache/ | ||||||
| *.directory | *.directory | ||||||
| *.prg | *.prg | ||||||
| *.bin | *.bin | ||||||
| *.p8ir |  | ||||||
| *.labels.txt | *.labels.txt | ||||||
| *.vm.txt | *.vm.txt | ||||||
| *.vice-mon-list | *.vice-mon-list | ||||||
| docs/build | docs/build | ||||||
|  | out/ | ||||||
| parser/**/*.interp | parser/**/*.interp | ||||||
| parser/**/*.tokens | parser/**/*.tokens | ||||||
| parser/**/*.java | parser/**/*.java | ||||||
| compiler/src/prog8/buildversion/* |  | ||||||
| *.py[cod] | *.py[cod] | ||||||
| *.egg | *.egg | ||||||
| *.egg-info | *.egg-info | ||||||
| .eggs/ | .eggs/ | ||||||
| /MANIFEST | /MANIFEST | ||||||
| .tox/ | .tox/ | ||||||
| .kotlin/ |  | ||||||
| __pycache__/ | __pycache__/ | ||||||
| parser.out | parser.out | ||||||
| parsetab.py | parsetab.py | ||||||
| @@ -37,6 +29,6 @@ parsetab.py | |||||||
| compiler/lib/ | compiler/lib/ | ||||||
|  |  | ||||||
| .gradle | .gradle | ||||||
|  | /prog8compiler.jar | ||||||
| sd*.img | sd*.img | ||||||
| *.d64 | *.d64 | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1581
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1581
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										12
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,19 +1,9 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project version="4"> | <project version="4"> | ||||||
|   <component name="Kotlin2JsCompilerArguments"> |  | ||||||
|     <option name="moduleKind" value="plain" /> |  | ||||||
|   </component> |  | ||||||
|   <component name="Kotlin2JvmCompilerArguments"> |   <component name="Kotlin2JvmCompilerArguments"> | ||||||
|     <option name="jvmTarget" value="11" /> |     <option name="jvmTarget" value="11" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="KotlinCommonCompilerArguments"> |  | ||||||
|     <option name="apiVersion" value="2.2" /> |  | ||||||
|     <option name="languageVersion" value="2.2" /> |  | ||||||
|   </component> |  | ||||||
|   <component name="KotlinCompilerSettings"> |  | ||||||
|     <option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" /> |  | ||||||
|   </component> |  | ||||||
|   <component name="KotlinJpsPluginSettings"> |   <component name="KotlinJpsPluginSettings"> | ||||||
|     <option name="version" value="2.2.0" /> |     <option name="version" value="1.8.21-release-380" /> | ||||||
|   </component> |   </component> | ||||||
| </project> | </project> | ||||||
							
								
								
									
										28
									
								
								.idea/libraries/KotlinJavaRuntime.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										28
									
								
								.idea/libraries/KotlinJavaRuntime.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,23 +1,19 @@ | |||||||
| <component name="libraryTable"> | <component name="libraryTable"> | ||||||
|   <library name="KotlinJavaRuntime" type="repository"> |   <library name="KotlinJavaRuntime"> | ||||||
|     <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" /> |  | ||||||
|     <CLASSES> |     <CLASSES> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" /> |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" /> |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" /> |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" /> | ||||||
|  |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" /> | ||||||
|     </CLASSES> |     </CLASSES> | ||||||
|     <JAVADOC> |     <JAVADOC /> | ||||||
|       <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.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" /> |  | ||||||
|     </JAVADOC> |  | ||||||
|     <SOURCES> |     <SOURCES> | ||||||
|       <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://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-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://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" /> |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" /> | ||||||
|  |       <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" /> | ||||||
|     </SOURCES> |     </SOURCES> | ||||||
|   </library> |   </library> | ||||||
| </component> | </component> | ||||||
							
								
								
									
										6
									
								
								.idea/libraries/antlr_antlr4.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/libraries/antlr_antlr4.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,13 +1,13 @@ | |||||||
| <component name="libraryTable"> | <component name="libraryTable"> | ||||||
|   <library name="antlr.antlr4" type="repository"> |   <library name="antlr.antlr4" type="repository"> | ||||||
|     <properties maven-id="org.antlr:antlr4:4.13.2"> |     <properties maven-id="org.antlr:antlr4:4.12.0"> | ||||||
|       <exclude> |       <exclude> | ||||||
|         <dependency maven-id="com.ibm.icu:icu4j" /> |         <dependency maven-id="com.ibm.icu:icu4j" /> | ||||||
|       </exclude> |       </exclude> | ||||||
|     </properties> |     </properties> | ||||||
|     <CLASSES> |     <CLASSES> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.2/antlr4-4.13.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.12.0/antlr4-4.12.0.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.2/antlr4-runtime-4.13.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.12.0/antlr4-runtime-4.12.0.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" /> | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								.idea/libraries/eclipse_lsp4j.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								.idea/libraries/eclipse_lsp4j.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,13 +0,0 @@ | |||||||
| <component name="libraryTable"> |  | ||||||
|   <library name="eclipse.lsp4j" type="repository"> |  | ||||||
|     <properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0" /> |  | ||||||
|     <CLASSES> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.13.1/gson-2.13.1.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.38.0/error_prone_annotations-2.38.0.jar!/" /> |  | ||||||
|     </CLASSES> |  | ||||||
|     <JAVADOC /> |  | ||||||
|     <SOURCES /> |  | ||||||
|   </library> |  | ||||||
| </component> |  | ||||||
							
								
								
									
										25
									
								
								.idea/libraries/github_hypfvieh_dbus_java.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								.idea/libraries/github_hypfvieh_dbus_java.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | <component name="libraryTable"> | ||||||
|  |   <library name="github.hypfvieh.dbus.java" type="repository"> | ||||||
|  |     <properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" /> | ||||||
|  |     <CLASSES> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" /> | ||||||
|  |     </CLASSES> | ||||||
|  |     <JAVADOC /> | ||||||
|  |     <SOURCES /> | ||||||
|  |   </library> | ||||||
|  | </component> | ||||||
							
								
								
									
										10
									
								
								.idea/libraries/glassfish_javax_json.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.idea/libraries/glassfish_javax_json.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | <component name="libraryTable"> | ||||||
|  |   <library name="glassfish.javax.json" type="repository"> | ||||||
|  |     <properties include-transitive-deps="false" maven-id="org.glassfish:javax.json:1.1.4" /> | ||||||
|  |     <CLASSES> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" /> | ||||||
|  |     </CLASSES> | ||||||
|  |     <JAVADOC /> | ||||||
|  |     <SOURCES /> | ||||||
|  |   </library> | ||||||
|  | </component> | ||||||
							
								
								
									
										23
									
								
								.idea/libraries/io_kotest_assertions_core_jvm.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.idea/libraries/io_kotest_assertions_core_jvm.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | <component name="libraryTable"> | ||||||
|  |   <library name="io.kotest.assertions.core.jvm" type="repository"> | ||||||
|  |     <properties maven-id="io.kotest:kotest-assertions-core-jvm:5.5.5" /> | ||||||
|  |     <CLASSES> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.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.2.0/opentest4j-1.2.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" /> | ||||||
|  |     </CLASSES> | ||||||
|  |     <JAVADOC /> | ||||||
|  |     <SOURCES /> | ||||||
|  |   </library> | ||||||
|  | </component> | ||||||
							
								
								
									
										23
									
								
								.idea/libraries/io_kotest_framework_datatest.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										23
									
								
								.idea/libraries/io_kotest_framework_datatest.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,23 +0,0 @@ | |||||||
| <component name="libraryTable"> |  | ||||||
|   <library name="io.kotest.framework.datatest" type="repository"> |  | ||||||
|     <properties maven-id="io.kotest:kotest-framework-datatest:5.9.1" /> |  | ||||||
|     <CLASSES> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest/5.9.1/kotest-framework-datatest-5.9.1.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest-jvm/5.9.1/kotest-framework-datatest-jvm-5.9.1.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" /> |  | ||||||
|     </CLASSES> |  | ||||||
|     <JAVADOC /> |  | ||||||
|     <SOURCES /> |  | ||||||
|   </library> |  | ||||||
| </component> |  | ||||||
							
								
								
									
										24
									
								
								.idea/libraries/io_kotest_property_jvm.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.idea/libraries/io_kotest_property_jvm.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | <component name="libraryTable"> | ||||||
|  |   <library name="io.kotest.property.jvm" type="repository"> | ||||||
|  |     <properties maven-id="io.kotest:kotest-property-jvm:5.5.5" /> | ||||||
|  |     <CLASSES> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.5.5/kotest-property-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> | ||||||
|  |     </CLASSES> | ||||||
|  |     <JAVADOC /> | ||||||
|  |     <SOURCES /> | ||||||
|  |   </library> | ||||||
|  | </component> | ||||||
							
								
								
									
										66
									
								
								.idea/libraries/io_kotest_runner_junit5_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										66
									
								
								.idea/libraries/io_kotest_runner_junit5_jvm.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,39 +1,55 @@ | |||||||
| <component name="libraryTable"> | <component name="libraryTable"> | ||||||
|   <library name="io.kotest.runner.junit5.jvm" type="repository"> |   <library name="io.kotest.runner.junit5.jvm" type="repository"> | ||||||
|     <properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.9.1" /> |     <properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.5.5" /> | ||||||
|     <CLASSES> |     <CLASSES> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.9.1/kotest-runner-junit5-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.5.5/kotest-runner-junit5-jvm-5.5.5.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.5.5/kotest-framework-api-jvm-5.5.5.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$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.4/kotlinx-coroutines-test-jvm-1.6.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.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$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.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-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.9.1/kotest-framework-engine-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.5.5/kotest-framework-engine-jvm-5.5.5.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.172/classgraph-4.8.172.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.154/classgraph-4.8.154.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.8.0/kotlinx-coroutines-debug-1.8.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.4/kotlinx-coroutines-debug-1.6.4.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.9.1/kotest-framework-discovery-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.5.5/kotest-framework-discovery-jvm-5.5.5.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.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/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.9.1/kotest-extensions-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.5.5/kotest-extensions-jvm-5.5.5.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.9.1/kotest-framework-concurrency-jvm-5.9.1.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-jvm/1.13.1/mockk-jvm-1.13.1.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.13.1/mockk-dsl-jvm-1.13.1.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.13.1/mockk-agent-jvm-1.13.1.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.2/objenesis-3.2.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api-jvm/1.13.1/mockk-agent-api-jvm-1.13.1.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-core-jvm/1.13.1/mockk-core-jvm-1.13.1.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.13.2/junit-4.13.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter/5.8.2/junit-jupiter-5.8.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.8.2/junit-jupiter-params-5.8.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.8.2/junit-jupiter-engine-5.8.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.6.4/kotlinx-coroutines-core-1.6.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.5.5/kotest-framework-concurrency-jvm-5.5.5.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.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$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" /> | ||||||
|     </CLASSES> |     </CLASSES> | ||||||
|     <JAVADOC /> |     <JAVADOC /> | ||||||
|     <SOURCES /> |     <SOURCES /> | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,8 +1,8 @@ | |||||||
| <component name="libraryTable"> | <component name="libraryTable"> | ||||||
|   <library name="jetbrains.kotlinx.cli.jvm" type="repository"> |   <library name="jetbrains.kotlinx.cli.jvm" type="repository"> | ||||||
|     <properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6" /> |     <properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.5" /> | ||||||
|     <CLASSES> |     <CLASSES> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.5/kotlinx-cli-jvm-0.3.5.jar!/" /> | ||||||
|     </CLASSES> |     </CLASSES> | ||||||
|     <JAVADOC /> |     <JAVADOC /> | ||||||
|     <SOURCES /> |     <SOURCES /> | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								.idea/libraries/michael_bull_kotlin_result_jvm.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								.idea/libraries/michael_bull_kotlin_result_jvm.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,20 +1,15 @@ | |||||||
| <component name="libraryTable"> | <component name="libraryTable"> | ||||||
|   <library name="michael.bull.kotlin.result.jvm" type="repository"> |   <library name="michael.bull.kotlin.result.jvm" type="repository"> | ||||||
|     <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" /> |     <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" /> | ||||||
|     <CLASSES> |     <CLASSES> | ||||||
|       <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$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.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/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" /> | ||||||
|     </CLASSES> |     </CLASSES> | ||||||
|     <JAVADOC> |     <JAVADOC /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-javadoc.jar!/" /> |     <SOURCES /> | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" /> |  | ||||||
|     </JAVADOC> |  | ||||||
|     <SOURCES> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-sources.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" /> |  | ||||||
|       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> |  | ||||||
|     </SOURCES> |  | ||||||
|   </library> |   </library> | ||||||
| </component> | </component> | ||||||
							
								
								
									
										11
									
								
								.idea/libraries/slf4j_simple.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.idea/libraries/slf4j_simple.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | <component name="libraryTable"> | ||||||
|  |   <library name="slf4j.simple" type="repository"> | ||||||
|  |     <properties maven-id="org.slf4j:slf4j-simple:2.0.7" /> | ||||||
|  |     <CLASSES> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.7/slf4j-simple-2.0.7.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.jar!/" /> | ||||||
|  |     </CLASSES> | ||||||
|  |     <JAVADOC /> | ||||||
|  |     <SOURCES /> | ||||||
|  |   </library> | ||||||
|  | </component> | ||||||
							
								
								
									
										18
									
								
								.idea/libraries/takes.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.idea/libraries/takes.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | <component name="libraryTable"> | ||||||
|  |   <library name="takes" type="repository"> | ||||||
|  |     <properties maven-id="org.takes:takes:1.24.4" /> | ||||||
|  |     <CLASSES> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" /> | ||||||
|  |       <root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" /> | ||||||
|  |     </CLASSES> | ||||||
|  |     <JAVADOC /> | ||||||
|  |     <SOURCES /> | ||||||
|  |   </library> | ||||||
|  | </component> | ||||||
							
								
								
									
										13
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							| @@ -4,29 +4,22 @@ | |||||||
|     <option name="perGrammarGenerationSettings"> |     <option name="perGrammarGenerationSettings"> | ||||||
|       <list> |       <list> | ||||||
|         <PerGrammarGenerationSettings> |         <PerGrammarGenerationSettings> | ||||||
|           <option name="fileName" value="$PROJECT_DIR$/parser/src/main/antlr/Prog8ANTLR.g4" /> |           <option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" /> | ||||||
|           <option name="autoGen" value="true" /> |           <option name="autoGen" value="true" /> | ||||||
|           <option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" /> |           <option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" /> | ||||||
|           <option name="libDir" value="" /> |           <option name="libDir" value="" /> | ||||||
|           <option name="encoding" value="" /> |           <option name="encoding" value="" /> | ||||||
|           <option name="pkg" value="" /> |           <option name="pkg" value="" /> | ||||||
|           <option name="language" value="Java" /> |           <option name="language" value="" /> | ||||||
|           <option name="generateListener" value="false" /> |           <option name="generateListener" value="false" /> | ||||||
|           <option name="generateVisitor" value="true" /> |  | ||||||
|         </PerGrammarGenerationSettings> |         </PerGrammarGenerationSettings> | ||||||
|       </list> |       </list> | ||||||
|     </option> |     </option> | ||||||
|   </component> |   </component> | ||||||
|   <component name="Black"> |  | ||||||
|     <option name="sdkName" value="Python 3.11" /> |  | ||||||
|   </component> |  | ||||||
|   <component name="FrameworkDetectionExcludesConfiguration"> |   <component name="FrameworkDetectionExcludesConfiguration"> | ||||||
|     <type id="Python" /> |     <type id="Python" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK"> |   <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK"> | ||||||
|     <output url="file://$PROJECT_DIR$/out" /> |     <output url="file://$PROJECT_DIR$/out" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="PythonCompatibilityInspectionAdvertiser"> |  | ||||||
|     <option name="version" value="3" /> |  | ||||||
|   </component> |  | ||||||
| </project> | </project> | ||||||
							
								
								
									
										7
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							| @@ -2,8 +2,6 @@ | |||||||
| <project version="4"> | <project version="4"> | ||||||
|   <component name="ProjectModuleManager"> |   <component name="ProjectModuleManager"> | ||||||
|     <modules> |     <modules> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/beanshell/beanshell.iml" filepath="$PROJECT_DIR$/beanshell/beanshell.iml" /> |  | ||||||
|       <module fileurl="file://$PROJECT_DIR$/benchmark-program/benchmark-program.iml" filepath="$PROJECT_DIR$/benchmark-program/benchmark-program.iml" /> |  | ||||||
|       <module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" /> |       <module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" /> |       <module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" /> |       <module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" /> | ||||||
| @@ -12,13 +10,12 @@ | |||||||
|       <module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" /> |       <module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" /> |       <module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" /> |       <module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" /> | ||||||
|  |       <module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" /> |       <module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" /> |       <module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" /> | ||||||
|  |       <module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" /> |       <module fileurl="file://$PROJECT_DIR$/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$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" /> | ||||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" /> |  | ||||||
|       <module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" /> |  | ||||||
|       <module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" /> |       <module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" /> | ||||||
|     </modules> |     </modules> | ||||||
|   </component> |   </component> | ||||||
|   | |||||||
| @@ -9,19 +9,17 @@ version: 2 | |||||||
| build: | build: | ||||||
|   os: ubuntu-22.04 |   os: ubuntu-22.04 | ||||||
|   tools: |   tools: | ||||||
|     python: "3.12" |     python: "3.11" | ||||||
|  |  | ||||||
|  | # Build documentation in the docs/ directory with Sphinx | ||||||
|  | sphinx: | ||||||
|  |   configuration: docs/source/conf.py | ||||||
|  |  | ||||||
|  | # If using Sphinx, optionally build your docs in additional formats such as PDF | ||||||
|  | formats: | ||||||
|  |   - pdf | ||||||
|  |  | ||||||
| # Optionally declare the Python requirements required to build your docs | # Optionally declare the Python requirements required to build your docs | ||||||
| python: | python: | ||||||
|   install: |   install: | ||||||
|     - requirements: docs/requirements.txt |     - requirements: docs/requirements.txt | ||||||
|  |  | ||||||
| # Build documentation in the docs/ directory with Sphinx |  | ||||||
| sphinx: |  | ||||||
|   configuration: docs/source/conf.py |  | ||||||
|   fail_on_warning: true |  | ||||||
|  |  | ||||||
| # If using Sphinx, optionally build your docs in additional formats such as PDF |  | ||||||
| formats: |  | ||||||
|   - pdf |  | ||||||
|   - epub |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,12 +0,0 @@ | |||||||
| # super simple Makefile to lauch the main gradle targets to build and/or test the prog8 compiler |  | ||||||
|  |  | ||||||
| .PHONY: all test |  | ||||||
|  |  | ||||||
| all: |  | ||||||
| 	gradle installdist installshadowdist |  | ||||||
| 	@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c" |  | ||||||
|  |  | ||||||
| test: |  | ||||||
| 	gradle build |  | ||||||
| 	@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c" |  | ||||||
|  |  | ||||||
							
								
								
									
										82
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,7 +1,3 @@ | |||||||
| [](https://ko-fi.com/H2H6S0FFF) |  | ||||||
|  |  | ||||||
| PayPal: [](https://paypal.me/irmendejong) |  | ||||||
|  |  | ||||||
| [](https://prog8.readthedocs.io/) | [](https://prog8.readthedocs.io/) | ||||||
|  |  | ||||||
| Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors | Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors | ||||||
| @@ -13,37 +9,11 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro | |||||||
| as used in many home computers from that era. It is a medium to low level programming language, | as used in many home computers from that era. It is a medium to low level programming language, | ||||||
| which aims to provide many conveniences over raw assembly code (even when using a macro assembler). | which aims to provide many conveniences over raw assembly code (even when using a macro assembler). | ||||||
|  |  | ||||||
| **Want to buy me a coffee or a pizza perhaps?** |  | ||||||
|  |  | ||||||
| This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can. |  | ||||||
| If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,  |  | ||||||
| you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Documentation | Documentation | ||||||
| ------------- | ------------- | ||||||
| Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at: | Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at: | ||||||
| https://prog8.readthedocs.io/ | https://prog8.readthedocs.io/ | ||||||
|  |  | ||||||
| How to get it/build it |  | ||||||
| ---------------------- |  | ||||||
|  |  | ||||||
| - Download the latest [official release](https://github.com/irmen/prog8/releases) from github. |  | ||||||
| - Or, if you want/need a bleeding edge development version, you can: |  | ||||||
|   - download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions). |  | ||||||
|   - you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html). |  | ||||||
|     Note that if you are not using *gradle* to build it, you might have to perform some manual |  | ||||||
|     tasks once to make it compile fully. These are explained in the linked instructions.  |  | ||||||
| - Alternatively, you can also install the compiler as a package on some linux distros: |  | ||||||
|   - Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8) |  | ||||||
|  |  | ||||||
| Community |  | ||||||
| --------- |  | ||||||
| Most of the development on Prog8 and the use of it is currently centered around  |  | ||||||
| the [Commander X16](https://www.commanderx16.com/) retro computer. Their [discord server](https://discord.gg/nS2PqEC) contains a small channel |  | ||||||
| dedicated to Prog8. Other than that, use the issue tracker on github. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Software license | Software license | ||||||
| ---------------- | ---------------- | ||||||
| GNU GPL 3.0 (see file LICENSE), with exception for generated code: | GNU GPL 3.0 (see file LICENSE), with exception for generated code: | ||||||
| @@ -56,33 +26,26 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code: | |||||||
| What does Prog8 provide? | What does Prog8 provide? | ||||||
| ------------------------ | ------------------------ | ||||||
|  |  | ||||||
| - all advantages of a higher level language over having to write assembly code manually | - reduction of source code length over raw assembly | ||||||
| - programs run very fast because it's compiled to native machine code | - fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8. | ||||||
| - code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS | - modularity, symbol scoping, subroutines | ||||||
| - modularity, symbol scoping, subroutines. No need for forward declarations. |  | ||||||
| - various data types other than just bytes (16-bit words, floats, strings) | - various data types other than just bytes (16-bit words, floats, strings) | ||||||
| - Structs and typed pointers | - floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do) | ||||||
| - floating point math is supported on certain targets |  | ||||||
| - access to most Kernal ROM routines as external subroutine definitions you can call normally |  | ||||||
| - tight control over Zeropage usage |  | ||||||
| - programs can be run multiple times without reloading because of automatic variable (re)initializations. |  | ||||||
| - programs can be configured to execute in ROM |  | ||||||
| - strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents. | - strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents. | ||||||
| - automatic static variable allocations, automatic string and array variables and string sharing | - automatic static variable allocations, automatic string and array variables and string sharing | ||||||
|  | - subroutines with input parameters and result values | ||||||
| - high-level program optimizations | - high-level program optimizations | ||||||
| - conditional branches that map 1:1 to cpu status flags | - small program boilerplate/compilersupport overhead | ||||||
|  | - programs can be run multiple times without reloading because of automatic variable (re)initializations. | ||||||
|  | - conditional branches | ||||||
| - ``when`` statement to provide a concise jump table alternative to if/elseif chains | - ``when`` statement to provide a concise jump table alternative to if/elseif chains | ||||||
| - ``on .. goto`` statement for fast jump tables |  | ||||||
| - ``in`` expression for concise and efficient multi-value/containment check  | - ``in`` expression for concise and efficient multi-value/containment check  | ||||||
| - ``defer`` statement to help write concise and robust subroutine cleanup logic | - many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse`` | ||||||
| - several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror`` |  | ||||||
| - various powerful built-in libraries to do I/O, number conversions, graphics and more   | - various powerful built-in libraries to do I/O, number conversions, graphics and more   | ||||||
| - subroutines can return more than one result value | - convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses | ||||||
| - inline assembly allows you to have full control when every cycle or byte matters | - inline assembly allows you to have full control when every cycle or byte matters | ||||||
| - supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets) | - supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64. | ||||||
| - encode strings and characters into petscii or screencodes or even other encodings | - encode strings and characters into petscii or screencodes as desired (C64/Cx16) | ||||||
| - Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks |  | ||||||
| - 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether  |  | ||||||
|  |  | ||||||
| *Rapid edit-compile-run-debug cycle:* | *Rapid edit-compile-run-debug cycle:* | ||||||
|  |  | ||||||
| @@ -93,11 +56,9 @@ What does Prog8 provide? | |||||||
|  |  | ||||||
| *Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!): | *Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!): | ||||||
|  |  | ||||||
| - "cx16": [CommanderX16](https://www.commanderx16.com)  (65c02 CPU) |  | ||||||
| - "c64": Commodore-64  (6502 like CPU) | - "c64": Commodore-64  (6502 like CPU) | ||||||
| - "c128": Commodore-128  (6502 like CPU - the Z80 cpu mode is not supported) | - "c128": Commodore-128  (6502 like CPU - the Z80 cpu mode is not supported) | ||||||
| - "pet32": Commodore PET (limited support) | - "cx16": [CommanderX16](https://www.commanderx16.com)  (65c02 CPU) | ||||||
| - 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) | - 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) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -132,11 +93,12 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm:: | |||||||
|     %zeropage basicsafe |     %zeropage basicsafe | ||||||
|      |      | ||||||
|     main { |     main { | ||||||
|         bool[256] sieve |      | ||||||
|  |         ubyte[256] sieve | ||||||
|         ubyte candidate_prime = 2       ; is increased in the loop |         ubyte candidate_prime = 2       ; is increased in the loop | ||||||
|      |      | ||||||
|         sub start() { |         sub start() { | ||||||
|             sys.memset(sieve, 256, 0)   ; clear the sieve |             sys.memset(sieve, 256, false)   ; clear the sieve | ||||||
|             txt.print("prime numbers up to 255:\n\n") |             txt.print("prime numbers up to 255:\n\n") | ||||||
|             ubyte amount=0 |             ubyte amount=0 | ||||||
|             repeat { |             repeat { | ||||||
| @@ -172,6 +134,9 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm:: | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| when compiled an ran on a C-64 you'll get: | when compiled an ran on a C-64 you'll get: | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -193,12 +158,3 @@ For instance here's a well known space ship animated in 3D with hidden line remo | |||||||
| in the CommanderX16 emulator: | 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) |  | ||||||
|   | |||||||
| @@ -1,65 +0,0 @@ | |||||||
| import org.jetbrains.kotlin.gradle.dsl.JvmTarget |  | ||||||
|  |  | ||||||
| plugins { |  | ||||||
|     id("application") |  | ||||||
|     kotlin("jvm") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| val serverMainClassName = "prog8lsp.MainKt" |  | ||||||
| val applicationName = "prog8-beanshell" |  | ||||||
|  |  | ||||||
| application { |  | ||||||
|     mainClass.set(serverMainClassName) |  | ||||||
|     description = "Code completions, diagnostics and more for Prog8" |  | ||||||
|     // applicationDefaultJvmArgs = listOf("-DkotlinLanguageServer.version=$version") |  | ||||||
|     applicationDistribution.into("bin") { |  | ||||||
|         filePermissions { |  | ||||||
|             user { |  | ||||||
|                 read=true |  | ||||||
|                 execute=true |  | ||||||
|                 write=true |  | ||||||
|             } |  | ||||||
|             other.execute = true |  | ||||||
|             group.execute = true |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| repositories { |  | ||||||
|     mavenCentral() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| dependencies { |  | ||||||
|     implementation(files("lib/bsh-3.0.0-SNAPSHOT.jar")) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| configurations.forEach { config -> |  | ||||||
|     config.resolutionStrategy { |  | ||||||
|         preferProjectModules() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sourceSets.main { |  | ||||||
|     java.srcDir("src") |  | ||||||
|     resources.srcDir("resources") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| tasks.startScripts { |  | ||||||
|     applicationName = "prog8-beanshell" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| tasks.register<Exec>("fixFilePermissions") { |  | ||||||
|     // When running on macOS or Linux the start script |  | ||||||
|     // needs executable permissions to run. |  | ||||||
|  |  | ||||||
|     onlyIf { !System.getProperty("os.name").lowercase().contains("windows") } |  | ||||||
|     commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| tasks.installDist { |  | ||||||
|     finalizedBy("fixFilePermissions") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| tasks.build { |  | ||||||
|     finalizedBy("installDist") |  | ||||||
| } |  | ||||||
										
											Binary file not shown.
										
									
								
							| @@ -1,48 +0,0 @@ | |||||||
| package prog8beanshell |  | ||||||
|  |  | ||||||
| import java.io.FilterReader |  | ||||||
| import java.io.Reader |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class CommandLineReader(val input: Reader): FilterReader(input)  { |  | ||||||
|     private val normal = 0 |  | ||||||
|     private val lastCharNL = 1 |  | ||||||
|     private val sentSemi = 2 |  | ||||||
|     private var state = lastCharNL |  | ||||||
|  |  | ||||||
|     override fun read(): Int { |  | ||||||
|         if (state == sentSemi) { |  | ||||||
|             this.state = lastCharNL |  | ||||||
|             return 10 |  | ||||||
|         } else { |  | ||||||
|             var b = input.read() |  | ||||||
|             while(b==13) b = input.read() |  | ||||||
|  |  | ||||||
|             if (b == 10) { |  | ||||||
|                 if (this.state == lastCharNL) { |  | ||||||
|                     b = 59 |  | ||||||
|                     this.state = sentSemi |  | ||||||
|                 } else { |  | ||||||
|                     this.state = lastCharNL |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 this.state = normal |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return b |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun read(buff: CharArray, off: Int, len: Int): Int { |  | ||||||
|         val b = read() |  | ||||||
|         if (b == -1) { |  | ||||||
|             return -1 |  | ||||||
|         } else { |  | ||||||
|             buff[off] = b.toChar() |  | ||||||
|             return 1 |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| package prog8beanshell |  | ||||||
|  |  | ||||||
| import bsh.FileReader |  | ||||||
| import bsh.Interpreter |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class BeanshellInterpreter { |  | ||||||
|  |  | ||||||
|     fun run(symbols: Map<String, Any>) { |  | ||||||
|         val interpreter = Interpreter(CommandLineReader(FileReader(System.`in`)), System.out, System.err, true) |  | ||||||
|         interpreter.setExitOnEOF(false) |  | ||||||
|         symbols.forEach { (name, value) -> interpreter.set(name, value) } |  | ||||||
|         interpreter.run() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fun main(args: Array<String>) { |  | ||||||
|     val i = BeanshellInterpreter() |  | ||||||
|     i.run(mapOf( |  | ||||||
|         "env" to System.getenv(), |  | ||||||
|         "args" to args |  | ||||||
|     )) |  | ||||||
| } |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| # 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 . |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1,71 +0,0 @@ | |||||||
| %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 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,72 +0,0 @@ | |||||||
| %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 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,72 +0,0 @@ | |||||||
| %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 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,93 +0,0 @@ | |||||||
| %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 |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|  |  | ||||||
| } |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 78 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 80 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 79 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 74 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 85 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 82 KiB | 
| @@ -1,85 +0,0 @@ | |||||||
| %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 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,107 +0,0 @@ | |||||||
| %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 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| .PHONY:  clean run |  | ||||||
|  |  | ||||||
| run: |  | ||||||
| 	prog8c -target cx16 benchmark.p8 |  | ||||||
| 	x16emu -run -prg benchmark.prg -warp |  | ||||||
|  |  | ||||||
| clean: |  | ||||||
| 	rm -f *.prg *.PRG *.asm *.vice-* *.BIN *.PAL *.zip *.7z |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -1,109 +0,0 @@ | |||||||
| %import textio |  | ||||||
| %import math |  | ||||||
|  |  | ||||||
| rotate3d { |  | ||||||
|     const ubyte WIDTH = 40 |  | ||||||
|     const ubyte HEIGHT = 30 |  | ||||||
|  |  | ||||||
|     sub benchmark(uword max_time) -> uword { |  | ||||||
|  |  | ||||||
|         uword anglex |  | ||||||
|         uword angley |  | ||||||
|         uword anglez |  | ||||||
|         uword frames |  | ||||||
|  |  | ||||||
|         txt.nl() |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|  |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
|             matrix_math.rotate_vertices(msb(anglex), msb(angley), msb(anglez)) |  | ||||||
|             draw_edges()   ; doesn't really draw anything in the benchmark, but does do the screen calculations |  | ||||||
|             anglex+=500 |  | ||||||
|             angley+=215 |  | ||||||
|             anglez+=453 |  | ||||||
|             frames++ |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return frames |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub draw_edges() { |  | ||||||
|  |  | ||||||
|         ; plot the points of the 3d cube |  | ||||||
|         ; first the points on the back, then the points on the front (painter algorithm) |  | ||||||
|  |  | ||||||
|         ubyte @zp i |  | ||||||
|         word @zp rz |  | ||||||
|         word @zp persp |  | ||||||
|         byte @shared sx |  | ||||||
|         byte @shared sy |  | ||||||
|  |  | ||||||
|         for i in 0 to len(matrix_math.xcoor)-1 { |  | ||||||
|             rz = matrix_math.rotatedz[i] |  | ||||||
|             if rz >= 10 { |  | ||||||
|                 persp = 600 + rz/64 |  | ||||||
|                 sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2 |  | ||||||
|                 sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2 |  | ||||||
|                 ;; txt.setcc(sx as ubyte, sy as ubyte, 46, 7) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for i in 0 to len(matrix_math.xcoor)-1 { |  | ||||||
|             rz = matrix_math.rotatedz[i] |  | ||||||
|             if rz < 10 { |  | ||||||
|                 persp = 600 + rz/64 |  | ||||||
|                 sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2 |  | ||||||
|                 sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2 |  | ||||||
|                 ;; txt.setcc(sx as ubyte, sy as ubyte, 81, 7) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         txt.chrout('.') |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| matrix_math { |  | ||||||
|     ; vertices |  | ||||||
|     word[] xcoor = [ -40, -40, -40, -40,  40,  40,  40, 40 ] |  | ||||||
|     word[] ycoor = [ -40, -40,  40,  40, -40, -40,  40, 40 ] |  | ||||||
|     word[] zcoor = [ -40,  40, -40,  40, -40,  40, -40, 40 ] |  | ||||||
|  |  | ||||||
|     ; storage for rotated coordinates |  | ||||||
|     word[len(xcoor)] rotatedx |  | ||||||
|     word[len(ycoor)] rotatedy |  | ||||||
|     word[len(zcoor)] rotatedz |  | ||||||
|  |  | ||||||
|     sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) { |  | ||||||
|         ; rotate around origin (0,0,0) |  | ||||||
|  |  | ||||||
|         ; set up the 3d rotation matrix values |  | ||||||
|         word wcosa = math.cos8(ax) |  | ||||||
|         word wsina = math.sin8(ax) |  | ||||||
|         word wcosb = math.cos8(ay) |  | ||||||
|         word wsinb = math.sin8(ay) |  | ||||||
|         word wcosc = math.cos8(az) |  | ||||||
|         word wsinc = math.sin8(az) |  | ||||||
|  |  | ||||||
|         word wcosa_sinb = wcosa*wsinb / 128 |  | ||||||
|         word wsina_sinb = wsina*wsinb / 128 |  | ||||||
|  |  | ||||||
|         word Axx = wcosa*wcosb / 128 |  | ||||||
|         word Axy = (wcosa_sinb*wsinc - wsina*wcosc) / 128 |  | ||||||
|         word Axz = (wcosa_sinb*wcosc + wsina*wsinc) / 128 |  | ||||||
|         word Ayx = wsina*wcosb / 128 |  | ||||||
|         word Ayy = (wsina_sinb*wsinc + wcosa*wcosc) / 128 |  | ||||||
|         word Ayz = (wsina_sinb*wcosc - wcosa*wsinc) / 128 |  | ||||||
|         word Azx = -wsinb |  | ||||||
|         word Azy = wcosb*wsinc / 128 |  | ||||||
|         word Azz = wcosb*wcosc / 128 |  | ||||||
|  |  | ||||||
|         ubyte @zp i |  | ||||||
|         for i in 0 to len(xcoor)-1 { |  | ||||||
|             ; don't normalize by dividing by 128, instead keep some precision for perspective calc later |  | ||||||
|             rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i] |  | ||||||
|             rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i] |  | ||||||
|             rotatedz[i] = Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i] |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,89 +0,0 @@ | |||||||
| adpcm { |  | ||||||
|  |  | ||||||
|     sub decode_benchmark(uword max_time) -> uword { |  | ||||||
|         uword num_blocks |  | ||||||
|         txt.nl() |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|  |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
|             adpcm.init(0,0) |  | ||||||
|             uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input |  | ||||||
|             repeat 252/2 { |  | ||||||
|                 unroll 2 { |  | ||||||
|                     ubyte @zp nibble = @(nibbles_ptr) |  | ||||||
|                     adpcm.decode_nibble(nibble & 15)     ; first word  (note: upper nibble needs to be zero!) |  | ||||||
|                     adpcm.decode_nibble(nibble>>4)       ; second word  (note: upper nibble is zero, after the shifts.) |  | ||||||
|                     nibbles_ptr++ |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             num_blocks++ |  | ||||||
|             txt.chrout('.') |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return num_blocks |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ; IMA ADPCM decoder.  Supports mono and stereo streams. |  | ||||||
|  |  | ||||||
|     byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8] |  | ||||||
|     uword[] t_step = [ |  | ||||||
|             7, 8, 9, 10, 11, 12, 13, 14, |  | ||||||
|             16, 17, 19, 21, 23, 25, 28, 31, |  | ||||||
|             34, 37, 41, 45, 50, 55, 60, 66, |  | ||||||
|             73, 80, 88, 97, 107, 118, 130, 143, |  | ||||||
|             157, 173, 190, 209, 230, 253, 279, 307, |  | ||||||
|             337, 371, 408, 449, 494, 544, 598, 658, |  | ||||||
|             724, 796, 876, 963, 1060, 1166, 1282, 1411, |  | ||||||
|             1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, |  | ||||||
|             3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, |  | ||||||
|             7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, |  | ||||||
|             15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, |  | ||||||
|             32767] |  | ||||||
|  |  | ||||||
|     uword @requirezp predict       ; decoded 16 bit pcm sample for first channel. |  | ||||||
|     ubyte @requirezp index |  | ||||||
|     uword @requirezp pstep |  | ||||||
|  |  | ||||||
|     sub init(uword startPredict, ubyte startIndex) { |  | ||||||
|         ; initialize first decoding channel. |  | ||||||
|         predict = startPredict |  | ||||||
|         index = startIndex |  | ||||||
|         pstep = t_step[index] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub decode_nibble(ubyte @zp nibble) { |  | ||||||
|         ; Decoder for a single nibble for the first channel. (value of 'nibble' needs to be strictly 0-15 !) |  | ||||||
|         ; This is the hotspot of the decoder algorithm! |  | ||||||
|         ; Note that the generated assembly from this is pretty efficient, |  | ||||||
|         ; rewriting it by hand in asm seems to improve it only ~10%. |  | ||||||
|         cx16.r0s = 0                ; difference |  | ||||||
|         if nibble & %0100 !=0 |  | ||||||
|             cx16.r0s += pstep |  | ||||||
|         pstep >>= 1 |  | ||||||
|         if nibble & %0010 !=0 |  | ||||||
|             cx16.r0s += pstep |  | ||||||
|         pstep >>= 1 |  | ||||||
|         if nibble & %0001 !=0 |  | ||||||
|             cx16.r0s += pstep |  | ||||||
|         pstep >>= 1 |  | ||||||
|         cx16.r0s += pstep |  | ||||||
|         if nibble & %1000 !=0 |  | ||||||
|             predict -= cx16.r0 |  | ||||||
|         else |  | ||||||
|             predict += cx16.r0 |  | ||||||
|  |  | ||||||
|         ; NOTE: the original C/Python code uses a 32 bits prediction value and clips it to a 16 bit word |  | ||||||
|         ;       but for speed reasons we only work with 16 bit words here all the time (with possible clipping error) |  | ||||||
|         ; if predicted > 32767: |  | ||||||
|         ;    predicted = 32767 |  | ||||||
|         ; elif predicted < -32767: |  | ||||||
|         ;    predicted = - 32767 |  | ||||||
|  |  | ||||||
|         index += t_index[nibble] as ubyte |  | ||||||
|         if_neg |  | ||||||
|             index = 0 |  | ||||||
|         else if index >= len(t_step)-1 |  | ||||||
|             index = len(t_step)-1 |  | ||||||
|         pstep = t_step[index] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,234 +0,0 @@ | |||||||
| ; 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 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,111 +0,0 @@ | |||||||
| %import gfx_lores |  | ||||||
| %import math |  | ||||||
|  |  | ||||||
| circles { |  | ||||||
|     const ubyte MAX_NUM_CIRCLES = 80 |  | ||||||
|     const ubyte GROWTH_RATE = 4 |  | ||||||
|     uword[MAX_NUM_CIRCLES] circle_x |  | ||||||
|     uword[MAX_NUM_CIRCLES] circle_y |  | ||||||
|     ubyte[MAX_NUM_CIRCLES] circle_radius |  | ||||||
|     ubyte color |  | ||||||
|     uword total_num_circles |  | ||||||
|  |  | ||||||
|     sub draw(bool use_kernal, uword max_time) -> uword { |  | ||||||
|         if use_kernal |  | ||||||
|             cx16.set_screen_mode(128) |  | ||||||
|         else |  | ||||||
|             gfx_lores.graphics_mode() |  | ||||||
|  |  | ||||||
|         math.rndseed(12345,6789) |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|  |  | ||||||
|         total_num_circles = 0 |  | ||||||
|         color = 16 |  | ||||||
|  |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
|             if use_kernal { |  | ||||||
|                 cx16.GRAPH_set_colors(0,0,0) |  | ||||||
|                 cx16.GRAPH_clear() |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|                 gfx_lores.clear_screen(0) |  | ||||||
|             total_num_circles += draw_circles(use_kernal, max_time) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if use_kernal |  | ||||||
|             cx16.set_screen_mode(3) |  | ||||||
|         else { |  | ||||||
|             gfx_lores.text_mode() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return total_num_circles |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub draw_circles(bool use_kernal, uword max_time) -> uword { |  | ||||||
|         uword @zp x |  | ||||||
|         uword @zp y |  | ||||||
|         ubyte @zp radius |  | ||||||
|  |  | ||||||
|         ubyte num_circles |  | ||||||
|  |  | ||||||
|         while num_circles<MAX_NUM_CIRCLES and cbm.RDTIM16()<max_time { |  | ||||||
|             x = math.rndw() % 320 |  | ||||||
|             y = math.rndw() % 240 |  | ||||||
|             radius = GROWTH_RATE |  | ||||||
|             if not_colliding() { |  | ||||||
|                 while not_edge() and not_colliding() { |  | ||||||
|                     radius += GROWTH_RATE |  | ||||||
|                 } |  | ||||||
|                 radius -= GROWTH_RATE |  | ||||||
|                 if radius>0 { |  | ||||||
|                     color++ |  | ||||||
|                     if color==0 |  | ||||||
|                         color=16 |  | ||||||
|                     if use_kernal { |  | ||||||
|                         cx16.GRAPH_set_colors(color, 255-color, 0) |  | ||||||
|                         cx16.GRAPH_draw_oval(x-radius, y-radius, radius*2, radius*2, true) |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                         gfx_lores.disc(x, y as ubyte, radius, color) |  | ||||||
|                     circle_x[num_circles] = x |  | ||||||
|                     circle_y[num_circles] = y |  | ||||||
|                     circle_radius[num_circles] = radius |  | ||||||
|                     num_circles++ |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return num_circles |  | ||||||
|  |  | ||||||
|         sub not_colliding() -> bool { |  | ||||||
|             if num_circles==0 |  | ||||||
|                 return true |  | ||||||
|             ubyte @zp c |  | ||||||
|             for c in 0 to num_circles-1 { |  | ||||||
|                 if distance(c) < (radius as uword) + circle_radius[c] |  | ||||||
|                     return false |  | ||||||
|             } |  | ||||||
|             return true |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub distance(ubyte cix) -> uword { |  | ||||||
|             word dx = x as word - circle_x[cix] |  | ||||||
|             word dy = y as word - circle_y[cix] |  | ||||||
|             uword sqx = dx*dx as uword |  | ||||||
|             uword sqy = dy*dy as uword |  | ||||||
|             return sqrt(sqx + sqy) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub not_edge() -> bool { |  | ||||||
|             if x as word - radius < 0 |  | ||||||
|                 return false |  | ||||||
|             if x + radius >= 320 |  | ||||||
|                 return false |  | ||||||
|             if y as word - radius < 0 |  | ||||||
|                 return false |  | ||||||
|             if y + radius >= 240 |  | ||||||
|                 return false |  | ||||||
|             return true |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,123 +0,0 @@ | |||||||
| ; conway's game of life. |  | ||||||
|  |  | ||||||
| %import math |  | ||||||
| %import textio |  | ||||||
|  |  | ||||||
| life { |  | ||||||
|     const ubyte WIDTH = 40 |  | ||||||
|     const ubyte HEIGHT = 30 |  | ||||||
|     const uword STRIDE = $0002+WIDTH |  | ||||||
|     uword world1 = memory("world1", (WIDTH+2)*(HEIGHT+2), 0) |  | ||||||
|     uword world2 = memory("world2", (WIDTH+2)*(HEIGHT+2), 0) |  | ||||||
|     uword @requirezp active_world = world1 |  | ||||||
|  |  | ||||||
|    sub benchmark(uword max_time) -> uword { |  | ||||||
|         txt.clear_screen() |  | ||||||
|         sys.memset(world1, (WIDTH+2)*(HEIGHT+2), 0) |  | ||||||
|         sys.memset(world2, (WIDTH+2)*(HEIGHT+2), 0) |  | ||||||
|  |  | ||||||
|         set_start_gen() |  | ||||||
|  |  | ||||||
|         uword gen |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|  |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
|             next_gen() |  | ||||||
|             gen++ |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return gen |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub set_start_gen() { |  | ||||||
|  |  | ||||||
| ; some way to set a custom start generation: |  | ||||||
| ;        str start_gen = "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "          **    " + |  | ||||||
| ;                        "        *    *  " + |  | ||||||
| ;                        "       *        " + |  | ||||||
| ;                        "       *     *  " + |  | ||||||
| ;                        "       ******   " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "                " + |  | ||||||
| ;                        "               " |  | ||||||
| ; |  | ||||||
| ;        for y in 0 to 15 { |  | ||||||
| ;            for x in 0 to 15 { |  | ||||||
| ;                if start_gen[y*16 + x]=='*' |  | ||||||
| ;                    active_world[offset + x] = 1 |  | ||||||
| ;            } |  | ||||||
| ;            offset += STRIDE |  | ||||||
| ;        } |  | ||||||
|  |  | ||||||
|         ; randomize whole world |  | ||||||
|         math.rndseed(12345,9999) |  | ||||||
|         uword offset = STRIDE+1 |  | ||||||
|         ubyte x |  | ||||||
|         ubyte y |  | ||||||
|         for y in 0 to HEIGHT-1 { |  | ||||||
|             for x in 0 to WIDTH-1 { |  | ||||||
|                 active_world[offset+x] = math.rnd() & 1 |  | ||||||
|             } |  | ||||||
|             offset += STRIDE |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub next_gen() { |  | ||||||
|         const ubyte DXOFFSET = 0 |  | ||||||
|         const ubyte DYOFFSET = 0 |  | ||||||
|         ubyte[2] cell_chars = [sc:' ', sc:'●'] |  | ||||||
|  |  | ||||||
|         uword @requirezp new_world = world1 |  | ||||||
|         if active_world == world1 |  | ||||||
|             new_world = world2 |  | ||||||
|  |  | ||||||
|         ; To avoid re-calculating word index lookups into the new- and active world arrays, |  | ||||||
|         ; we calculate the required pointer values upfront. |  | ||||||
|         ; Inside the loop we can use ptr+x just fine (results in efficient LDA (ptr),Y instruction because x is a byte type), |  | ||||||
|         ; and for each row we simply add the stride to the pointer. |  | ||||||
|         ; It's more readable to use active_world[offset] etc, but offset is a word value, and this produces |  | ||||||
|         ; inefficient assembly code because we can't use a register indexed mode in this case. Costly inside a loop. |  | ||||||
|  |  | ||||||
|         uword @requirezp new_world_ptr = new_world + STRIDE+1-DXOFFSET |  | ||||||
|         uword @requirezp active_world_ptr = active_world + STRIDE+1-DXOFFSET |  | ||||||
|  |  | ||||||
|         ubyte x |  | ||||||
|         ubyte y |  | ||||||
|         for y in DYOFFSET to HEIGHT+DYOFFSET-1 { |  | ||||||
|  |  | ||||||
|             cx16.vaddr_autoincr(1, $b000 + 256*y, 0, 2)     ;  allows us to use simple Vera data byte assigns later instead of setchr() calls |  | ||||||
|  |  | ||||||
|             for x in DXOFFSET to WIDTH+DXOFFSET-1 { |  | ||||||
|                 ; count the living neighbors |  | ||||||
|                 ubyte cell = @(active_world_ptr + x) |  | ||||||
|                 uword @requirezp ptr = active_world_ptr + x - STRIDE - 1 |  | ||||||
|                 ubyte neighbors = @(ptr) + @(ptr+1) + @(ptr+2) + |  | ||||||
|                                   @(ptr+STRIDE) + cell + @(ptr+STRIDE+2) + |  | ||||||
|                                   @(ptr+STRIDE*2) + @(ptr+STRIDE*2+1) + @(ptr+STRIDE*2+2) |  | ||||||
|  |  | ||||||
|                 ; apply game of life rules |  | ||||||
|                 if neighbors==3 |  | ||||||
|                     cell=1 |  | ||||||
|                 else if neighbors!=4 |  | ||||||
|                     cell=0 |  | ||||||
|                 @(new_world_ptr + x) = cell |  | ||||||
|  |  | ||||||
|                 ; draw new cell |  | ||||||
|                 ; txt.setchr(x,y,cell_chars[cell]) |  | ||||||
|                 cx16.VERA_DATA0 = cell_chars[cell] |  | ||||||
|             } |  | ||||||
|             active_world_ptr += STRIDE |  | ||||||
|             new_world_ptr += STRIDE |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         active_world = new_world |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| %import textio |  | ||||||
| %import floats |  | ||||||
|  |  | ||||||
| mandelbrot { |  | ||||||
|     const ubyte width = 39 |  | ||||||
|     const ubyte height = 29 |  | ||||||
|     const ubyte max_iter = 15 |  | ||||||
|  |  | ||||||
|     sub calc(uword max_time) -> uword  { |  | ||||||
|         uword num_pixels |  | ||||||
|         ubyte pixelx |  | ||||||
|         ubyte pixely |  | ||||||
|  |  | ||||||
|         txt.home() |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|  |  | ||||||
|         while cbm.RDTIM16() < max_time { |  | ||||||
|             for pixely in 0 to height-1 { |  | ||||||
|                 float yy = (pixely as float)/0.40/height - 1.3 |  | ||||||
|  |  | ||||||
|                 for pixelx in 0 to width-1 { |  | ||||||
|                     float xx = (pixelx as float)/0.32/width - 2.2 |  | ||||||
|  |  | ||||||
|                     float xsquared = 0.0 |  | ||||||
|                     float ysquared = 0.0 |  | ||||||
|                     float x = 0.0 |  | ||||||
|                     float y = 0.0 |  | ||||||
|                     ubyte iter = 0 |  | ||||||
|  |  | ||||||
|                     while iter<max_iter and xsquared+ysquared<4.0 { |  | ||||||
|                         y = x*y*2.0 + yy |  | ||||||
|                         x = xsquared - ysquared + xx |  | ||||||
|                         xsquared = x*x |  | ||||||
|                         ysquared = y*y |  | ||||||
|                         iter++ |  | ||||||
|                     } |  | ||||||
|                     txt.color2(1, max_iter-iter) |  | ||||||
|                     txt.spc() |  | ||||||
|                     num_pixels++ |  | ||||||
|  |  | ||||||
|                     if cbm.RDTIM16()>=max_time |  | ||||||
|                         goto finished |  | ||||||
|                 } |  | ||||||
|                 txt.nl() |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             txt.clear_screen() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
| finished: |  | ||||||
|         txt.color2(1, 6) |  | ||||||
|         return num_pixels |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,343 +0,0 @@ | |||||||
| %import textio |  | ||||||
| %import math |  | ||||||
|  |  | ||||||
| ; Even though prog8 only has support for extremely limited recursion, |  | ||||||
| ; you can write recursive algorithms with a bit of extra work by building your own explicit stack structure. |  | ||||||
| ; This program shows a depth-first maze generation algorithm (1 possible path from start to finish), |  | ||||||
| ; and a depth-first maze solver algorithm, both using a stack to store the path taken. |  | ||||||
|  |  | ||||||
| ; Note: this program can be compiled for multiple target systems. |  | ||||||
|  |  | ||||||
| maze { |  | ||||||
|     uword score |  | ||||||
|  |  | ||||||
|     sub bench(uword max_time) -> uword { |  | ||||||
|         txt.nl() |  | ||||||
|         score=0 |  | ||||||
|         math.rndseed(2345,44332) |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
|             maze.initialize() |  | ||||||
|             maze.drawStartFinish() |  | ||||||
|             if maze.generate(max_time) { |  | ||||||
|                 maze.openpassages() |  | ||||||
|                 maze.drawStartFinish() |  | ||||||
|                 if maze.solve(max_time) { |  | ||||||
|                     maze.drawStartFinish() |  | ||||||
|                 } else break |  | ||||||
|             } else break |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return score |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const uword screenwidth = 40 |  | ||||||
|     const uword screenheight = 30 |  | ||||||
|  |  | ||||||
|     const ubyte numCellsHoriz = (screenwidth-1) / 2 |  | ||||||
|     const ubyte numCellsVert = (screenheight-1) / 2 |  | ||||||
|  |  | ||||||
|     ; maze start and finish cells |  | ||||||
|     const ubyte startCx = 0 |  | ||||||
|     const ubyte startCy = 0 |  | ||||||
|     const ubyte finishCx = numCellsHoriz-1 |  | ||||||
|     const ubyte finishCy = numCellsVert-1 |  | ||||||
|  |  | ||||||
|     ; cell properties |  | ||||||
|     const ubyte STONE = 128 |  | ||||||
|     const ubyte WALKED = 64 |  | ||||||
|     const ubyte BACKTRACKED = 32 |  | ||||||
|     const ubyte UP = 1 |  | ||||||
|     const ubyte RIGHT = 2 |  | ||||||
|     const ubyte DOWN = 4 |  | ||||||
|     const ubyte LEFT = 8 |  | ||||||
|     const ubyte WALLCOLOR = 12 |  | ||||||
|     const ubyte EMPTYCOLOR = 0 |  | ||||||
|  |  | ||||||
|     ; unfortunately on larger screens (cx16), the number of cells exceeds 256 and doesn't fit in a regular array anymore. |  | ||||||
|     uword cells = memory("cells", numCellsHoriz*numCellsVert, 0) |  | ||||||
|  |  | ||||||
|     ubyte[256] cx_stack |  | ||||||
|     ubyte[256] cy_stack |  | ||||||
|     ubyte stackptr |  | ||||||
|  |  | ||||||
|     ubyte[4] directionflags = [LEFT,RIGHT,UP,DOWN] |  | ||||||
|  |  | ||||||
|     sub generate(uword max_time) -> bool { |  | ||||||
|         ubyte cx = startCx |  | ||||||
|         ubyte cy = startCy |  | ||||||
|  |  | ||||||
|         stackptr = 0 |  | ||||||
|         @(celladdr(cx,cy)) &= ~STONE |  | ||||||
|         drawCell(cx, cy) |  | ||||||
|         uword cells_to_carve = numCellsHoriz * numCellsVert - 1 |  | ||||||
|  |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
| carve_restart_after_repath: |  | ||||||
|             ubyte direction = choose_uncarved_direction() |  | ||||||
|             if direction==0 { |  | ||||||
|                 ;backtrack |  | ||||||
|                 stackptr-- |  | ||||||
|                 if stackptr==255 { |  | ||||||
|                     ; stack empty. |  | ||||||
|                     ; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit) |  | ||||||
|                     if cells_to_carve!=0 { |  | ||||||
|                         if repath() |  | ||||||
|                             goto carve_restart_after_repath |  | ||||||
|                     } |  | ||||||
|                     return true |  | ||||||
|                 } |  | ||||||
|                 cx = cx_stack[stackptr] |  | ||||||
|                 cy = cy_stack[stackptr] |  | ||||||
|             } else { |  | ||||||
|                 cx_stack[stackptr] = cx |  | ||||||
|                 cy_stack[stackptr] = cy |  | ||||||
|                 stackptr++ |  | ||||||
|                 if stackptr==0 { |  | ||||||
|                     ; stack overflow, we can't track our path any longer. |  | ||||||
|                     ; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit) |  | ||||||
|                     if cells_to_carve!=0 { |  | ||||||
|                         if repath() |  | ||||||
|                             goto carve_restart_after_repath |  | ||||||
|                     } |  | ||||||
|                     return true |  | ||||||
|                 } |  | ||||||
|                 @(celladdr(cx,cy)) |= direction |  | ||||||
|                 when direction { |  | ||||||
|                     UP -> { |  | ||||||
|                         cy-- |  | ||||||
|                         @(celladdr(cx,cy)) |= DOWN |  | ||||||
|                     } |  | ||||||
|                     RIGHT -> { |  | ||||||
|                         cx++ |  | ||||||
|                         @(celladdr(cx,cy)) |= LEFT |  | ||||||
|  |  | ||||||
|                         score++ |  | ||||||
|                     } |  | ||||||
|                     DOWN -> { |  | ||||||
|                         cy++ |  | ||||||
|                         @(celladdr(cx,cy)) |= UP |  | ||||||
|                     } |  | ||||||
|                     LEFT -> { |  | ||||||
|                         cx-- |  | ||||||
|                         @(celladdr(cx,cy)) |= RIGHT |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 @(celladdr(cx,cy)) &= ~STONE |  | ||||||
|                 cells_to_carve-- |  | ||||||
|                 drawCell(cx, cy) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return false |  | ||||||
|  |  | ||||||
|         sub repath() -> bool { |  | ||||||
|             ; repath: try to find a new start cell with possible directions. |  | ||||||
|             ; we limit our number of searches so that the algorith doesn't get stuck |  | ||||||
|             ; for too long on bad rng... just accept a few unused cells in that case. |  | ||||||
|             repeat 255 { |  | ||||||
|                 do { |  | ||||||
|                     cx = math.rnd() % numCellsHoriz |  | ||||||
|                     cy = math.rnd() % numCellsVert |  | ||||||
|                 } until @(celladdr(cx, cy)) & STONE ==0 |  | ||||||
|                 if available_uncarved()!=0 |  | ||||||
|                     return true |  | ||||||
|             } |  | ||||||
|             return false |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub available_uncarved() -> ubyte { |  | ||||||
|             ubyte candidates = 0 |  | ||||||
|             if cx>0 and @(celladdr(cx-1, cy)) & STONE !=0 |  | ||||||
|                 candidates |= LEFT |  | ||||||
|             if cx<numCellsHoriz-1 and @(celladdr(cx+1, cy)) & STONE !=0 |  | ||||||
|                 candidates |= RIGHT |  | ||||||
|             if cy>0 and @(celladdr(cx, cy-1)) & STONE !=0 |  | ||||||
|                 candidates |= UP |  | ||||||
|             if cy<numCellsVert-1 and @(celladdr(cx, cy+1)) & STONE !=0 |  | ||||||
|                 candidates |= DOWN |  | ||||||
|             return candidates |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub choose_uncarved_direction() -> ubyte { |  | ||||||
|             ubyte candidates =  available_uncarved() |  | ||||||
|             if candidates==0 |  | ||||||
|                 return 0 |  | ||||||
|  |  | ||||||
|             repeat { |  | ||||||
|                 ubyte choice = candidates & directionflags[math.rnd() & 3] |  | ||||||
|                 if choice!=0 |  | ||||||
|                     return choice |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub openpassages() { |  | ||||||
|         ; open just a few extra passages, so that multiple routes are possible in theory. |  | ||||||
|         ubyte numpassages |  | ||||||
|         ubyte cx |  | ||||||
|         ubyte cy |  | ||||||
|         do { |  | ||||||
|             do { |  | ||||||
|                 cx = math.rnd() % (numCellsHoriz-2) + 1 |  | ||||||
|                 cy = math.rnd() % (numCellsVert-2) + 1 |  | ||||||
|             } until @(celladdr(cx, cy)) & STONE ==0 |  | ||||||
|             ubyte direction = directionflags[math.rnd() & 3] |  | ||||||
|             if @(celladdr(cx, cy)) & direction == 0 { |  | ||||||
|                 when direction { |  | ||||||
|                     LEFT -> { |  | ||||||
|                         if @(celladdr(cx-1,cy)) & STONE == 0 { |  | ||||||
|                             @(celladdr(cx,cy)) |= LEFT |  | ||||||
|                             drawCell(cx,cy) |  | ||||||
|                             numpassages++ |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     RIGHT -> { |  | ||||||
|                         if @(celladdr(cx+1,cy)) & STONE == 0 { |  | ||||||
|                             @(celladdr(cx,cy)) |= RIGHT |  | ||||||
|                             drawCell(cx,cy) |  | ||||||
|                             numpassages++ |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     UP -> { |  | ||||||
|                         if @(celladdr(cx,cy-1)) & STONE == 0 { |  | ||||||
|                             @(celladdr(cx,cy)) |= UP |  | ||||||
|                             drawCell(cx,cy) |  | ||||||
|                             numpassages++ |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     DOWN -> { |  | ||||||
|                         if @(celladdr(cx,cy+1)) & STONE == 0 { |  | ||||||
|                             @(celladdr(cx,cy)) |= DOWN |  | ||||||
|                             drawCell(cx,cy) |  | ||||||
|                             numpassages++ |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } until numpassages==10 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub solve(uword max_time) -> bool { |  | ||||||
|         ubyte cx = startCx |  | ||||||
|         ubyte cy = startCy |  | ||||||
|         const uword max_path_length = 1024 |  | ||||||
|  |  | ||||||
|         ; the path through the maze can be longer than 256 so doesn't fit in a regular array.... :( |  | ||||||
|         uword pathstack = memory("pathstack", max_path_length, 0) |  | ||||||
|         uword pathstackptr = 0 |  | ||||||
|  |  | ||||||
|         @(celladdr(cx,cy)) |= WALKED |  | ||||||
|         ; txt.setcc(cx*2+1, cy*2+1, 81, 1) |  | ||||||
|  |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
| solve_loop: |  | ||||||
|             if cx==finishCx and cy==finishCy { |  | ||||||
|                 ;txt.home() |  | ||||||
|                 txt.print("found! path length: ") |  | ||||||
|                 txt.print_uw(pathstackptr) |  | ||||||
|                 txt.nl() |  | ||||||
|                 return true |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             ubyte cell = @(celladdr(cx,cy)) |  | ||||||
|             if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0 { |  | ||||||
|                 @(pathstack + pathstackptr) = UP |  | ||||||
|                 ;txt.setcc(cx*2+1, cy*2, 81, 3) |  | ||||||
|                 cy-- |  | ||||||
|             } |  | ||||||
|             else if cell & DOWN !=0 and @(celladdr(cx,cy+1)) & (WALKED|BACKTRACKED) ==0 { |  | ||||||
|                 @(pathstack + pathstackptr) = DOWN |  | ||||||
|                 ;txt.setcc(cx*2+1, cy*2+2, 81, 3) |  | ||||||
|                 cy++ |  | ||||||
|             } |  | ||||||
|             else if cell & LEFT !=0 and @(celladdr(cx-1,cy)) & (WALKED|BACKTRACKED) ==0 { |  | ||||||
|                 @(pathstack + pathstackptr) = LEFT |  | ||||||
|                 ;txt.setcc(cx*2, cy*2+1, 81, 3) |  | ||||||
|                 cx-- |  | ||||||
|             } |  | ||||||
|             else if cell & RIGHT !=0 and @(celladdr(cx+1,cy)) & (WALKED|BACKTRACKED) ==0 { |  | ||||||
|                 @(pathstack + pathstackptr) = RIGHT |  | ||||||
|                 ;txt.setcc(cx*2+2, cy*2+1, 81, 3) |  | ||||||
|                 cx++ |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 ; dead end, pop stack |  | ||||||
|                 pathstackptr-- |  | ||||||
|                 if pathstackptr==65535 { |  | ||||||
|                     txt.print("no solution?!\n") |  | ||||||
|                     return true |  | ||||||
|                 } |  | ||||||
|                 @(celladdr(cx,cy)) |= BACKTRACKED |  | ||||||
|                 ;txt.setcc(cx*2+1, cy*2+1, 81, 2) |  | ||||||
|                 when @(pathstack + pathstackptr) { |  | ||||||
|                     UP -> { |  | ||||||
|                         ;txt.setcc(cx*2+1, cy*2+2, 81, 9) |  | ||||||
|                         cy++ |  | ||||||
|                     } |  | ||||||
|                     DOWN -> { |  | ||||||
|                         ;txt.setcc(cx*2+1, cy*2, 81, 9) |  | ||||||
|                         cy-- |  | ||||||
|                     } |  | ||||||
|                     LEFT -> { |  | ||||||
|                         ;txt.setcc(cx*2+2, cy*2+1, 81, 9) |  | ||||||
|                         cx++ |  | ||||||
|                     } |  | ||||||
|                     RIGHT -> { |  | ||||||
|                         ;txt.setcc(cx*2, cy*2+1, 81, 9) |  | ||||||
|                         cx-- |  | ||||||
|  |  | ||||||
|                         score++ |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 goto solve_loop |  | ||||||
|             } |  | ||||||
|             pathstackptr++ |  | ||||||
|             if pathstackptr==max_path_length { |  | ||||||
|                 txt.print("stack overflow, path too long\n") |  | ||||||
|                 return true |  | ||||||
|             } |  | ||||||
|             @(celladdr(cx,cy)) |= WALKED |  | ||||||
|             ;txt.setcc(cx*2+1, cy*2+1, 81, 1) |  | ||||||
|         } |  | ||||||
|         return false |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub celladdr(ubyte cx, ubyte cy) -> uword { |  | ||||||
|         return cells+(numCellsHoriz as uword)*cy+cx |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub drawCell(ubyte cx, ubyte cy) { |  | ||||||
|         return |  | ||||||
| ;        ubyte x = cx * 2 + 1 |  | ||||||
| ;        ubyte y = cy * 2 + 1 |  | ||||||
| ;        ubyte doors = @(celladdr(cx,cy)) |  | ||||||
| ;        if doors & UP !=0 |  | ||||||
| ;            txt.setcc(x, y-1, ' ', EMPTYCOLOR) |  | ||||||
| ;        if doors & RIGHT !=0 |  | ||||||
| ;            txt.setcc(x+1, y, ' ', EMPTYCOLOR) |  | ||||||
| ;        if doors & DOWN !=0 |  | ||||||
| ;            txt.setcc(x, y+1, ' ', EMPTYCOLOR) |  | ||||||
| ;        if doors & LEFT !=0 |  | ||||||
| ;            txt.setcc(x-1, y, ' ', EMPTYCOLOR) |  | ||||||
| ;        if doors & STONE !=0 |  | ||||||
| ;            txt.setcc(x, y, 160, WALLCOLOR) |  | ||||||
| ;        else |  | ||||||
| ;            txt.setcc(x, y, 32, EMPTYCOLOR) |  | ||||||
| ; |  | ||||||
| ;        if doors & WALKED !=0 |  | ||||||
| ;            txt.setcc(x, y, 81, 1) |  | ||||||
| ;        if doors & BACKTRACKED !=0 |  | ||||||
| ;            txt.setcc(x, y, 81, 2) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub initialize() { |  | ||||||
|         sys.memset(cells, numCellsHoriz*numCellsVert, STONE) |  | ||||||
|         ; txt.fill_screen(160, WALLCOLOR) |  | ||||||
|         drawStartFinish() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub drawStartFinish() { |  | ||||||
|         ; txt.setcc(startCx*2+1,startCy*2+1,sc:'s',5) |  | ||||||
|         ; txt.setcc(finishCx*2+1, finishCy*2+1, sc:'f', 13) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,63 +0,0 @@ | |||||||
| %import textio |  | ||||||
|  |  | ||||||
| ; Recursive N-Queens solver. |  | ||||||
| ; The problem is: find all possible ways to place 8 Queen chess pieces on a chess board, so that none of them attacks any other. |  | ||||||
| ; (this program prints all solutions without taking mirroring and flipping the chess board into account) |  | ||||||
| ; Note: this program can be compiled for multiple target systems. |  | ||||||
|  |  | ||||||
| queens { |  | ||||||
|     const ubyte NUMQUEENS=8 |  | ||||||
|     ubyte[NUMQUEENS] board |  | ||||||
|  |  | ||||||
|     sub could_place(ubyte row, ubyte col) -> bool { |  | ||||||
|         if row==0 |  | ||||||
|             return true |  | ||||||
|         ubyte i |  | ||||||
|         for i in 0 to row-1 { |  | ||||||
|             if board[i]==col or board[i]-i==col-row or board[i]+i==col+row |  | ||||||
|                 return false |  | ||||||
|         } |  | ||||||
|         return true |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     uword solution_count |  | ||||||
|     uword maximum_duration |  | ||||||
|  |  | ||||||
|     sub place_queen(ubyte row) -> bool { |  | ||||||
|         if row == NUMQUEENS { |  | ||||||
|             solution_count++ |  | ||||||
|             txt.chrout('.') |  | ||||||
|             return cbm.RDTIM16()<maximum_duration |  | ||||||
|         } |  | ||||||
|         bool continue_running=true |  | ||||||
|         ubyte col |  | ||||||
|         for col in 0 to NUMQUEENS-1 { |  | ||||||
|             if could_place(row, col) { |  | ||||||
|                 board[row] = col |  | ||||||
|                 ; we need to save the local variables row and col. |  | ||||||
|                 sys.push(row) |  | ||||||
|                 sys.push(col) |  | ||||||
|                 continue_running = place_queen(row + 1) |  | ||||||
|                 ; restore the local variables after the recursive call. |  | ||||||
|                 col = sys.pop() |  | ||||||
|                 row = sys.pop() |  | ||||||
|                 board[row] = 0 |  | ||||||
|  |  | ||||||
|                 if not continue_running |  | ||||||
|                     break |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return continue_running |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub bench(uword max_time) -> uword { |  | ||||||
|         solution_count = 0 |  | ||||||
|         maximum_duration = max_time |  | ||||||
|         txt.nl() |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|         while cbm.RDTIM16() < maximum_duration { |  | ||||||
|             void place_queen(0) |  | ||||||
|         } |  | ||||||
|         return solution_count |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,69 +0,0 @@ | |||||||
| %import sprites |  | ||||||
| %import coroutines |  | ||||||
| %import math |  | ||||||
|  |  | ||||||
|  |  | ||||||
| animsprites { |  | ||||||
|     uword num_iterations |  | ||||||
|     ubyte[64] sx |  | ||||||
|     ubyte[64] sy |  | ||||||
|     ubyte[64] sc |  | ||||||
|     ubyte[64] dx |  | ||||||
|     ubyte[64] dy |  | ||||||
|     uword maximum_duration |  | ||||||
|  |  | ||||||
|     sub benchmark(uword max_duration) -> uword { |  | ||||||
|         maximum_duration = max_duration |  | ||||||
|         math.rndseed(1122,9876) |  | ||||||
|         cx16.set_screen_mode(3) |  | ||||||
|         cx16.mouse_config2(1) |  | ||||||
|         sprites.set_mousepointer_hand() |  | ||||||
|         repeat 64 |  | ||||||
|             void coroutines.add(animsprite, 0) |  | ||||||
|         cx16.mouse_config2(0) |  | ||||||
|  |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|         coroutines.run(supervisor) |  | ||||||
|  |  | ||||||
|         sprites.reset(0, 64) |  | ||||||
|         return num_iterations |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub supervisor() -> bool { |  | ||||||
|         if cbm.RDTIM16() >= maximum_duration { |  | ||||||
|             coroutines.killall() |  | ||||||
|             return false |  | ||||||
|         } |  | ||||||
|         return true |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub animsprite() { |  | ||||||
|         num_iterations++ |  | ||||||
|         ; set up the sprite |  | ||||||
|         ubyte sprnum = coroutines.current() |  | ||||||
|         cx16.r6L, cx16.r7 = sprites.get_data_ptr(0) |  | ||||||
|         sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0) |  | ||||||
|         sx[sprnum] = math.rnd() |  | ||||||
|         sy[sprnum] = math.rnd() |  | ||||||
|         sc[sprnum] = math.rnd() |  | ||||||
|         dx[sprnum] = if math.rnd()&1 == 1  1 else 255 |  | ||||||
|         dy[sprnum] = if math.rnd()&1 == 1  1 else 255 |  | ||||||
|  |  | ||||||
|         ; move the sprite around |  | ||||||
|         while sc[sprnum]!=0 { |  | ||||||
|             animate(sprnum) |  | ||||||
|             void coroutines.yield() |  | ||||||
|             sprnum = coroutines.current() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub animate(ubyte spr) { |  | ||||||
|             defer sc[spr]-- |  | ||||||
|             sprites.pos(spr, sx[spr], sy[spr]) |  | ||||||
|             sx[spr] += dx[spr] |  | ||||||
|             sy[spr] += dy[spr] |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ; end the task but replace it with a fresh animated sprite task |  | ||||||
|         void coroutines.add(animsprite, 0) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,989 +0,0 @@ | |||||||
| %import textio |  | ||||||
| %import conv |  | ||||||
| %import strings |  | ||||||
|  |  | ||||||
|  |  | ||||||
| textelite { |  | ||||||
|  |  | ||||||
|     const ubyte numforLave = 7      ;  Lave is 7th generated planet in galaxy one |  | ||||||
|     const ubyte numforZaonce = 129 |  | ||||||
|     const ubyte numforDiso = 147 |  | ||||||
|     const ubyte numforRiedquat = 46 |  | ||||||
|     uword num_commands |  | ||||||
|  |  | ||||||
|     sub bench(uword max_time) -> uword { |  | ||||||
|         num_commands = 0 |  | ||||||
|         txt.lowercase() |  | ||||||
|         cbm.SETTIM(0,0,0) |  | ||||||
|         while cbm.RDTIM16()<max_time { |  | ||||||
|             reinit() |  | ||||||
|             run_commands(max_time) |  | ||||||
|         } |  | ||||||
|         return num_commands |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub reinit() { |  | ||||||
|         ;txt.clear_screen() |  | ||||||
|         ;txt.print("\n --- TextElite v1.3 ---\n") |  | ||||||
|         txt.print("\nnew game\n") |  | ||||||
|         elite_planet.set_seed(0, 0) |  | ||||||
|         elite_galaxy.travel_to(1, numforLave) |  | ||||||
|         elite_market.init(0)  ;  Lave's market is seeded with 0 |  | ||||||
|         elite_ship.init() |  | ||||||
|         elite_planet.display(false, 0) |  | ||||||
|         input_index = 0 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub run_commands(uword max_time) { |  | ||||||
|         while cbm.RDTIM16() < max_time { |  | ||||||
|             str input = "????????" |  | ||||||
|             ;txt.print("\nCash: ") |  | ||||||
|             ;elite_util.print_10s(elite_ship.cash) |  | ||||||
|             ;txt.print("\nCommand (?=help): ") |  | ||||||
|             ubyte num_chars = next_input(input) |  | ||||||
|             ;txt.nl() |  | ||||||
|             if num_chars!=0 { |  | ||||||
|                 when input[0] { |  | ||||||
|                     'q' -> { |  | ||||||
|                         bool has_error = false |  | ||||||
|                         if elite_galaxy.number != 2 { |  | ||||||
|                             txt.print("\nERROR: galaxy is not 2: ") |  | ||||||
|                             txt.print_ub(elite_galaxy.number) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if elite_planet.number != 164 { |  | ||||||
|                             txt.print("\nERROR: planet is not 164: ") |  | ||||||
|                             txt.print_ub(elite_planet.number) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if elite_planet.x != 116 { |  | ||||||
|                             txt.print("\nERROR: planet.x is not 116: ") |  | ||||||
|                             txt.print_ub(elite_planet.x) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if elite_planet.y != 201 { |  | ||||||
|                             txt.print("\nERROR: planet.y is not 201: ") |  | ||||||
|                             txt.print_ub(elite_planet.y) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if "ribeen" != elite_planet.name { |  | ||||||
|                             txt.print("\nERROR: planet.name is not 'ribeen': ") |  | ||||||
|                             txt.print(elite_planet.name) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if elite_ship.cash != 1212 { |  | ||||||
|                             txt.print("\nERROR: cash is not 1212: ") |  | ||||||
|                             txt.print_uw(elite_ship.cash) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if elite_ship.fuel != 50 { |  | ||||||
|                             txt.print("\nERROR: fuel is not 50:") |  | ||||||
|                             txt.print_ub(elite_ship.fuel) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if elite_ship.cargohold[0] != 3 { |  | ||||||
|                             txt.print("\nERROR: food is not 3:") |  | ||||||
|                             txt.print_ub(elite_ship.cargohold[0]) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if elite_ship.cargohold[1] != 0 { |  | ||||||
|                             txt.print("\nERROR: textiles is not 0:") |  | ||||||
|                             txt.print_ub(elite_ship.cargohold[1]) |  | ||||||
|                             txt.nl() |  | ||||||
|                             has_error=true |  | ||||||
|                         } |  | ||||||
|                         if has_error |  | ||||||
|                             sys.exit(1) |  | ||||||
|                         return |  | ||||||
|                     } |  | ||||||
|                     'b' -> elite_trader.do_buy() |  | ||||||
|                     's' -> elite_trader.do_sell() |  | ||||||
|                     'f' -> elite_trader.do_fuel() |  | ||||||
|                     'j' -> elite_trader.do_jump() |  | ||||||
|                     't' -> elite_trader.do_teleport() |  | ||||||
|                     'g' -> elite_trader.do_next_galaxy() |  | ||||||
|                     'i' -> elite_trader.do_info() |  | ||||||
|                     'm' -> { |  | ||||||
|                         if input[1]=='a' and input[2]=='p' |  | ||||||
|                             elite_trader.do_map() |  | ||||||
|                         else |  | ||||||
|                             elite_trader.do_show_market() |  | ||||||
|                     } |  | ||||||
|                     'l' -> elite_trader.do_local() |  | ||||||
|                     'c' -> elite_trader.do_cash() |  | ||||||
|                     'h' -> elite_trader.do_hold() |  | ||||||
|                 } |  | ||||||
|                 num_commands++ |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     str[] inputs = [ |  | ||||||
|         "i", |  | ||||||
|         "diso", |  | ||||||
|         "i", |  | ||||||
|         "lave", |  | ||||||
|         "m", |  | ||||||
|         "b", |  | ||||||
|         "food", |  | ||||||
|         "15", |  | ||||||
|         "map", |  | ||||||
|         "g", |  | ||||||
|         "map", |  | ||||||
|         "l", |  | ||||||
|         "j", |  | ||||||
|         "zao", |  | ||||||
|         "s", |  | ||||||
|         "food", |  | ||||||
|         "12", |  | ||||||
|         "tele", |  | ||||||
|         "quti", |  | ||||||
|         "tele", |  | ||||||
|         "aro", |  | ||||||
|         "i", |  | ||||||
|         "diso", |  | ||||||
|         "i", |  | ||||||
|         "lave", |  | ||||||
|         "i", |  | ||||||
|         "zao", |  | ||||||
|         "galhyp", |  | ||||||
|         "fuel", |  | ||||||
|         "20", |  | ||||||
|         "j", |  | ||||||
|         "rib", |  | ||||||
|         "i", |  | ||||||
|         "rib", |  | ||||||
|         "i", |  | ||||||
|         "tiri", |  | ||||||
|         "q", |  | ||||||
|         0 |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|     ubyte input_index |  | ||||||
|  |  | ||||||
|     sub next_input(str buffer) -> ubyte { |  | ||||||
|         input_index++ |  | ||||||
|         return strings.copy(inputs[input_index], buffer) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| elite_trader { |  | ||||||
|     str input = "??????????" |  | ||||||
|     ubyte num_chars |  | ||||||
|  |  | ||||||
|     sub do_jump() { |  | ||||||
|         ;txt.print("\nJump to what system? ") |  | ||||||
|         jump_to_system() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_teleport() { |  | ||||||
|         ;txt.print("\nCheat! Teleport to what system? ") |  | ||||||
|         ubyte fuel = elite_ship.fuel |  | ||||||
|         elite_ship.fuel = 255 |  | ||||||
|         jump_to_system() |  | ||||||
|         elite_ship.fuel = fuel |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub jump_to_system() { |  | ||||||
|         void textelite.next_input(input) |  | ||||||
|         ubyte current_planet = elite_planet.number |  | ||||||
|         ubyte x = elite_planet.x |  | ||||||
|         ubyte y = elite_planet.y |  | ||||||
|         if elite_galaxy.search_closest_planet(input) { |  | ||||||
|             ubyte distance = elite_planet.distance(x, y) |  | ||||||
|             if distance <= elite_ship.fuel { |  | ||||||
|                 elite_galaxy.init_market_for_planet() |  | ||||||
|                 elite_ship.fuel -= distance |  | ||||||
|                 ;txt.print("\n\nHyperspace jump! Arrived at:\n") |  | ||||||
|                 elite_planet.display(true,0 ) |  | ||||||
|                 return |  | ||||||
|             } |  | ||||||
|             ;txt.print("\nInsufficient fuel\n") |  | ||||||
|         } else { |  | ||||||
|             ;txt.print(" Not found!\n") |  | ||||||
|         } |  | ||||||
|         elite_galaxy.travel_to(elite_galaxy.number, current_planet) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_buy() { |  | ||||||
|         ;txt.print("\nBuy what commodity? ") |  | ||||||
|         str commodity = "???????????????" |  | ||||||
|         void textelite.next_input(commodity) |  | ||||||
|         ubyte ci = elite_market.match(commodity) |  | ||||||
|         if ci & 128 !=0 { |  | ||||||
|             txt.print("Unknown\n") |  | ||||||
|         } else { |  | ||||||
|             ;txt.print("\nHow much? ") |  | ||||||
|             void textelite.next_input(input) |  | ||||||
|             ubyte amount = conv.str2ubyte(input) |  | ||||||
|             if elite_market.current_quantity[ci] < amount { |  | ||||||
|                 txt.print(" Insufficient supply!\n") |  | ||||||
|             } else { |  | ||||||
|                 uword price = elite_market.current_price[ci] * amount |  | ||||||
|                 ;txt.print(" Total price: ") |  | ||||||
|                 ;elite_util.print_10s(price) |  | ||||||
|                 if price > elite_ship.cash { |  | ||||||
|                     txt.print(" Not enough cash!\n") |  | ||||||
|                 } else { |  | ||||||
|                     elite_ship.cash -= price |  | ||||||
|                     elite_ship.cargohold[ci] += amount |  | ||||||
|                     elite_market.current_quantity[ci] -= amount |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_sell() { |  | ||||||
|         ;txt.print("\nSell what commodity? ") |  | ||||||
|         str commodity = "???????????????" |  | ||||||
|         void textelite.next_input(commodity) |  | ||||||
|         ubyte ci = elite_market.match(commodity) |  | ||||||
|         if ci & 128 !=0 { |  | ||||||
|             txt.print("Unknown\n") |  | ||||||
|         } else { |  | ||||||
|             ;txt.print("\nHow much? ") |  | ||||||
|             void textelite.next_input(input) |  | ||||||
|             ubyte amount = conv.str2ubyte(input) |  | ||||||
|             if elite_ship.cargohold[ci] < amount { |  | ||||||
|                 txt.print(" Insufficient supply!\n") |  | ||||||
|             } else { |  | ||||||
|                 uword price = elite_market.current_price[ci] * amount |  | ||||||
|                 ;txt.print(" Total price: ") |  | ||||||
|                 ;elite_util.print_10s(price) |  | ||||||
|                 elite_ship.cash += price |  | ||||||
|                 elite_ship.cargohold[ci] -= amount |  | ||||||
|                 elite_market.current_quantity[ci] += amount |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_fuel() { |  | ||||||
|         ;txt.print("\nBuy fuel. Amount? ") |  | ||||||
|         void textelite.next_input(input) |  | ||||||
|         ubyte buy_fuel = 10*conv.str2ubyte(input) |  | ||||||
|         ubyte max_fuel = elite_ship.Max_fuel - elite_ship.fuel |  | ||||||
|         if buy_fuel > max_fuel |  | ||||||
|             buy_fuel = max_fuel |  | ||||||
|         uword price = buy_fuel as uword * elite_ship.Fuel_cost |  | ||||||
|         if price > elite_ship.cash { |  | ||||||
|             txt.print("Not enough cash!\n") |  | ||||||
|         } else { |  | ||||||
|             elite_ship.cash -= price |  | ||||||
|             elite_ship.fuel += buy_fuel |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_cash() { |  | ||||||
|         ;txt.print("\nCheat! Set cash amount: ") |  | ||||||
|         void textelite.next_input(input) |  | ||||||
|         elite_ship.cash = conv.str2uword(input) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_hold() { |  | ||||||
|         ;txt.print("\nCheat! Set cargohold size: ") |  | ||||||
|         void textelite.next_input(input) |  | ||||||
|         elite_ship.Max_cargo = conv.str2ubyte(input) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_next_galaxy() { |  | ||||||
|         txt.print("\n>>>>> Galaxy Hyperjump!\n") |  | ||||||
|         elite_galaxy.travel_to(elite_galaxy.number+1, elite_planet.number) |  | ||||||
|         elite_planet.display(false, 0) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_info() { |  | ||||||
|         ;txt.print("\nSystem name (empty=current): ") |  | ||||||
|         num_chars = textelite.next_input(input) |  | ||||||
|         if num_chars!=0 { |  | ||||||
|             ubyte current_planet = elite_planet.number |  | ||||||
|             ubyte x = elite_planet.x |  | ||||||
|             ubyte y = elite_planet.y |  | ||||||
|             if elite_galaxy.search_closest_planet(input) { |  | ||||||
|                 ubyte distance = elite_planet.distance(x, y) |  | ||||||
|                 elite_planet.display(false, distance) |  | ||||||
|             } else { |  | ||||||
|                 ;txt.print(" Not found!") |  | ||||||
|             } |  | ||||||
|             elite_galaxy.travel_to(elite_galaxy.number, current_planet) |  | ||||||
|         } else { |  | ||||||
|             elite_planet.display(false, 0) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_local() { |  | ||||||
|         elite_galaxy.local_area() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_map() { |  | ||||||
|         ;txt.print("\n(l)ocal or (g)alaxy starmap? ") |  | ||||||
|         num_chars = textelite.next_input(input) |  | ||||||
|         if num_chars!=0 { |  | ||||||
|             elite_galaxy.starmap(input[0]=='l') |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub do_show_market() { |  | ||||||
|         elite_market.display() |  | ||||||
|         ;txt.print("\nFuel: ") |  | ||||||
|         ;elite_util.print_10s(elite_ship.fuel) |  | ||||||
|         ;txt.print("   Cargohold space: ") |  | ||||||
|         ;txt.print_ub(elite_ship.cargo_free()) |  | ||||||
|         ;txt.print("t\n") |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| elite_ship { |  | ||||||
|     const ubyte Max_fuel = 70 |  | ||||||
|     const ubyte Fuel_cost = 2 |  | ||||||
|     ubyte Max_cargo = 20 |  | ||||||
|  |  | ||||||
|     ubyte fuel |  | ||||||
|     uword cash |  | ||||||
|     ubyte[17] cargohold |  | ||||||
|  |  | ||||||
|     sub init() { |  | ||||||
|         sys.memset(cargohold, len(cargohold), 0) |  | ||||||
|         fuel = Max_fuel |  | ||||||
|         cash = 1000 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| elite_market { |  | ||||||
|     ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] |  | ||||||
|     byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] |  | ||||||
|     ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] |  | ||||||
|     ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] |  | ||||||
|     str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", |  | ||||||
|                      "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] |  | ||||||
|  |  | ||||||
|     ubyte[17] current_quantity |  | ||||||
|     uword[17] current_price |  | ||||||
|  |  | ||||||
|     sub init(ubyte fluct) { |  | ||||||
|         ; Prices and availabilities are influenced by the planet's economy type |  | ||||||
|         ; (0-7) and a random "fluctuation" byte that was kept within the saved |  | ||||||
|         ; commander position to keep the market prices constant over gamesaves. |  | ||||||
|         ; Availabilities must be saved with the game since the player alters them |  | ||||||
|         ; by buying (and selling(?)) |  | ||||||
|         ; |  | ||||||
|         ; Almost all commands are one byte only and overflow "errors" are |  | ||||||
|         ; extremely frequent and exploited. |  | ||||||
|         ; |  | ||||||
|         ; Trade Item prices are held internally in a single byte=true value/4. |  | ||||||
|         ; The decimal point in prices is introduced only when printing them. |  | ||||||
|         ; Internally, all prices are integers. |  | ||||||
|         ; The player's cash is held in four bytes. |  | ||||||
|         ubyte ci |  | ||||||
|         for ci in 0 to len(names)-1 { |  | ||||||
|             word product |  | ||||||
|             byte changing |  | ||||||
|             product = elite_planet.economy as word * gradients[ci] |  | ||||||
|             changing = fluct & maskbytes[ci]  as byte |  | ||||||
|             ubyte q = (basequants[ci] as word + changing - product) as ubyte |  | ||||||
|             if q & $80 !=0 |  | ||||||
|                 q = 0  ; clip to positive 8-bit |  | ||||||
|             current_quantity[ci] = q & $3f |  | ||||||
|             q = (baseprices[ci] + changing + product) as ubyte |  | ||||||
|             current_price[ci] = q * $0004 |  | ||||||
|         } |  | ||||||
|         current_quantity[16] = 0        ; force nonavailability of Alien Items |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub display() { |  | ||||||
|         return |  | ||||||
| ;        ubyte ci |  | ||||||
| ;        txt.nl() |  | ||||||
| ;        elite_planet.print_name_uppercase() |  | ||||||
| ;        txt.print(" trade market:\n    COMMODITY / PRICE / AVAIL / IN HOLD\n") |  | ||||||
| ;        for ci in 0 to len(names)-1 { |  | ||||||
| ;            elite_util.print_right(13, names[ci]) |  | ||||||
| ;            txt.print("   ") |  | ||||||
| ;            elite_util.print_10s(current_price[ci]) |  | ||||||
| ;            txt.column(24) |  | ||||||
| ;            txt.print_ub(current_quantity[ci]) |  | ||||||
| ;            txt.chrout(' ') |  | ||||||
| ;            when units[ci] { |  | ||||||
| ;                0 -> txt.chrout('t') |  | ||||||
| ;                1 -> txt.print("kg") |  | ||||||
| ;                2 -> txt.chrout('g') |  | ||||||
| ;            } |  | ||||||
| ;            txt.column(32) |  | ||||||
| ;            txt.print_ub(elite_ship.cargohold[ci]) |  | ||||||
| ;            txt.nl() |  | ||||||
| ;        } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub match(uword nameptr) -> ubyte { |  | ||||||
|         ubyte ci |  | ||||||
|         for ci in 0 to len(names)-1 { |  | ||||||
|             if elite_util.prefix_matches(nameptr, names[ci]) |  | ||||||
|                 return ci |  | ||||||
|         } |  | ||||||
|         return 255 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| elite_galaxy { |  | ||||||
|     const uword GALSIZE = 256 |  | ||||||
|     const uword base0 = $5A4A       ; seeds for the first galaxy |  | ||||||
|     const uword base1 = $0248 |  | ||||||
|     const uword base2 = $B753 |  | ||||||
|  |  | ||||||
|     str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" |  | ||||||
|  |  | ||||||
|     ubyte number |  | ||||||
|  |  | ||||||
|     uword[3] seed |  | ||||||
|  |  | ||||||
|     sub init(ubyte galaxynum) { |  | ||||||
|         number = 1 |  | ||||||
|         elite_planet.number = 255 |  | ||||||
|         seed[0] = base0 |  | ||||||
|         seed[1] = base1 |  | ||||||
|         seed[2] = base2 |  | ||||||
|         repeat galaxynum-1 { |  | ||||||
|             nextgalaxy() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub nextgalaxy() { |  | ||||||
|         textelite.num_commands++ |  | ||||||
|  |  | ||||||
|         seed[0] = twist(seed[0]) |  | ||||||
|         seed[1] = twist(seed[1]) |  | ||||||
|         seed[2] = twist(seed[2]) |  | ||||||
|         number++ |  | ||||||
|         if number==9 |  | ||||||
|             number = 1 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub travel_to(ubyte galaxynum, ubyte system) { |  | ||||||
|         init(galaxynum) |  | ||||||
|         generate_next_planet()   ; always at least planet 0  (separate to avoid repeat ubyte overflow) |  | ||||||
|         repeat system { |  | ||||||
|             generate_next_planet() |  | ||||||
|             textelite.num_commands++ |  | ||||||
|         } |  | ||||||
|         elite_planet.name = make_current_planet_name() |  | ||||||
|         init_market_for_planet() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub init_market_for_planet() { |  | ||||||
|         elite_market.init(lsb(seed[0])+msb(seed[2])) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub search_closest_planet(uword nameptr) -> bool { |  | ||||||
|         textelite.num_commands++ |  | ||||||
|  |  | ||||||
|         ubyte x = elite_planet.x |  | ||||||
|         ubyte y = elite_planet.y |  | ||||||
|         ubyte current_planet_num = elite_planet.number |  | ||||||
|  |  | ||||||
|         init(number) |  | ||||||
|         bool found = false |  | ||||||
|         ubyte current_closest_pi |  | ||||||
|         ubyte current_distance = 127 |  | ||||||
|         ubyte pi |  | ||||||
|         for pi in 0 to 255 { |  | ||||||
|             generate_next_planet() |  | ||||||
|             elite_planet.name = make_current_planet_name() |  | ||||||
|             if elite_util.prefix_matches(nameptr, elite_planet.name) { |  | ||||||
|                 ubyte distance = elite_planet.distance(x, y) |  | ||||||
|                 if distance < current_distance { |  | ||||||
|                     current_distance = distance |  | ||||||
|                     current_closest_pi = pi |  | ||||||
|                     found = true |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if found |  | ||||||
|             travel_to(number, current_closest_pi) |  | ||||||
|         else |  | ||||||
|             travel_to(number, current_planet_num) |  | ||||||
|  |  | ||||||
|         return found |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub local_area() { |  | ||||||
|         ubyte current_planet = elite_planet.number |  | ||||||
|         ubyte px = elite_planet.x |  | ||||||
|         ubyte py = elite_planet.y |  | ||||||
|         ubyte pn = 0 |  | ||||||
|  |  | ||||||
|         init(number) |  | ||||||
| ;        txt.print("\nGalaxy #") |  | ||||||
| ;        txt.print_ub(number) |  | ||||||
| ;        txt.print(" - systems in vicinity:\n") |  | ||||||
|         do { |  | ||||||
|             generate_next_planet() |  | ||||||
|             ubyte distance = elite_planet.distance(px, py) |  | ||||||
|             if distance <= elite_ship.Max_fuel { |  | ||||||
| ;                if distance <= elite_ship.fuel |  | ||||||
| ;                    txt.chrout('*') |  | ||||||
| ;                else |  | ||||||
| ;                    txt.chrout('-') |  | ||||||
| ;                txt.spc() |  | ||||||
|                 elite_planet.name = make_current_planet_name() |  | ||||||
|                 elite_planet.display(true, distance) |  | ||||||
|             } |  | ||||||
|             pn++ |  | ||||||
|         } until pn==0 |  | ||||||
|  |  | ||||||
|         travel_to(number, current_planet) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub starmap(bool local) { |  | ||||||
|         ubyte current_planet = elite_planet.number |  | ||||||
|         ubyte px = elite_planet.x |  | ||||||
|         ubyte py = elite_planet.y |  | ||||||
|         str current_name = "        "       ; 8 max |  | ||||||
|         ubyte pn = 0 |  | ||||||
|  |  | ||||||
|         current_name = elite_planet.name |  | ||||||
|         init(number) |  | ||||||
| ;        txt.clear_screen() |  | ||||||
| ;        txt.print("Galaxy #") |  | ||||||
| ;        txt.print_ub(number) |  | ||||||
| ;        if local |  | ||||||
| ;            txt.print(" - local systems") |  | ||||||
| ;        else |  | ||||||
| ;            txt.print(" - galaxy") |  | ||||||
| ;        txt.print(" starmap:\n") |  | ||||||
|         ubyte max_distance = 255 |  | ||||||
|         if local |  | ||||||
|             max_distance = elite_ship.Max_fuel |  | ||||||
|         ubyte home_sx |  | ||||||
|         ubyte home_sy |  | ||||||
|         ubyte home_distance |  | ||||||
|  |  | ||||||
|         do { |  | ||||||
|             generate_next_planet() |  | ||||||
|             ubyte distance = elite_planet.distance(px, py) |  | ||||||
|             if distance <= max_distance { |  | ||||||
|                 elite_planet.name = make_current_planet_name() |  | ||||||
|                 elite_planet.name[0] = strings.upperchar(elite_planet.name[0]) |  | ||||||
|                 uword tx = elite_planet.x |  | ||||||
|                 uword ty = elite_planet.y |  | ||||||
|                 if local { |  | ||||||
|                     tx = tx + 24 - px |  | ||||||
|                     ty = ty + 24 - py |  | ||||||
|                 } |  | ||||||
|                 ubyte sx = display_scale_x(tx) |  | ||||||
|                 ubyte sy = display_scale_y(ty) |  | ||||||
|                 ubyte char = '*' |  | ||||||
|                 if elite_planet.number==current_planet |  | ||||||
|                     char = '%' |  | ||||||
|                 if local { |  | ||||||
|                     print_planet_details(elite_planet.name, sx, sy, distance) |  | ||||||
|                 } else if elite_planet.number==current_planet { |  | ||||||
|                     home_distance = distance |  | ||||||
|                     home_sx = sx |  | ||||||
|                     home_sy = sy |  | ||||||
|                 } |  | ||||||
|                 ; txt.setchr(2+sx, 2+sy, char) |  | ||||||
|             } |  | ||||||
|             pn++ |  | ||||||
|         } until pn==0 |  | ||||||
|  |  | ||||||
|         if not local |  | ||||||
|             print_planet_details(current_name, home_sx, home_sy, home_distance) |  | ||||||
|  |  | ||||||
| ;        if local |  | ||||||
| ;            txt.plot(0, display_scale_y(64) + 4) |  | ||||||
| ;        else |  | ||||||
| ;            txt.plot(0, display_scale_y(256) + 4 as ubyte) |  | ||||||
|         travel_to(number, current_planet) |  | ||||||
|  |  | ||||||
|         sub print_planet_details(str name, ubyte screenx, ubyte screeny, ubyte d) { |  | ||||||
|             return |  | ||||||
| ;            txt.plot(2+screenx-2, 2+screeny+1) |  | ||||||
| ;            txt.print(name) |  | ||||||
| ;            if d!=0 { |  | ||||||
| ;                txt.plot(2+screenx-2, 2+screeny+2) |  | ||||||
| ;                elite_util.print_10s(d) |  | ||||||
| ;                txt.print(" LY") |  | ||||||
| ;            } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub display_scale_x(uword x) -> ubyte { |  | ||||||
|             if local |  | ||||||
|                 return x/2 as ubyte |  | ||||||
|             return x/8 as ubyte |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub display_scale_y(uword y) -> ubyte { |  | ||||||
|             if local |  | ||||||
|                 return y/4 as ubyte |  | ||||||
|             return y/16 as ubyte |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ubyte pn_pair1 |  | ||||||
|     ubyte pn_pair2 |  | ||||||
|     ubyte pn_pair3 |  | ||||||
|     ubyte pn_pair4 |  | ||||||
|     bool longname |  | ||||||
|  |  | ||||||
|     sub generate_next_planet() { |  | ||||||
|         determine_planet_properties() |  | ||||||
|         longname = lsb(seed[0]) & 64 !=0 |  | ||||||
|  |  | ||||||
|         ; Always four iterations of random number |  | ||||||
|         pn_pair1 = (msb(seed[2]) & 31) * 2 |  | ||||||
|         tweakseed() |  | ||||||
|         pn_pair2 = (msb(seed[2]) & 31) * 2 |  | ||||||
|         tweakseed() |  | ||||||
|         pn_pair3 = (msb(seed[2]) & 31) * 2 |  | ||||||
|         tweakseed() |  | ||||||
|         pn_pair4 = (msb(seed[2]) & 31) * 2 |  | ||||||
|         tweakseed() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub make_current_planet_name() -> str { |  | ||||||
|         ubyte ni = 0 |  | ||||||
|         str name = "         "    ; max 8 |  | ||||||
|  |  | ||||||
|         if pn_pairs[pn_pair1] != '.' { |  | ||||||
|             name[ni] = pn_pairs[pn_pair1] |  | ||||||
|             ni++ |  | ||||||
|         } |  | ||||||
|         if pn_pairs[pn_pair1+1] != '.' { |  | ||||||
|             name[ni] = pn_pairs[pn_pair1+1] |  | ||||||
|             ni++ |  | ||||||
|         } |  | ||||||
|         if pn_pairs[pn_pair2] != '.' { |  | ||||||
|             name[ni] = pn_pairs[pn_pair2] |  | ||||||
|             ni++ |  | ||||||
|         } |  | ||||||
|         if pn_pairs[pn_pair2+1] != '.' { |  | ||||||
|             name[ni] = pn_pairs[pn_pair2+1] |  | ||||||
|             ni++ |  | ||||||
|         } |  | ||||||
|         if pn_pairs[pn_pair3] != '.' { |  | ||||||
|             name[ni] = pn_pairs[pn_pair3] |  | ||||||
|             ni++ |  | ||||||
|         } |  | ||||||
|         if pn_pairs[pn_pair3+1] != '.' { |  | ||||||
|             name[ni] = pn_pairs[pn_pair3+1] |  | ||||||
|             ni++ |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if longname { |  | ||||||
|             if pn_pairs[pn_pair4] != '.' { |  | ||||||
|                 name[ni] = pn_pairs[pn_pair4] |  | ||||||
|                 ni++ |  | ||||||
|             } |  | ||||||
|             if pn_pairs[pn_pair4+1] != '.' { |  | ||||||
|                 name[ni] = pn_pairs[pn_pair4+1] |  | ||||||
|                 ni++ |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         name[ni] = 0 |  | ||||||
|         return name |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub determine_planet_properties() { |  | ||||||
|         ; create the planet's characteristics |  | ||||||
|         elite_planet.number++ |  | ||||||
|         elite_planet.x = msb(seed[1]) |  | ||||||
|         elite_planet.y = msb(seed[0]) |  | ||||||
|         elite_planet.govtype = lsb(seed[1]) >> 3 & 7  ; bits 3,4 &5 of w1 |  | ||||||
|         elite_planet.economy = msb(seed[0]) & 7  ; bits 8,9 &A of w0 |  | ||||||
|         if elite_planet.govtype <= 1 |  | ||||||
|             elite_planet.economy = (elite_planet.economy | 2) |  | ||||||
|         elite_planet.techlevel = (msb(seed[1]) & 3) + (elite_planet.economy ^ 7) |  | ||||||
|         elite_planet.techlevel += elite_planet.govtype >> 1 |  | ||||||
|         if elite_planet.govtype & 1 !=0 |  | ||||||
|             elite_planet.techlevel++ |  | ||||||
|         elite_planet.population = 4 * elite_planet.techlevel + elite_planet.economy |  | ||||||
|         elite_planet.population += elite_planet.govtype + 1 |  | ||||||
|         elite_planet.productivity = ((elite_planet.economy ^ 7) + 3) * (elite_planet.govtype + 4) |  | ||||||
|         elite_planet.productivity *= elite_planet.population * 8 |  | ||||||
|         ubyte seed2_msb = msb(seed[2]) |  | ||||||
|         elite_planet.radius = mkword((seed2_msb & 15) + 11, elite_planet.x) |  | ||||||
|         elite_planet.species_is_alien = lsb(seed[2]) & 128 !=0      ; bit 7 of w2_lo |  | ||||||
|         if elite_planet.species_is_alien { |  | ||||||
|             elite_planet.species_size = (seed2_msb >> 2) & 7      ; bits 2-4 of w2_hi |  | ||||||
|             elite_planet.species_color = seed2_msb >> 5           ; bits 5-7 of w2_hi |  | ||||||
|             elite_planet.species_look = (seed2_msb ^ msb(seed[1])) & 7   ;bits 0-2 of (w0_hi EOR w1_hi) |  | ||||||
|             elite_planet.species_kind = (elite_planet.species_look + (seed2_msb & 3)) & 7      ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         elite_planet.goatsoup_seed[0] = lsb(seed[1]) |  | ||||||
|         elite_planet.goatsoup_seed[1] = msb(seed[1]) |  | ||||||
|         elite_planet.goatsoup_seed[2] = lsb(seed[2]) |  | ||||||
|         elite_planet.goatsoup_seed[3] = seed2_msb |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub tweakseed() { |  | ||||||
|         uword temp = seed[0] + seed[1] + seed[2] |  | ||||||
|         seed[0] = seed[1] |  | ||||||
|         seed[1] = seed[2] |  | ||||||
|         seed[2] = temp |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub twist(uword x) -> uword { |  | ||||||
|         ubyte xh = msb(x) |  | ||||||
|         ubyte xl = lsb(x) |  | ||||||
|         xh <<= 1        ; make sure carry flag is not used on first shift! |  | ||||||
|         rol(xl) |  | ||||||
|         return mkword(xh, xl) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| elite_planet { |  | ||||||
|     str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"] |  | ||||||
|     str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""] |  | ||||||
|     str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"] |  | ||||||
|     str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] |  | ||||||
|     str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] |  | ||||||
|     str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] |  | ||||||
|     str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] |  | ||||||
|     str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] |  | ||||||
|     str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] |  | ||||||
|     str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] |  | ||||||
|     str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] |  | ||||||
|     str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] |  | ||||||
|     str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] |  | ||||||
|     str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] |  | ||||||
|     str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] |  | ||||||
|     str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] |  | ||||||
|     str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"] |  | ||||||
|     str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"] |  | ||||||
|     str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"] |  | ||||||
|     str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] |  | ||||||
|     str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] |  | ||||||
|     str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] |  | ||||||
|     str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] |  | ||||||
|     str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] |  | ||||||
|     str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] |  | ||||||
|     str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] |  | ||||||
|     str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"] |  | ||||||
|     str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] |  | ||||||
|     str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] |  | ||||||
|     str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] |  | ||||||
|     str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"] |  | ||||||
|     str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] |  | ||||||
|     str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] |  | ||||||
|     str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] |  | ||||||
|     str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] |  | ||||||
|     str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] |  | ||||||
|  |  | ||||||
|     uword[] @shared wordlists = [ |  | ||||||
|         words81, words82, words83, words84, words85, words86, words87, words88, |  | ||||||
|         words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, |  | ||||||
|         words91, words92, words93, words94, words95, words96, words97, words98, |  | ||||||
|         words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, |  | ||||||
|         wordsA1, wordsA2, wordsA3, wordsA4] |  | ||||||
|  |  | ||||||
|     str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" |  | ||||||
|  |  | ||||||
|     ubyte[4] goatsoup_rnd = [0, 0, 0, 0] |  | ||||||
|     ubyte[4] goatsoup_seed = [0, 0, 0, 0] |  | ||||||
|  |  | ||||||
|     str name = "        "       ; 8 max |  | ||||||
|     ubyte number                ; starts at 0 in new galaxy, then increases by 1 for each generated planet |  | ||||||
|     ubyte x |  | ||||||
|     ubyte y |  | ||||||
|     ubyte economy |  | ||||||
|     ubyte govtype |  | ||||||
|     ubyte techlevel |  | ||||||
|     ubyte population |  | ||||||
|     uword productivity |  | ||||||
|     uword radius |  | ||||||
|     bool species_is_alien      ; otherwise "Human Colonials" |  | ||||||
|     ubyte species_size |  | ||||||
|     ubyte species_color |  | ||||||
|     ubyte species_look |  | ||||||
|     ubyte species_kind |  | ||||||
|  |  | ||||||
|     sub set_seed(uword s1, uword s2) { |  | ||||||
|         goatsoup_seed[0] = lsb(s1) |  | ||||||
|         goatsoup_seed[1] = msb(s1) |  | ||||||
|         goatsoup_seed[2] = lsb(s2) |  | ||||||
|         goatsoup_seed[3] = msb(s2) |  | ||||||
|         reset_rnd() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub reset_rnd() { |  | ||||||
|         goatsoup_rnd[0] = goatsoup_seed[0] |  | ||||||
|         goatsoup_rnd[1] = goatsoup_seed[1] |  | ||||||
|         goatsoup_rnd[2] = goatsoup_seed[2] |  | ||||||
|         goatsoup_rnd[3] = goatsoup_seed[3] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub random_name() -> str { |  | ||||||
|         ubyte ii |  | ||||||
|         str randname = "        "       ; 8 chars max |  | ||||||
|         ubyte nx = 0 |  | ||||||
|         for ii in 0 to goatsoup_rnd_number() & 3 { |  | ||||||
|             ubyte xx = goatsoup_rnd_number() & $3e |  | ||||||
|             if pairs0[xx] != '.' { |  | ||||||
|                 randname[nx] = pairs0[xx] |  | ||||||
|                 nx++ |  | ||||||
|             } |  | ||||||
|             xx++ |  | ||||||
|             if pairs0[xx] != '.' { |  | ||||||
|                 randname[nx] = pairs0[xx] |  | ||||||
|                 nx++ |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         randname[nx] = 0 |  | ||||||
|         randname[0] = strings.upperchar(randname[0]) |  | ||||||
|         return randname |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub goatsoup_rnd_number() -> ubyte { |  | ||||||
|         ubyte xx = goatsoup_rnd[0] * 2 |  | ||||||
|         uword a = xx as uword + goatsoup_rnd[2] |  | ||||||
|         if goatsoup_rnd[0] > 127 |  | ||||||
|             a ++ |  | ||||||
|         goatsoup_rnd[0] = lsb(a) |  | ||||||
|         goatsoup_rnd[2] = xx |  | ||||||
|         xx = goatsoup_rnd[1] |  | ||||||
|         ubyte ac = xx + goatsoup_rnd[3] + msb(a) |  | ||||||
|         goatsoup_rnd[1] = ac |  | ||||||
|         goatsoup_rnd[3] = xx |  | ||||||
|         return ac |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub distance(ubyte px, ubyte py) -> ubyte { |  | ||||||
|         uword ax |  | ||||||
|         uword ay |  | ||||||
|         if px>x |  | ||||||
|             ax=px-x |  | ||||||
|         else |  | ||||||
|             ax=x-px |  | ||||||
|         if py>y |  | ||||||
|             ay=py-y |  | ||||||
|         else |  | ||||||
|             ay=y-py |  | ||||||
|         ay /= 2 |  | ||||||
|         ubyte d = sqrt(ax*ax + ay*ay) |  | ||||||
|         if d>63 |  | ||||||
|             return 255 |  | ||||||
|         return d*4 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub soup() -> str { |  | ||||||
|         str planet_result = " " * 160 |  | ||||||
|         uword[6] source_stack |  | ||||||
|         ubyte stack_ptr = 0 |  | ||||||
|         str start_source = "\x8F is \x97." |  | ||||||
|         uword source_ptr = &start_source |  | ||||||
|         uword result_ptr = &planet_result |  | ||||||
|  |  | ||||||
|         reset_rnd() |  | ||||||
|         recursive_soup() |  | ||||||
|         return planet_result |  | ||||||
|  |  | ||||||
|         sub recursive_soup() { |  | ||||||
|             repeat { |  | ||||||
|                 ubyte c = @(source_ptr) |  | ||||||
|                 source_ptr++ |  | ||||||
|                 if c == $00 { |  | ||||||
|                     @(result_ptr) = 0 |  | ||||||
|                     return |  | ||||||
|                 } |  | ||||||
|                 else if c <= $80 { |  | ||||||
|                     @(result_ptr) = c |  | ||||||
|                     result_ptr++ |  | ||||||
|                 } |  | ||||||
|                 else { |  | ||||||
|                     if c <= $a4 { |  | ||||||
|                         ubyte rnr = goatsoup_rnd_number() |  | ||||||
|                         ubyte wordNr = ((rnr >= $33) as ubyte) + ((rnr >= $66) as ubyte) + ((rnr >= $99) as ubyte) + ((rnr >= $CC) as ubyte) |  | ||||||
|                         source_stack[stack_ptr] = source_ptr |  | ||||||
|                         stack_ptr++ |  | ||||||
|                         source_ptr = getword(c, wordNr) |  | ||||||
|                         recursive_soup()    ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case |  | ||||||
|                         stack_ptr-- |  | ||||||
|                         source_ptr = source_stack[stack_ptr] |  | ||||||
|                     } else { |  | ||||||
|                         if c == $b0 { |  | ||||||
|                             @(result_ptr) = strings.upperchar(name[0]) |  | ||||||
|                             result_ptr++ |  | ||||||
|                             concat_string(&name + 1) |  | ||||||
|                         } |  | ||||||
|                         else if c == $b1 { |  | ||||||
|                             @(result_ptr) = strings.upperchar(name[0]) |  | ||||||
|                             result_ptr++ |  | ||||||
|                             ubyte ni |  | ||||||
|                             for ni in 1 to len(name) { |  | ||||||
|                                 ubyte cc = name[ni] |  | ||||||
|                                 if cc in ['e', 'o', 0] |  | ||||||
|                                     break |  | ||||||
|                                 else { |  | ||||||
|                                     @(result_ptr) = cc |  | ||||||
|                                     result_ptr++ |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                             @(result_ptr) = 'i' |  | ||||||
|                             result_ptr++ |  | ||||||
|                             @(result_ptr) = 'a' |  | ||||||
|                             result_ptr++ |  | ||||||
|                             @(result_ptr) = 'n' |  | ||||||
|                             result_ptr++ |  | ||||||
|                         } |  | ||||||
|                         else if c == $b2 { |  | ||||||
|                             concat_string(random_name()) |  | ||||||
|                         } |  | ||||||
|                         else { |  | ||||||
|                             @(result_ptr) = c |  | ||||||
|                             result_ptr++ |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         sub concat_string(uword str_ptr) { |  | ||||||
|             repeat { |  | ||||||
|                 ubyte c = @(str_ptr) |  | ||||||
|                 if c==0 |  | ||||||
|                     break |  | ||||||
|                 else { |  | ||||||
|                     @(result_ptr) = c |  | ||||||
|                     str_ptr++ |  | ||||||
|                     result_ptr++ |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub display(bool compressed, ubyte distance) { |  | ||||||
|         txt.print(soup()) |  | ||||||
|         txt.nl() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     sub getword(ubyte listnum, ubyte wordidx) -> uword { |  | ||||||
|         uword list = wordlists[listnum-$81] |  | ||||||
|         return peekw(list + wordidx*2) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| elite_util { |  | ||||||
|     sub prefix_matches(uword prefixptr, uword stringptr) -> bool { |  | ||||||
|         repeat { |  | ||||||
|             ubyte pc = @(prefixptr) |  | ||||||
|             ubyte sc = @(stringptr) |  | ||||||
|             if pc == 0 |  | ||||||
|                 return true |  | ||||||
|             ; to lowercase for case insensitive compare: |  | ||||||
|             if strings.lowerchar(pc)!=strings.lowerchar(sc) |  | ||||||
|                 return false |  | ||||||
|             prefixptr++ |  | ||||||
|             stringptr++ |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,118 +0,0 @@ | |||||||
|  |  | ||||||
| ;  This benchmark program is meant to check for regressions in the |  | ||||||
| ;  Prog8 compiler's code-generator (performance wise). |  | ||||||
| ; |  | ||||||
| ;  As the X16 computer is a more or less fixed system, it's not very useful |  | ||||||
| ;  to benchmark the computer itself with. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| %import textio |  | ||||||
| %import b_adpcm |  | ||||||
| %import b_circles |  | ||||||
| %import b_3d |  | ||||||
| %import b_life |  | ||||||
| %import b_mandelbrot |  | ||||||
| %import b_queens |  | ||||||
| %import b_textelite |  | ||||||
| %import b_maze |  | ||||||
| %import b_sprites |  | ||||||
| %import b_btree |  | ||||||
|  |  | ||||||
| %zeropage basicsafe |  | ||||||
| %option no_sysinit |  | ||||||
|  |  | ||||||
|  |  | ||||||
| main { |  | ||||||
|  |  | ||||||
|     str[20] benchmark_names |  | ||||||
|     uword[20] benchmark_score |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     sub start() { |  | ||||||
|         ubyte benchmark_number |  | ||||||
|  |  | ||||||
|         cx16.set_screen_mode(3) |  | ||||||
|         txt.color2(1, 6) |  | ||||||
|         txt.clear_screen() |  | ||||||
|  |  | ||||||
|         txt.print("\n\n\n  prog8 compiler benchmark tests.\n\n  benchmark version v12\n\n") |  | ||||||
|         sys.wait(60) |  | ||||||
|  |  | ||||||
|         benchmark_number = 0 |  | ||||||
|  |  | ||||||
|         announce_benchmark("maze solver") |  | ||||||
|         benchmark_score[benchmark_number]  = maze.bench(300) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("n-queens") |  | ||||||
|         benchmark_score[benchmark_number]  = queens.bench(300) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("mandelbrot (floating point)") |  | ||||||
|         benchmark_score[benchmark_number]  = mandelbrot.calc(400) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("game of life") |  | ||||||
|         benchmark_score[benchmark_number]  = life.benchmark(300) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("3d model rotation") |  | ||||||
|         benchmark_score[benchmark_number]  = rotate3d.benchmark(300) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("adpcm audio decoding") |  | ||||||
|         benchmark_score[benchmark_number]  = adpcm.decode_benchmark(300) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("circles with gfx_lores") |  | ||||||
|         benchmark_score[benchmark_number]  = circles.draw(false, 400) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("text-elite") |  | ||||||
|         benchmark_score[benchmark_number]  = textelite.bench(120) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("sprites-coroutines-defer") |  | ||||||
|         benchmark_score[benchmark_number]  = animsprites.benchmark(300) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         announce_benchmark("btree-struct-pointers") |  | ||||||
|         benchmark_score[benchmark_number]  = btree.benchmark(200) |  | ||||||
|         benchmark_number++ |  | ||||||
|  |  | ||||||
|         benchmark_names[benchmark_number] = 0 |  | ||||||
|         benchmark_score[benchmark_number] = 0 |  | ||||||
|  |  | ||||||
|         cx16.set_screen_mode(3) |  | ||||||
|         txt.uppercase() |  | ||||||
|         txt.color2(1, 6) |  | ||||||
|         uword total_score |  | ||||||
|         benchmark_number = 0 |  | ||||||
|         txt.print("\nscore benchmark (v12)\n\n") |  | ||||||
|         do { |  | ||||||
|             txt.spc() |  | ||||||
|             txt.print_uw(benchmark_score[benchmark_number]) |  | ||||||
|             txt.column(6) |  | ||||||
|             txt.print(benchmark_names[benchmark_number]) |  | ||||||
|             total_score += benchmark_score[benchmark_number] |  | ||||||
|             txt.nl() |  | ||||||
|             benchmark_number++ |  | ||||||
|         } until benchmark_names[benchmark_number]==0 |  | ||||||
|  |  | ||||||
|         txt.print("\n\ntotal score : ") |  | ||||||
|         txt.print_uw(total_score) |  | ||||||
|         txt.print(" (higher=better)\n") |  | ||||||
|  |  | ||||||
|         sub announce_benchmark(str name) { |  | ||||||
|             benchmark_names[benchmark_number] = name |  | ||||||
|             cx16.set_screen_mode(3) |  | ||||||
|             txt.uppercase() |  | ||||||
|             txt.color2(1, 6) |  | ||||||
|             txt.clear_screen() |  | ||||||
|             txt.plot(4, 6) |  | ||||||
|             txt.print(benchmark_names[benchmark_number]) |  | ||||||
|             txt.nl() |  | ||||||
|             sys.wait(60) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										10
									
								
								build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | plugins { | ||||||
|  |     id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | allprojects { | ||||||
|  |     repositories { | ||||||
|  |         mavenLocal() | ||||||
|  |         mavenCentral() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| import org.jetbrains.kotlin.gradle.dsl.JvmTarget |  | ||||||
| import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode |  | ||||||
| import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature |  | ||||||
| import org.jetbrains.kotlin.gradle.dsl.KotlinVersion |  | ||||||
|  |  | ||||||
|  |  | ||||||
| plugins { |  | ||||||
|     kotlin("jvm") version "2.2.20" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| allprojects { |  | ||||||
|     apply(plugin="kotlin") |  | ||||||
|  |  | ||||||
|     repositories { |  | ||||||
|         mavenLocal() |  | ||||||
|         mavenCentral() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     kotlin { |  | ||||||
|         compilerOptions { |  | ||||||
|             freeCompilerArgs = listOf("-Xwhen-guards") |  | ||||||
|             jvmTarget = JvmTarget.JVM_11 |  | ||||||
|             jvmDefault = JvmDefaultMode.NO_COMPATIBILITY |  | ||||||
|             // languageVersion.set(KotlinVersion.KOTLIN_2_3) |  | ||||||
|         } |  | ||||||
|         sourceSets.all { |  | ||||||
|             languageSettings { |  | ||||||
|                 // enable language features like so: |  | ||||||
|                 // enableLanguageFeature(LanguageFeature.WhenGuards.name) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     java { |  | ||||||
|         targetCompatibility = JavaVersion.VERSION_11 |  | ||||||
|         sourceCompatibility = JavaVersion.VERSION_11 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										43
									
								
								codeCore/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								codeCore/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  |  | ||||||
|  | plugins { | ||||||
|  |     id 'java' | ||||||
|  |     id 'application' | ||||||
|  |     id "org.jetbrains.kotlin.jvm" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | java { | ||||||
|  |     toolchain { | ||||||
|  |         languageVersion = JavaLanguageVersion.of(javaVersion) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | compileKotlin { | ||||||
|  |     kotlinOptions { | ||||||
|  |         jvmTarget = javaVersion | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | compileTestKotlin { | ||||||
|  |     kotlinOptions { | ||||||
|  |         jvmTarget = javaVersion | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | dependencies { | ||||||
|  |     // should have no dependencies to other modules | ||||||
|  |     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" | ||||||
|  |     implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | sourceSets { | ||||||
|  |     main { | ||||||
|  |         java { | ||||||
|  |             srcDirs = ["${project.projectDir}/src"] | ||||||
|  |         } | ||||||
|  |         resources { | ||||||
|  |             srcDirs = ["${project.projectDir}/res"] | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // note: there are no unit tests in this module! | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| plugins { |  | ||||||
|     kotlin("jvm") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| dependencies { |  | ||||||
|     // should have no dependencies to other modules |  | ||||||
|     // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") |  | ||||||
|     implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sourceSets { |  | ||||||
|     main { |  | ||||||
|         java { |  | ||||||
|             srcDir("${project.projectDir}/src") |  | ||||||
|         } |  | ||||||
|         resources { |  | ||||||
|             srcDir("${project.projectDir}/res") |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // note: there are no unit tests in this module! |  | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package prog8 | package prog8.code | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * By convention, the right side of an `Either` is used to hold successful values. |  * By convention, the right side of an `Either` is used to hold successful values. | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| package prog8.code |  | ||||||
|  |  | ||||||
| import java.io.IOException |  | ||||||
| import java.nio.file.Path |  | ||||||
| import kotlin.io.path.absolute |  | ||||||
|  |  | ||||||
|  |  | ||||||
| const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings" |  | ||||||
|  |  | ||||||
| val PROG8_CONTAINER_MODULES = arrayOf(INTERNED_STRINGS_MODULENAME)      // option to add more if needed one day |  | ||||||
|  |  | ||||||
| // all automatically generated labels everywhere need to have the same label name prefix: |  | ||||||
| const val GENERATED_LABEL_PREFIX = "p8_label_gen_" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Returns the absolute path of the given path, |  | ||||||
|  * where links are replaced by the actual directories, |  | ||||||
|  * and containing no redundant path elements. |  | ||||||
|  * If the path doesn't refer to an existing directory or file on the file system, |  | ||||||
|  * it is returned unchanged. |  | ||||||
|  */ |  | ||||||
| fun Path.sanitize(): Path { |  | ||||||
|     return try { |  | ||||||
|         this.toRealPath().normalize() |  | ||||||
|     } catch (_: java.nio.file.NoSuchFileException) { |  | ||||||
|         this.absolute().normalize() |  | ||||||
|         //throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) } |  | ||||||
|     } catch (iox: IOException) { |  | ||||||
|         throw FileSystemException(this.toFile()).also { it.initCause(iox) } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										251
									
								
								codeCore/src/prog8/code/SymbolTable.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								codeCore/src/prog8/code/SymbolTable.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | |||||||
|  | package prog8.code | ||||||
|  |  | ||||||
|  | import prog8.code.ast.PtNode | ||||||
|  | import prog8.code.ast.PtProgram | ||||||
|  | import prog8.code.core.* | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Tree structure containing all symbol definitions in the program | ||||||
|  |  * (blocks, subroutines, variables (all types), memoryslabs, and labels). | ||||||
|  |  */ | ||||||
|  | class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) { | ||||||
|  |     /** | ||||||
|  |      * The table as a flat mapping of scoped names to the StNode. | ||||||
|  |      * This gives the fastest lookup possible (no need to traverse tree nodes) | ||||||
|  |      */ | ||||||
|  |  | ||||||
|  |     private var cachedFlat: Map<String, StNode>? = null | ||||||
|  |  | ||||||
|  |     val flat: Map<String, StNode> get()  { | ||||||
|  |         if(cachedFlat!=null) | ||||||
|  |             return cachedFlat!! | ||||||
|  |  | ||||||
|  |         val result = mutableMapOf<String, StNode>() | ||||||
|  |         fun collect(node: StNode) { | ||||||
|  |             for(child in node.children) { | ||||||
|  |                 result[child.value.scopedName] = child.value | ||||||
|  |                 collect(child.value) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         collect(this) | ||||||
|  |         cachedFlat = result | ||||||
|  |         return result | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun resetCachedFlat() { | ||||||
|  |         cachedFlat = null | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val allVariables: Collection<StStaticVariable> by lazy { | ||||||
|  |         val vars = mutableListOf<StStaticVariable>() | ||||||
|  |         fun collect(node: StNode) { | ||||||
|  |             for(child in node.children) { | ||||||
|  |                 if(child.value.type== StNodeType.STATICVAR) | ||||||
|  |                     vars.add(child.value as StStaticVariable) | ||||||
|  |                 else | ||||||
|  |                     collect(child.value) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         collect(this) | ||||||
|  |         vars | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val allMemMappedVariables: Collection<StMemVar> by lazy { | ||||||
|  |         val vars = mutableListOf<StMemVar>() | ||||||
|  |         fun collect(node: StNode) { | ||||||
|  |             for(child in node.children) { | ||||||
|  |                 if(child.value.type== StNodeType.MEMVAR) | ||||||
|  |                     vars.add(child.value as StMemVar) | ||||||
|  |                 else | ||||||
|  |                     collect(child.value) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         collect(this) | ||||||
|  |         vars | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val allMemorySlabs: Collection<StMemorySlab> by lazy { | ||||||
|  |         val vars = mutableListOf<StMemorySlab>() | ||||||
|  |         fun collect(node: StNode) { | ||||||
|  |             for(child in node.children) { | ||||||
|  |                 if(child.value.type== StNodeType.MEMORYSLAB) | ||||||
|  |                     vars.add(child.value as StMemorySlab) | ||||||
|  |                 else | ||||||
|  |                     collect(child.value) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         collect(this) | ||||||
|  |         vars | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun lookup(scopedName: String) = flat[scopedName] | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | enum class StNodeType { | ||||||
|  |     GLOBAL, | ||||||
|  |     // MODULE,     // not used with current scoping rules | ||||||
|  |     BLOCK, | ||||||
|  |     SUBROUTINE, | ||||||
|  |     ROMSUB, | ||||||
|  |     LABEL, | ||||||
|  |     STATICVAR, | ||||||
|  |     MEMVAR, | ||||||
|  |     CONSTANT, | ||||||
|  |     BUILTINFUNC, | ||||||
|  |     MEMORYSLAB | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | open class StNode(val name: String, | ||||||
|  |                   val type: StNodeType, | ||||||
|  |                   val astNode: PtNode, | ||||||
|  |                   val children: MutableMap<String, StNode> = mutableMapOf() | ||||||
|  | ) { | ||||||
|  |  | ||||||
|  |     lateinit var parent: StNode | ||||||
|  |  | ||||||
|  |     val scopedName: String by lazy { scopedNameList.joinToString(".") } | ||||||
|  |  | ||||||
|  |     open fun lookup(scopedName: String) = | ||||||
|  |         lookup(scopedName.split('.')) | ||||||
|  |  | ||||||
|  |     fun lookupUnscopedOrElse(name: String, default: () -> StNode) = | ||||||
|  |         lookupUnscoped(name) ?: default() | ||||||
|  |  | ||||||
|  |     fun lookupOrElse(scopedName: String, default: () -> StNode): StNode = | ||||||
|  |         lookup(scopedName.split('.')) ?: default() | ||||||
|  |  | ||||||
|  |     fun lookupUnscoped(name: String): StNode? { | ||||||
|  |         // first consider the builtin functions | ||||||
|  |         var globalscope = this | ||||||
|  |         while(globalscope.type!= StNodeType.GLOBAL) | ||||||
|  |             globalscope = globalscope.parent | ||||||
|  |         val globalNode = globalscope.children[name] | ||||||
|  |         if(globalNode!=null && globalNode.type== StNodeType.BUILTINFUNC) | ||||||
|  |             return globalNode | ||||||
|  |  | ||||||
|  |         // search for the unqualified name in the current scope or its parent scopes | ||||||
|  |         var scope=this | ||||||
|  |         while(true) { | ||||||
|  |             val node = scope.children[name] | ||||||
|  |             if(node!=null) | ||||||
|  |                 return node | ||||||
|  |             if(scope.type== StNodeType.GLOBAL) | ||||||
|  |                 return null | ||||||
|  |             else | ||||||
|  |                 scope = scope.parent | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun add(child: StNode) { | ||||||
|  |         children[child.name] = child | ||||||
|  |         child.parent = this | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private val scopedNameList: List<String> by lazy { | ||||||
|  |         if(type==StNodeType.GLOBAL) | ||||||
|  |             emptyList() | ||||||
|  |         else | ||||||
|  |             parent.scopedNameList + name | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun lookup(scopedName: List<String>): StNode? { | ||||||
|  |         // a scoped name refers to a name in another namespace, and always stars from the root. | ||||||
|  |         var node = this | ||||||
|  |         while(node.type!=StNodeType.GLOBAL) | ||||||
|  |             node = node.parent | ||||||
|  |  | ||||||
|  |         for(name in scopedName) { | ||||||
|  |             if(name in node.children) | ||||||
|  |                 node = node.children.getValue(name) | ||||||
|  |             else | ||||||
|  |                 return null | ||||||
|  |         } | ||||||
|  |         return node | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class StStaticVariable(name: String, | ||||||
|  |                        val dt: DataType, | ||||||
|  |                        val onetimeInitializationNumericValue: Double?,      // regular (every-run-time) initialization is done via regular assignments | ||||||
|  |                        val onetimeInitializationStringValue: StString?, | ||||||
|  |                        val onetimeInitializationArrayValue: StArray?, | ||||||
|  |                        val length: Int?,            // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte | ||||||
|  |                        val zpwish: ZeropageWish,    // used in the variable allocator | ||||||
|  |                        astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) { | ||||||
|  |  | ||||||
|  |     val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         if(length!=null) { | ||||||
|  |             require(onetimeInitializationNumericValue == null) | ||||||
|  |             if(onetimeInitializationArrayValue!=null) | ||||||
|  |                 require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length) | ||||||
|  |         } | ||||||
|  |         if(onetimeInitializationNumericValue!=null) { | ||||||
|  |             require(dt in NumericDatatypes) | ||||||
|  |             require(onetimeInitializationNumericValue!=0.0) { "zero as init value should just remain uninitialized"} | ||||||
|  |         } | ||||||
|  |         if(onetimeInitializationArrayValue!=null) { | ||||||
|  |             require(dt in ArrayDatatypes) | ||||||
|  |             if(onetimeInitializationArrayValue.all { it.number!=null} ) { | ||||||
|  |                 require(onetimeInitializationArrayValue.any { it.number != 0.0 }) { "array of all zerors as init value should just remain uninitialized" } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(onetimeInitializationStringValue!=null) { | ||||||
|  |             require(dt == DataType.STR) | ||||||
|  |             require(length == onetimeInitializationStringValue.first.length+1) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) : | ||||||
|  |     StNode(name, StNodeType.CONSTANT, astNode) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StMemVar(name: String, | ||||||
|  |                val dt: DataType, | ||||||
|  |                val address: UInt, | ||||||
|  |                val length: Int?,             // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte | ||||||
|  |                astNode: PtNode) : | ||||||
|  |     StNode(name, StNodeType.MEMVAR, astNode) { | ||||||
|  |  | ||||||
|  |     init{ | ||||||
|  |         if(dt in ArrayDatatypes || dt == DataType.STR) | ||||||
|  |             require(length!=null) { "memory mapped array or string must have known length" } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class StMemorySlab( | ||||||
|  |     name: String, | ||||||
|  |     val size: UInt, | ||||||
|  |     val align: UInt, | ||||||
|  |     astNode: PtNode | ||||||
|  | ): | ||||||
|  |     StNode(name, StNodeType.MEMORYSLAB, astNode) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) : | ||||||
|  |         StNode(name, StNodeType.SUBROUTINE, astNode) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StRomSub(name: String, | ||||||
|  |                val address: UInt?,      // null in case of asmsub, specified in case of romsub | ||||||
|  |                val parameters: List<StRomSubParameter>, | ||||||
|  |                val returns: List<StRomSubParameter>, | ||||||
|  |                astNode: PtNode) : | ||||||
|  |     StNode(name, StNodeType.ROMSUB, astNode) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StSubroutineParameter(val name: String, val type: DataType) | ||||||
|  | class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType) | ||||||
|  | class StArrayElement(val number: Double?, val addressOfSymbol: String?) | ||||||
|  |  | ||||||
|  | typealias StString = Pair<String, Encoding> | ||||||
|  | typealias StArray = List<StArrayElement> | ||||||
| @@ -3,19 +3,18 @@ package prog8.code | |||||||
| import prog8.code.ast.* | import prog8.code.ast.* | ||||||
| import prog8.code.core.* | import prog8.code.core.* | ||||||
| import prog8.code.target.VMTarget | import prog8.code.target.VMTarget | ||||||
| 
 | import java.util.* | ||||||
| 
 | 
 | ||||||
| class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) { | class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) { | ||||||
|     fun make(): SymbolTable { |     fun make(): SymbolTable { | ||||||
|         val st = SymbolTable(program) |         val st = SymbolTable(program) | ||||||
| 
 | 
 | ||||||
|         BuiltinFunctions.forEach { |         BuiltinFunctions.forEach { | ||||||
|             val dt = DataType.forDt(it.value.returnType ?: BaseDataType.UNDEFINED) |             st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY))) | ||||||
|             st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, dt, Position.DUMMY))) |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         val scopestack = ArrayDeque<StNode>() |         val scopestack = Stack<StNode>() | ||||||
|         scopestack.add(st) |         scopestack.push(st) | ||||||
|         program.children.forEach { |         program.children.forEach { | ||||||
|             addToSt(it, scopestack) |             addToSt(it, scopestack) | ||||||
|         } |         } | ||||||
| @@ -23,93 +22,79 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp | |||||||
| 
 | 
 | ||||||
|         if(options.compTarget.name != VMTarget.NAME) { |         if(options.compTarget.name != VMTarget.NAME) { | ||||||
|             listOf( |             listOf( | ||||||
|                 PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.zeropage.SCRATCH_B1, null, Position.DUMMY), |                 PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY), | ||||||
|                 PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.zeropage.SCRATCH_REG, null, Position.DUMMY), |                 PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY), | ||||||
|                 PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.zeropage.SCRATCH_W1, null, Position.DUMMY), |                 PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY), | ||||||
|                 PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.zeropage.SCRATCH_W2, null, Position.DUMMY), |                 PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY), | ||||||
|  |                 PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY), | ||||||
|  |                 PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY) | ||||||
|             ).forEach { |             ).forEach { | ||||||
|                 it.parent = program |                 it.parent = program | ||||||
|                 st.add(StMemVar(it.name, it.type, it.address, it.arraySize, it)) |                 st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it)) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return st |         return st | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun addToSt(node: PtNode, scope: ArrayDeque<StNode>) { |     private fun addToSt(node: PtNode, scope: Stack<StNode>) { | ||||||
|         val stNode = when(node) { |         val stNode = when(node) { | ||||||
|             is PtAsmSub -> { |             is PtAsmSub -> { | ||||||
|                 val parameters = node.parameters.map { StExtSubParameter(it.first, it.second.type) } |                 val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) } | ||||||
|                 val returns = node.returns.map { StExtSubParameter(it.first, it.second) } |                 val returns = node.returns.map { StRomSubParameter(it.first, it.second) } | ||||||
|                 StExtSub(node.name, node.address, parameters, returns, node) |                 StRomSub(node.name, node.address, parameters, returns, node) | ||||||
|             } |             } | ||||||
|             is PtBlock -> { |             is PtBlock -> { | ||||||
|                 StNode(node.name, StNodeType.BLOCK, node) |                 StNode(node.name, StNodeType.BLOCK, node) | ||||||
|             } |             } | ||||||
|             is PtConstant -> { |             is PtConstant -> { | ||||||
|                 require(node.type.isNumericOrBool) |                 StConstant(node.name, node.type, node.value, node) | ||||||
|                 StConstant(node.name, node.type.base, node.value, node) |  | ||||||
|             } |             } | ||||||
|             is PtLabel -> { |             is PtLabel -> { | ||||||
|                 StNode(node.name, StNodeType.LABEL, node) |                 StNode(node.name, StNodeType.LABEL, node) | ||||||
|             } |             } | ||||||
|             is PtMemMapped -> { |             is PtMemMapped -> { | ||||||
|                 StMemVar(node.name, node.type, node.address, node.arraySize, node) |                 StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node) | ||||||
|             } |             } | ||||||
|             is PtSub -> { |             is PtSub -> { | ||||||
|                 val params = node.signature.children.map { |                 val params = node.parameters.map {StSubroutineParameter(it.name, it.type) } | ||||||
|                     it as PtSubroutineParameter |                 StSub(node.name, params, node.returntype, node) | ||||||
|                     StSubroutineParameter(it.name, it.type, it.register) |  | ||||||
|                 } |  | ||||||
|                 StSub(node.name, params, node.signature.returns, node) |  | ||||||
|             } |  | ||||||
|             is PtStructDecl -> { |  | ||||||
|                 val size = node.fields.sumOf { program.memsizer.memorySize(it.first, 1) } |  | ||||||
|                 StStruct(node.name, node.fields, size.toUInt(), node) |  | ||||||
|             } |             } | ||||||
|             is PtVariable -> { |             is PtVariable -> { | ||||||
|                 val initialNumeric: Double? |                 val initialNumeric: Double? | ||||||
|                 val initialString: StString? |                 val initialString: StString? | ||||||
|                 val initialArray: StArray? |                 val initialArray: StArray? | ||||||
|                 val numElements: UInt? |                 val numElements: Int? | ||||||
|                 val value = node.value |                 val value = node.value | ||||||
|                 if(value!=null) { |                 if(value!=null) { | ||||||
|  |                     val number = (value as? PtNumber)?.number | ||||||
|  |                     initialNumeric = if(number==0.0) null else number       // 0 as init value -> just uninitialized | ||||||
|                     when (value) { |                     when (value) { | ||||||
|                         is PtString -> { |                         is PtString -> { | ||||||
|                             initialString = StString(value.value, value.encoding) |                             initialString = StString(value.value, value.encoding) | ||||||
|                             initialArray = null |                             initialArray = null | ||||||
|                             initialNumeric = null |                             numElements = value.value.length + 1   // include the terminating 0-byte | ||||||
|                             numElements = (value.value.length + 1).toUInt()   // include the terminating 0-byte |  | ||||||
|                         } |                         } | ||||||
|                         is PtArray -> { |                         is PtArray -> { | ||||||
|                             initialArray = makeInitialArray(value, scope) |                             val array = makeInitialArray(value) | ||||||
|  |                             initialArray = if(array.all { it.number==0.0 }) null else array            // all 0 as init value -> just uninitialized | ||||||
|                             initialString = null |                             initialString = null | ||||||
|                             initialNumeric = null |                             numElements = array.size | ||||||
|                             numElements = initialArray.size.toUInt() |                             require(node.arraySize?.toInt()==numElements) | ||||||
|                             require(node.arraySize==numElements) |  | ||||||
|                         } |                         } | ||||||
|                         else -> { |                         else -> { | ||||||
|                             require(value is PtNumber) |  | ||||||
|                             initialString = null |                             initialString = null | ||||||
|                             initialArray = null |                             initialArray = null | ||||||
|                             val number = value.number |                             numElements = node.arraySize?.toInt() | ||||||
|                             initialNumeric = number |  | ||||||
|                             numElements = node.arraySize |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     initialNumeric = null |                     initialNumeric = null | ||||||
|                     initialArray = null |                     initialArray = null | ||||||
|                     initialString = null |                     initialString = null | ||||||
|                     numElements = node.arraySize |                     numElements = node.arraySize?.toInt() | ||||||
|                 } |                 } | ||||||
| //                if(node.type in SplitWordArrayTypes) { |                 StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node) | ||||||
| //                    ... split array also add _lsb and _msb to symboltable? |  | ||||||
| //                } |  | ||||||
|                 val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align, node.dirty,node) |  | ||||||
|                 if(initialNumeric!=null) |  | ||||||
|                     stVar.setOnetimeInitNumeric(initialNumeric) |  | ||||||
|                 stVar |  | ||||||
|             } |             } | ||||||
|             is PtBuiltinFunctionCall -> { |             is PtBuiltinFunctionCall -> { | ||||||
|                 if(node.name=="memory") { |                 if(node.name=="memory") { | ||||||
| @@ -119,13 +104,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp | |||||||
|                     val size = (node.args[1] as PtNumber).number.toUInt() |                     val size = (node.args[1] as PtNumber).number.toUInt() | ||||||
|                     val align = (node.args[2] as PtNumber).number.toUInt() |                     val align = (node.args[2] as PtNumber).number.toUInt() | ||||||
|                     // don't add memory slabs in nested scope, just put them in the top level of the ST |                     // don't add memory slabs in nested scope, just put them in the top level of the ST | ||||||
|                     scope.first().add(StMemorySlab("memory_$slabname", size, align, node)) |                     scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node)) | ||||||
|                 } |  | ||||||
|                 else if(node.name=="prog8_lib_structalloc") { |  | ||||||
|                     val instance = handleStructAllocation(node) |  | ||||||
|                     if(instance!=null) { |  | ||||||
|                         scope.first().add(instance)  // don't add struct instances in nested scope, just put them in the top level of the ST |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|                 null |                 null | ||||||
|             } |             } | ||||||
| @@ -133,61 +112,22 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(stNode!=null) { |         if(stNode!=null) { | ||||||
|             scope.last().add(stNode) |             scope.peek().add(stNode) | ||||||
|             scope.add(stNode) |             scope.push(stNode) | ||||||
|         } |         } | ||||||
|         node.children.forEach { |         node.children.forEach { | ||||||
|             addToSt(it, scope) |             addToSt(it, scope) | ||||||
|         } |         } | ||||||
|         if(stNode!=null) |         if(stNode!=null) | ||||||
|             scope.removeLast() |             scope.pop() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun handleStructAllocation(node: PtBuiltinFunctionCall): StStructInstance? { |     private fun makeInitialArray(value: PtArray): List<StArrayElement> { | ||||||
|         val struct = node.type.subType as? StStruct ?: return null |  | ||||||
|         val initialValues = node.args.map { |  | ||||||
|             when(it) { |  | ||||||
|                 is PtAddressOf -> StArrayElement(null, it.identifier!!.name, null, null,null) |  | ||||||
|                 is PtBool -> StArrayElement(null, null, null, null, it.value) |  | ||||||
|                 is PtNumber -> StArrayElement(it.number, null, null, null, null) |  | ||||||
|                 else -> throw AssemblyError("invalid structalloc argument type $it") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         val label =  SymbolTable.labelnameForStructInstance(node) |  | ||||||
|         val scopedStructName = if(struct.astNode!=null) (struct.astNode as PtNamedNode).scopedName else struct.scopedNameString |  | ||||||
|         return StStructInstance(label, scopedStructName, initialValues, struct.size, null) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun makeInitialArray(value: PtArray, scope: ArrayDeque<StNode>): List<StArrayElement> { |  | ||||||
|         return value.children.map { |         return value.children.map { | ||||||
|             when(it) { |             when(it) { | ||||||
|                 is PtAddressOf -> { |                 is PtAddressOf -> StArrayElement(null, it.identifier.name) | ||||||
|                     when { |                 is PtIdentifier -> StArrayElement(null, it.name) | ||||||
|                         it.isFromArrayElement -> TODO("address-of array element $it in initial array value  ${it.position}") |                 is PtNumber -> StArrayElement(it.number, null) | ||||||
|                         else -> StArrayElement(null, it.identifier!!.name, null, null,null) |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 is PtNumber -> StArrayElement(it.number, null, null,null,null) |  | ||||||
|                 is PtBool -> StArrayElement(null, null, null,null,it.value) |  | ||||||
|                 is PtBuiltinFunctionCall -> { |  | ||||||
|                     if(it.name=="prog8_lib_structalloc") { |  | ||||||
|                         val instance = handleStructAllocation(it) |  | ||||||
|                         if(instance==null) { |  | ||||||
|                             val label = SymbolTable.labelnameForStructInstance(it) |  | ||||||
|                             if (it.args.isEmpty()) |  | ||||||
|                                 StArrayElement(null, null, null, label, null) |  | ||||||
|                             else |  | ||||||
|                                 StArrayElement(null, null, label, null, null) |  | ||||||
|                         } else { |  | ||||||
|                             scope.first().add(instance)  // don't add struct instances in nested scope, just put them in the top level of the ST |  | ||||||
|                             if (it.args.isEmpty()) |  | ||||||
|                                 StArrayElement(null, null, null, instance.name, null) |  | ||||||
|                             else |  | ||||||
|                                 StArrayElement(null, null, instance.name, null, null) |  | ||||||
|                         } |  | ||||||
|                     } else |  | ||||||
|                         TODO("support for initial array element via ${it.name}  ${it.position}") |  | ||||||
|                 } |  | ||||||
|                 else -> throw AssemblyError("invalid array element $it") |                 else -> throw AssemblyError("invalid array element $it") | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -230,7 +170,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp | |||||||
| //                } | //                } | ||||||
| //                VarDeclType.MEMORY -> { | //                VarDeclType.MEMORY -> { | ||||||
| //                    val numElements = | //                    val numElements = | ||||||
| //                        if(decl.isArray) | //                        if(decl.datatype in ArrayDatatypes) | ||||||
| //                            decl.arraysize!!.constIndex() | //                            decl.arraysize!!.constIndex() | ||||||
| //                        else null | //                        else null | ||||||
| //                    val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position) | //                    val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position) | ||||||
| @@ -3,7 +3,7 @@ package prog8.code.ast | |||||||
| import prog8.code.core.IMemSizer | import prog8.code.core.IMemSizer | ||||||
| import prog8.code.core.IStringEncoding | import prog8.code.core.IStringEncoding | ||||||
| import prog8.code.core.Position | import prog8.code.core.Position | ||||||
| import prog8.code.source.SourceCode | import prog8.code.core.SourceCode | ||||||
| import java.nio.file.Path | import java.nio.file.Path | ||||||
| 
 | 
 | ||||||
| // New simplified AST for the code generator. | // New simplified AST for the code generator. | ||||||
| @@ -14,10 +14,6 @@ sealed class PtNode(val position: Position) { | |||||||
|     val children = mutableListOf<PtNode>() |     val children = mutableListOf<PtNode>() | ||||||
|     lateinit var parent: PtNode |     lateinit var parent: PtNode | ||||||
| 
 | 
 | ||||||
|     fun parentHasBeenSet() = ::parent.isInitialized |  | ||||||
| 
 |  | ||||||
|     override fun toString(): String = "${super.toString()} at $position" |  | ||||||
|      |  | ||||||
|     fun add(child: PtNode) { |     fun add(child: PtNode) { | ||||||
|         children.add(child) |         children.add(child) | ||||||
|         child.parent = this |         child.parent = this | ||||||
| @@ -41,10 +37,9 @@ class PtNodeGroup : PtNode(Position.DUMMY) | |||||||
| sealed class PtNamedNode(var name: String, position: Position): PtNode(position) { | sealed class PtNamedNode(var name: String, position: Position): PtNode(position) { | ||||||
|     // Note that as an exception, the 'name' is not read-only |     // Note that as an exception, the 'name' is not read-only | ||||||
|     // but a var. This is to allow for cheap node renames. |     // but a var. This is to allow for cheap node renames. | ||||||
|     val scopedName: String |     val scopedName: String by lazy { | ||||||
|         get() { |  | ||||||
|         var namedParent: PtNode = this.parent |         var namedParent: PtNode = this.parent | ||||||
|             return if(namedParent is PtProgram) |         if(namedParent is PtProgram) | ||||||
|             name |             name | ||||||
|         else { |         else { | ||||||
|             while (namedParent !is PtNamedNode) |             while (namedParent !is PtNamedNode) | ||||||
| @@ -68,24 +63,23 @@ class PtProgram( | |||||||
|         children.asSequence().filterIsInstance<PtBlock>() |         children.asSequence().filterIsInstance<PtBlock>() | ||||||
| 
 | 
 | ||||||
|     fun entrypoint(): PtSub? = |     fun entrypoint(): PtSub? = | ||||||
|         // returns the main.start subroutine if it exists |         allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub? | ||||||
|         allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" } |  | ||||||
|             ?.children |  | ||||||
|             ?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub? |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PtBlock(name: String, | class PtBlock(name: String, | ||||||
|  |               val address: UInt?, | ||||||
|               val library: Boolean, |               val library: Boolean, | ||||||
|  |               val forceOutput: Boolean, | ||||||
|  |               val alignment: BlockAlignment, | ||||||
|               val source: SourceCode,       // taken from the module the block is defined in. |               val source: SourceCode,       // taken from the module the block is defined in. | ||||||
|               val options: Options, |  | ||||||
|               position: Position |               position: Position | ||||||
| ) : PtNamedNode(name, position) { | ) : PtNamedNode(name, position) { | ||||||
|     class Options(val address: UInt? = null, |     enum class BlockAlignment { | ||||||
|                   val forceOutput: Boolean = false, |         NONE, | ||||||
|                   val noSymbolPrefixing: Boolean = false, |         WORD, | ||||||
|                   val veraFxMuls: Boolean = false, |         PAGE | ||||||
|                   val ignoreUnused: Boolean = false) |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -103,9 +97,6 @@ class PtLabel(name: String, position: Position) : PtNamedNode(name, position) | |||||||
| class PtBreakpoint(position: Position): PtNode(position) | class PtBreakpoint(position: Position): PtNode(position) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PtAlign(val align: UInt, position: Position): PtNode(position) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) | class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
							
								
								
									
										301
									
								
								codeCore/src/prog8/code/ast/AstExpressions.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								codeCore/src/prog8/code/ast/AstExpressions.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,301 @@ | |||||||
|  | package prog8.code.ast | ||||||
|  |  | ||||||
|  | import prog8.code.core.DataType | ||||||
|  | import prog8.code.core.Encoding | ||||||
|  | import prog8.code.core.NumericDatatypes | ||||||
|  | import prog8.code.core.Position | ||||||
|  | import java.util.* | ||||||
|  | import kotlin.math.abs | ||||||
|  | import kotlin.math.round | ||||||
|  |  | ||||||
|  |  | ||||||
|  | sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) { | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         if(type==DataType.BOOL) | ||||||
|  |             throw IllegalArgumentException("bool should have become ubyte @$position") | ||||||
|  |         if(type==DataType.UNDEFINED) { | ||||||
|  |             @Suppress("LeakingThis") | ||||||
|  |             when(this) { | ||||||
|  |                 is PtBuiltinFunctionCall -> { /* void function call */ } | ||||||
|  |                 is PtFunctionCall -> { /* void function call */ } | ||||||
|  |                 is PtIdentifier -> { /* non-variable identifier */ } | ||||||
|  |                 else -> throw IllegalArgumentException("type should be known @$position") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     infix fun isSameAs(other: PtExpression): Boolean { | ||||||
|  |         return when(this) { | ||||||
|  |             is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier | ||||||
|  |             is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index | ||||||
|  |             is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right | ||||||
|  |             is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable | ||||||
|  |             is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name | ||||||
|  |             is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register | ||||||
|  |             is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address | ||||||
|  |             is PtNumber -> other is PtNumber && other.type==type && other.number==number | ||||||
|  |             is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value | ||||||
|  |             is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step | ||||||
|  |             is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value | ||||||
|  |             else -> false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     infix fun isSameAs(target: PtAssignTarget): Boolean { | ||||||
|  |         return when { | ||||||
|  |             target.memory != null && this is PtMemoryByte-> { | ||||||
|  |                 target.memory!!.address isSameAs this.address | ||||||
|  |             } | ||||||
|  |             target.identifier != null && this is PtIdentifier -> { | ||||||
|  |                 this.name == target.identifier!!.name | ||||||
|  |             } | ||||||
|  |             target.array != null && this is PtArrayIndexer -> { | ||||||
|  |                 this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index | ||||||
|  |             } | ||||||
|  |             else -> false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() | ||||||
|  |  | ||||||
|  |     fun isSimple(): Boolean { | ||||||
|  |         return when(this) { | ||||||
|  |             is PtAddressOf -> true | ||||||
|  |             is PtArray -> true | ||||||
|  |             is PtArrayIndexer -> index is PtNumber || index is PtIdentifier | ||||||
|  |             is PtBinaryExpression -> false | ||||||
|  |             is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") | ||||||
|  |             is PtContainmentCheck -> false | ||||||
|  |             is PtFunctionCall -> false | ||||||
|  |             is PtIdentifier -> true | ||||||
|  |             is PtMachineRegister -> true | ||||||
|  |             is PtMemoryByte -> address is PtNumber || address is PtIdentifier | ||||||
|  |             is PtNumber -> true | ||||||
|  |             is PtPrefix -> value.isSimple() | ||||||
|  |             is PtRange -> true | ||||||
|  |             is PtString -> true | ||||||
|  |             is PtTypeCast -> value.isSimple() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |     fun clone(): PtExpression { | ||||||
|  |         fun withClonedChildrenFrom(orig: PtExpression, clone: PtExpression): PtExpression { | ||||||
|  |             orig.children.forEach { clone.add((it as PtExpression).clone()) } | ||||||
|  |             return clone | ||||||
|  |         } | ||||||
|  |         when(this) { | ||||||
|  |             is PtAddressOf -> return withClonedChildrenFrom(this, PtAddressOf(position)) | ||||||
|  |             is PtArray -> return withClonedChildrenFrom(this, PtArray(type, position)) | ||||||
|  |             is PtArrayIndexer -> return withClonedChildrenFrom(this, PtArrayIndexer(type, position)) | ||||||
|  |             is PtBinaryExpression -> return withClonedChildrenFrom(this, PtBinaryExpression(operator, type, position)) | ||||||
|  |             is PtBuiltinFunctionCall -> return withClonedChildrenFrom(this, PtBuiltinFunctionCall(name, void, hasNoSideEffects, type, position)) | ||||||
|  |             is PtContainmentCheck -> return withClonedChildrenFrom(this, PtContainmentCheck(position)) | ||||||
|  |             is PtFunctionCall -> return withClonedChildrenFrom(this, PtFunctionCall(name, void, type, position)) | ||||||
|  |             is PtIdentifier -> return withClonedChildrenFrom(this, PtIdentifier(name, type, position)) | ||||||
|  |             is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position)) | ||||||
|  |             is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position)) | ||||||
|  |             is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position)) | ||||||
|  |             is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position)) | ||||||
|  |             is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position)) | ||||||
|  |             is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position)) | ||||||
|  |             is PtTypeCast -> return withClonedChildrenFrom(this, PtTypeCast(type, position)) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { | ||||||
|  |     val identifier: PtIdentifier | ||||||
|  |         get() = children.single() as PtIdentifier | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) { | ||||||
|  |     val variable: PtIdentifier | ||||||
|  |         get() = children[0] as PtIdentifier | ||||||
|  |     val index: PtExpression | ||||||
|  |         get() = children[1] as PtExpression | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         require(elementType in NumericDatatypes) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtArray(type: DataType, position: Position): PtExpression(type, position) { | ||||||
|  |     override fun hashCode(): Int = Objects.hash(children, type) | ||||||
|  |     override fun equals(other: Any?): Boolean { | ||||||
|  |         if(other==null || other !is PtArray) | ||||||
|  |             return false | ||||||
|  |         return type==other.type && children == other.children | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val size: Int | ||||||
|  |         get() = children.size | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtBuiltinFunctionCall(val name: String, | ||||||
|  |                             val void: Boolean, | ||||||
|  |                             val hasNoSideEffects: Boolean, | ||||||
|  |                             type: DataType, | ||||||
|  |                             position: Position) : PtExpression(type, position) { | ||||||
|  |     init { | ||||||
|  |         if(!void) | ||||||
|  |             require(type!=DataType.UNDEFINED) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val args: List<PtExpression> | ||||||
|  |         get() = children.map { it as PtExpression } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) { | ||||||
|  |     // note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^. | ||||||
|  |     val left: PtExpression | ||||||
|  |         get() = children[0] as PtExpression | ||||||
|  |     val right: PtExpression | ||||||
|  |         get() = children[1] as PtExpression | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) { | ||||||
|  |     val element: PtExpression | ||||||
|  |         get() = children[0] as PtExpression | ||||||
|  |     val iterable: PtIdentifier | ||||||
|  |         get() = children[1] as PtIdentifier | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtFunctionCall(val name: String, | ||||||
|  |                      val void: Boolean, | ||||||
|  |                      type: DataType, | ||||||
|  |                      position: Position) : PtExpression(type, position) { | ||||||
|  |     init { | ||||||
|  |         if(!void) | ||||||
|  |             require(type!=DataType.UNDEFINED) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     val args: List<PtExpression> | ||||||
|  |         get() = children.map { it as PtExpression } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) { | ||||||
|  |     override fun toString(): String { | ||||||
|  |         return "[PtIdentifier:$name $type $position]" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun copy() = PtIdentifier(name, type, position) | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) { | ||||||
|  |     val address: PtExpression | ||||||
|  |         get() = children.single() as PtExpression | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { | ||||||
|  |  | ||||||
|  |     companion object { | ||||||
|  |         fun fromBoolean(bool: Boolean, position: Position): PtNumber = | ||||||
|  |             PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         if(type==DataType.BOOL) | ||||||
|  |             throw IllegalArgumentException("bool should have become ubyte @$position") | ||||||
|  |         if(type!=DataType.FLOAT) { | ||||||
|  |             val rounded = round(number) | ||||||
|  |             if (rounded != number) | ||||||
|  |                 throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position") | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun hashCode(): Int = Objects.hash(type, number) | ||||||
|  |  | ||||||
|  |     override fun equals(other: Any?): Boolean { | ||||||
|  |         if(other==null || other !is PtNumber) | ||||||
|  |             return false | ||||||
|  |         return number==other.number | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number) | ||||||
|  |  | ||||||
|  |     override fun toString() = "PtNumber:$type:$number" | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) { | ||||||
|  |     val value: PtExpression | ||||||
|  |         get() = children.single() as PtExpression | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         // note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0 | ||||||
|  |         require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtRange(type: DataType, position: Position) : PtExpression(type, position) { | ||||||
|  |     val from: PtExpression | ||||||
|  |         get() = children[0] as PtExpression | ||||||
|  |     val to: PtExpression | ||||||
|  |         get() = children[1] as PtExpression | ||||||
|  |     val step: PtNumber | ||||||
|  |         get() = children[2] as PtNumber | ||||||
|  |  | ||||||
|  |     fun toConstantIntegerRange(): IntProgression? { | ||||||
|  |         fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression { | ||||||
|  |             return when { | ||||||
|  |                 fromVal <= toVal -> when { | ||||||
|  |                     stepVal <= 0 -> IntRange.EMPTY | ||||||
|  |                     stepVal == 1 -> fromVal..toVal | ||||||
|  |                     else -> fromVal..toVal step stepVal | ||||||
|  |                 } | ||||||
|  |                 else -> when { | ||||||
|  |                     stepVal >= 0 -> IntRange.EMPTY | ||||||
|  |                     stepVal == -1 -> fromVal downTo toVal | ||||||
|  |                     else -> fromVal downTo toVal step abs(stepVal) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val fromLv = from as? PtNumber | ||||||
|  |         val toLv = to as? PtNumber | ||||||
|  |         val stepLv = step as? PtNumber | ||||||
|  |         if(fromLv==null || toLv==null || stepLv==null) | ||||||
|  |             return null | ||||||
|  |         val fromVal = fromLv.number.toInt() | ||||||
|  |         val toVal = toLv.number.toInt() | ||||||
|  |         val stepVal = stepLv.number.toInt() | ||||||
|  |         return makeRange(fromVal, toVal, stepVal) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) { | ||||||
|  |     override fun hashCode(): Int = Objects.hash(value, encoding) | ||||||
|  |     override fun equals(other: Any?): Boolean { | ||||||
|  |         if(other==null || other !is PtString) | ||||||
|  |             return false | ||||||
|  |         return value==other.value && encoding == other.encoding | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) { | ||||||
|  |     val value: PtExpression | ||||||
|  |         get() = children.single() as PtExpression | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // special node that isn't created from compiling user code, but used internally in the Intermediate Code | ||||||
|  | class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null | ||||||
|  | fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null | ||||||
							
								
								
									
										163
									
								
								codeCore/src/prog8/code/ast/AstPrinter.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								codeCore/src/prog8/code/ast/AstPrinter.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | |||||||
|  | package prog8.code.ast | ||||||
|  |  | ||||||
|  | import prog8.code.core.* | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root), | ||||||
|  |  * passing it as a String to the specified receiver function. | ||||||
|  |  */ | ||||||
|  | fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) { | ||||||
|  |     fun type(dt: DataType) = "!${dt.name.lowercase()}!" | ||||||
|  |     fun txt(node: PtNode): String { | ||||||
|  |         return when(node) { | ||||||
|  |             is PtAssignTarget -> "<target>" | ||||||
|  |             is PtAssignment -> "<assign>" | ||||||
|  |             is PtAugmentedAssign -> "<inplace-assign> ${node.operator}" | ||||||
|  |             is PtBreakpoint -> "%breakpoint" | ||||||
|  |             is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" | ||||||
|  |             is PtAddressOf -> "&" | ||||||
|  |             is PtArray -> "array len=${node.children.size} ${type(node.type)}" | ||||||
|  |             is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}" | ||||||
|  |             is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}" | ||||||
|  |             is PtBuiltinFunctionCall -> { | ||||||
|  |                 val str = if(node.void) "void " else "" | ||||||
|  |                 str + node.name + "()" | ||||||
|  |             } | ||||||
|  |             is PtContainmentCheck -> "in" | ||||||
|  |             is PtFunctionCall -> { | ||||||
|  |                 val str = if(node.void) "void " else "" | ||||||
|  |                 str + node.name + "()" | ||||||
|  |             } | ||||||
|  |             is PtIdentifier -> "${node.name} ${type(node.type)}" | ||||||
|  |             is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}" | ||||||
|  |             is PtMemoryByte -> "@()" | ||||||
|  |             is PtNumber -> { | ||||||
|  |                 val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex() | ||||||
|  |                 "$numstr ${type(node.type)}" | ||||||
|  |             } | ||||||
|  |             is PtPrefix -> node.operator | ||||||
|  |             is PtRange -> "<range>" | ||||||
|  |             is PtString -> "\"${node.value.escape()}\"" | ||||||
|  |             is PtTypeCast -> "as ${node.type.name.lowercase()}" | ||||||
|  |             is PtForLoop -> "for" | ||||||
|  |             is PtIfElse -> "ifelse" | ||||||
|  |             is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}" | ||||||
|  |             is PtInlineAssembly -> { | ||||||
|  |                 if(node.isIR) | ||||||
|  |                     "%ir {{ ...${node.assembly.length} characters... }}" | ||||||
|  |                 else | ||||||
|  |                     "%asm {{ ...${node.assembly.length} characters... }}" | ||||||
|  |             } | ||||||
|  |             is PtJump -> { | ||||||
|  |                 if(node.identifier!=null) | ||||||
|  |                     "goto ${node.identifier.name}" | ||||||
|  |                 else if(node.address!=null) | ||||||
|  |                     "goto ${node.address.toHex()}" | ||||||
|  |                 else if(node.generatedLabel!=null) | ||||||
|  |                     "goto ${node.generatedLabel}" | ||||||
|  |                 else | ||||||
|  |                     "???" | ||||||
|  |             } | ||||||
|  |             is PtAsmSub -> { | ||||||
|  |                 val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..." | ||||||
|  |                 val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}" | ||||||
|  |                 val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}") | ||||||
|  |                 val str = if (node.inline) "inline " else "" | ||||||
|  |                 if(node.address==null) { | ||||||
|  |                     str + "asmsub ${node.name}($params) $clobbers $returns" | ||||||
|  |                 } else { | ||||||
|  |                     str + "romsub ${node.address.toHex()} = ${node.name}($params) $clobbers $returns" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             is PtBlock -> { | ||||||
|  |                 val addr = if(node.address==null) "" else "@${node.address.toHex()}" | ||||||
|  |                 val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}" | ||||||
|  |                 "\nblock '${node.name}' $addr $align" | ||||||
|  |             } | ||||||
|  |             is PtConstant -> { | ||||||
|  |                 val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString() | ||||||
|  |                 "const ${node.type.name.lowercase()} ${node.name} = $value" | ||||||
|  |             } | ||||||
|  |             is PtLabel -> "${node.name}:" | ||||||
|  |             is PtMemMapped -> { | ||||||
|  |                 if(node.type in ArrayDatatypes) { | ||||||
|  |                     val arraysize = if(node.arraySize==null) "" else node.arraySize.toString() | ||||||
|  |                     val eltType = ArrayToElementTypes.getValue(node.type) | ||||||
|  |                     "&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}" | ||||||
|  |                 } else { | ||||||
|  |                     "&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             is PtSub -> { | ||||||
|  |                 val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..." | ||||||
|  |                 var str = "sub ${node.name}($params) " | ||||||
|  |                 if(node.returntype!=null) | ||||||
|  |                     str += "-> ${node.returntype.name.lowercase()}" | ||||||
|  |                 str | ||||||
|  |             } | ||||||
|  |             is PtVariable -> { | ||||||
|  |                 val str = if(node.arraySize!=null) { | ||||||
|  |                     val eltType = ArrayToElementTypes.getValue(node.type) | ||||||
|  |                     "${eltType.name.lowercase()}[${node.arraySize}] ${node.name}" | ||||||
|  |                 } | ||||||
|  |                 else if(node.type in ArrayDatatypes) { | ||||||
|  |                     val eltType = ArrayToElementTypes.getValue(node.type) | ||||||
|  |                     "${eltType.name.lowercase()}[] ${node.name}" | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                     "${node.type.name.lowercase()} ${node.name}" | ||||||
|  |                 if(node.value!=null) | ||||||
|  |                     str + " = " + txt(node.value) | ||||||
|  |                 else | ||||||
|  |                     str | ||||||
|  |             } | ||||||
|  |             is PtNodeGroup -> "<group>" | ||||||
|  |             is PtNop -> "nop" | ||||||
|  |             is PtPostIncrDecr -> "<post> ${node.operator}" | ||||||
|  |             is PtProgram -> "PROGRAM ${node.name}" | ||||||
|  |             is PtRepeatLoop -> "repeat" | ||||||
|  |             is PtReturn -> "return" | ||||||
|  |             is PtSubroutineParameter -> "${node.type.name.lowercase()} ${node.name}" | ||||||
|  |             is PtWhen -> "when" | ||||||
|  |             is PtWhenChoice -> { | ||||||
|  |                 if(node.isElse) | ||||||
|  |                     "else" | ||||||
|  |                 else | ||||||
|  |                     "->" | ||||||
|  |             } | ||||||
|  |             else -> throw InternalCompilerException("unrecognised ast node $node") | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if(root is PtProgram) { | ||||||
|  |         output(txt(root)) | ||||||
|  |         root.children.forEach { | ||||||
|  |             walkAst(it) { node, depth -> | ||||||
|  |                 val txt = txt(node) | ||||||
|  |                 val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true | ||||||
|  |                 if(!library || !skipLibraries) { | ||||||
|  |                     if (txt.isNotEmpty()) | ||||||
|  |                         output("    ".repeat(depth) + txt(node)) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         println() | ||||||
|  |     } else { | ||||||
|  |         walkAst(root) { node, depth -> | ||||||
|  |             val txt = txt(node) | ||||||
|  |             val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true | ||||||
|  |             if(!library || !skipLibraries) { | ||||||
|  |                 if (txt.isNotEmpty()) | ||||||
|  |                     output("    ".repeat(depth) + txt(node)) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) { | ||||||
|  |     fun recurse(node: PtNode, depth: Int) { | ||||||
|  |         act(node, depth) | ||||||
|  |         node.children.forEach { recurse(it, depth+1) } | ||||||
|  |     } | ||||||
|  |     recurse(root, 0) | ||||||
|  | } | ||||||
							
								
								
									
										172
									
								
								codeCore/src/prog8/code/ast/AstStatements.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								codeCore/src/prog8/code/ast/AstStatements.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | |||||||
|  | package prog8.code.ast | ||||||
|  |  | ||||||
|  | import prog8.code.core.* | ||||||
|  |  | ||||||
|  |  | ||||||
|  | sealed interface IPtSubroutine { | ||||||
|  |     val name: String | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class PtAsmSub( | ||||||
|  |     name: String, | ||||||
|  |     val address: UInt?, | ||||||
|  |     val clobbers: Set<CpuRegister>, | ||||||
|  |     val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>, | ||||||
|  |     val returns: List<Pair<RegisterOrStatusflag, DataType>>, | ||||||
|  |     val inline: Boolean, | ||||||
|  |     position: Position | ||||||
|  | ) : PtNamedNode(name, position), IPtSubroutine | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtSub( | ||||||
|  |     name: String, | ||||||
|  |     val parameters: List<PtSubroutineParameter>, | ||||||
|  |     val returntype: DataType?, | ||||||
|  |     position: Position | ||||||
|  | ) : PtNamedNode(name, position), IPtSubroutine { | ||||||
|  |     init { | ||||||
|  |         // params and return value should not be str | ||||||
|  |         if(parameters.any{ it.type !in NumericDatatypes }) | ||||||
|  |             throw AssemblyError("non-numeric parameter") | ||||||
|  |         if(returntype!=null && returntype !in NumericDatatypes) | ||||||
|  |             throw AssemblyError("non-numeric returntype $returntype") | ||||||
|  |         parameters.forEach { it.parent=this } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | sealed interface IPtAssignment { | ||||||
|  |     val children: MutableList<PtNode> | ||||||
|  |     val target: PtAssignTarget | ||||||
|  |         get() = children[0] as PtAssignTarget | ||||||
|  |     val value: PtExpression | ||||||
|  |         get() = children[1] as PtExpression | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class PtAssignment(position: Position) : PtNode(position), IPtAssignment | ||||||
|  |  | ||||||
|  | class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtAssignTarget(position: Position) : PtNode(position) { | ||||||
|  |     val identifier: PtIdentifier? | ||||||
|  |         get() = children.single() as? PtIdentifier | ||||||
|  |     val array: PtArrayIndexer? | ||||||
|  |         get() = children.single() as? PtArrayIndexer | ||||||
|  |     val memory: PtMemoryByte? | ||||||
|  |         get() = children.single() as? PtMemoryByte | ||||||
|  |  | ||||||
|  |     val type: DataType | ||||||
|  |         get() { | ||||||
|  |             return when(val tgt = children.single()) { | ||||||
|  |                 is PtIdentifier -> tgt.type | ||||||
|  |                 is PtArrayIndexer -> tgt.type | ||||||
|  |                 is PtMemoryByte -> tgt.type | ||||||
|  |                 else -> throw AssemblyError("weird target $tgt") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this) | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) { | ||||||
|  |     val trueScope: PtNodeGroup | ||||||
|  |         get() = children[0] as PtNodeGroup | ||||||
|  |     val falseScope: PtNodeGroup | ||||||
|  |         get() = children[1] as PtNodeGroup | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtForLoop(position: Position) : PtNode(position) { | ||||||
|  |     val variable: PtIdentifier | ||||||
|  |         get() = children[0] as PtIdentifier | ||||||
|  |     val iterable: PtExpression | ||||||
|  |         get() = children[1] as PtExpression | ||||||
|  |     val statements: PtNodeGroup | ||||||
|  |         get() = children[2] as PtNodeGroup | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtIfElse(position: Position) : PtNode(position) { | ||||||
|  |     val condition: PtExpression | ||||||
|  |         get() = children[0] as PtExpression | ||||||
|  |     val ifScope: PtNodeGroup | ||||||
|  |         get() = children[1] as PtNodeGroup | ||||||
|  |     val elseScope: PtNodeGroup | ||||||
|  |         get() = children[2] as PtNodeGroup | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtJump(val identifier: PtIdentifier?, | ||||||
|  |              val address: UInt?, | ||||||
|  |              val generatedLabel: String?, | ||||||
|  |              position: Position) : PtNode(position) { | ||||||
|  |     init { | ||||||
|  |         identifier?.let {it.parent = this } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) { | ||||||
|  |     val target: PtAssignTarget | ||||||
|  |         get() = children.single() as PtAssignTarget | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtRepeatLoop(position: Position) : PtNode(position) { | ||||||
|  |     val count: PtExpression | ||||||
|  |         get() = children[0] as PtExpression | ||||||
|  |     val statements: PtNodeGroup | ||||||
|  |         get() = children[1] as PtNodeGroup | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtReturn(position: Position) : PtNode(position) { | ||||||
|  |     val hasValue = children.any() | ||||||
|  |     val value: PtExpression? | ||||||
|  |         get() { | ||||||
|  |             return if(children.any()) | ||||||
|  |                 children.single() as PtExpression | ||||||
|  |             else | ||||||
|  |                 null | ||||||
|  |         } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | sealed interface IPtVariable { | ||||||
|  |     val name: String | ||||||
|  |     val type: DataType | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable { | ||||||
|  |     init { | ||||||
|  |         value?.let {it.parent=this} | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtWhen(position: Position) : PtNode(position) { | ||||||
|  |     val value: PtExpression | ||||||
|  |         get() = children[0] as PtExpression | ||||||
|  |     val choices: PtNodeGroup | ||||||
|  |         get() = children[1] as PtNodeGroup | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) { | ||||||
|  |     val values: PtNodeGroup | ||||||
|  |         get() = children[0] as PtNodeGroup | ||||||
|  |     val statements: PtNodeGroup | ||||||
|  |         get() = children[1] as PtNodeGroup | ||||||
|  | } | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| package prog8.code.core | package prog8.code.core | ||||||
|  |  | ||||||
| class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?) | class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean) | ||||||
| class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean) | class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean) | ||||||
| class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) { | class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) { | ||||||
|     override fun toString(): String { |     override fun toString(): String { | ||||||
|         val paramConvs =  params.mapIndexed { index, it -> |         val paramConvs =  params.mapIndexed { index, it -> | ||||||
| @@ -13,43 +13,35 @@ class CallConvention(val params: List<ParamConvention>, val returns: ReturnConve | |||||||
|         } |         } | ||||||
|         val returnConv = |         val returnConv = | ||||||
|             when { |             when { | ||||||
|                 returns.reg == RegisterOrPair.FAC1 -> "floatFAC1" |  | ||||||
|                 returns.reg!=null -> returns.reg.toString() |                 returns.reg!=null -> returns.reg.toString() | ||||||
|  |                 returns.floatFac1 -> "floatFAC1" | ||||||
|                 else -> "<no returnvalue>" |                 else -> "<no returnvalue>" | ||||||
|             } |             } | ||||||
|         return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]" |         return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| class FParam(val name: String, vararg val possibleDatatypes: BaseDataType) | class FParam(val name: String, val possibleDatatypes: Array<DataType>) | ||||||
|  |  | ||||||
|  |  | ||||||
| private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) |  | ||||||
| private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG) |  | ||||||
| private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class FSignature(val pure: Boolean,      // does it have side effects? | class FSignature(val pure: Boolean,      // does it have side effects? | ||||||
|                  val returnType: BaseDataType?, |                       val parameters: List<FParam>, | ||||||
|                  vararg val parameters: FParam) { |                       val returnType: DataType?) { | ||||||
|  |  | ||||||
|     fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention { |     fun callConvention(actualParamTypes: List<DataType>): CallConvention { | ||||||
|         val returns: ReturnConvention = when (returnType) { |         val returns: ReturnConvention = when (returnType) { | ||||||
|             BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A) |             DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false) | ||||||
|             BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY) |             DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false) | ||||||
|             BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15_32) |             DataType.FLOAT -> ReturnConvention(returnType, null, true) | ||||||
|             BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1) |             in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false) | ||||||
|             in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY) |             null -> ReturnConvention(null, null, false) | ||||||
|             null -> ReturnConvention(null, null) |  | ||||||
|             else -> { |             else -> { | ||||||
|                 // return type depends on arg type |                 // return type depends on arg type | ||||||
|                 when (val paramType = actualParamTypes.first()) { |                 when (val paramType = actualParamTypes.first()) { | ||||||
|                     BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A) |                     DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false) | ||||||
|                     BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY) |                     DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false) | ||||||
|                     BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15_32) |                     DataType.FLOAT -> ReturnConvention(paramType, null, true) | ||||||
|                     BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1) |                     in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false) | ||||||
|                     in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY) |                     else -> ReturnConvention(paramType, null, false) | ||||||
|                     else -> ReturnConvention(paramType, null) |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -57,28 +49,16 @@ class FSignature(val pure: Boolean,      // does it have side effects? | |||||||
|         return when { |         return when { | ||||||
|             actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns) |             actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns) | ||||||
|             actualParamTypes.size==1 -> { |             actualParamTypes.size==1 -> { | ||||||
|                 // One parameter goes via register/registerpair. |                 // one parameter goes via register/registerpair | ||||||
|                 // this avoids repeated code for every caller to store the value in the subroutine's argument variable. |  | ||||||
|                 // (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site). |  | ||||||
|                 val paramConv = when(val paramType = actualParamTypes[0]) { |                 val paramConv = when(val paramType = actualParamTypes[0]) { | ||||||
|                     BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false) |                     DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false) | ||||||
|                     BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false) |                     DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false) | ||||||
|                     BaseDataType.LONG -> ParamConvention(paramType, RegisterOrPair.R14R15_32, false) |                     DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, 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 PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false) | ||||||
|                     in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false) |  | ||||||
|                     else -> ParamConvention(paramType, null, false) |                     else -> ParamConvention(paramType, null, false) | ||||||
|                 } |                 } | ||||||
|                 CallConvention(listOf(paramConv), returns) |                 CallConvention(listOf(paramConv), returns) | ||||||
|             } |             } | ||||||
|             actualParamTypes.size==2 && (actualParamTypes[0].isByte && actualParamTypes[1].isWord) -> { |  | ||||||
|                 TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet") |  | ||||||
|             } |  | ||||||
|             actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> { |  | ||||||
|                 TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet") |  | ||||||
|             } |  | ||||||
|             actualParamTypes.size==3 && actualParamTypes.all { it.isByte } -> { |  | ||||||
|                 TODO("opportunity to pass 3 byte arguments in A,Y and X registers but not implemented yet") |  | ||||||
|             } |  | ||||||
|             else -> { |             else -> { | ||||||
|                 // multiple parameters go via variables |                 // multiple parameters go via variables | ||||||
|                 val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) } |                 val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) } | ||||||
| @@ -88,83 +68,46 @@ class FSignature(val pure: Boolean,      // does it have side effects? | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| val BuiltinFunctions: Map<String, FSignature> = mapOf( | val BuiltinFunctions: Map<String, FSignature> = mapOf( | ||||||
|     "setlsb"  to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)), |     // this set of function have no return value and operate in-place: | ||||||
|     "setmsb"  to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)), |     "rol"       to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||||
|     "rol"     to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)), |     "ror"       to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||||
|     "ror"     to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)), |     "rol2"      to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||||
|     "rol2"    to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)), |     "ror2"      to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), | ||||||
|     "ror2"    to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)), |     "sort"      to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), | ||||||
|     "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 |     "reverse"   to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), | ||||||
|     "prog8_lib_stringcompare"     to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)), |     // cmp returns a status in the carry flag, but not a proper return value | ||||||
|     "prog8_lib_square_byte"       to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)), |     "cmp"       to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), | ||||||
|     "prog8_lib_square_word"       to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)), |     "prog8_lib_stringcompare"       to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), | ||||||
|     "prog8_lib_structalloc"       to FSignature(true, BaseDataType.UWORD), |     "abs"       to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD), | ||||||
|     "abs"           to FSignature(true, null, FParam("value", *NumericDatatypes)), |     "len"       to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), | ||||||
|     "abs__byte"     to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)), |     // normal functions follow: | ||||||
|     "abs__word"     to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)), |     "sizeof"    to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE), | ||||||
|     "abs__long"     to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG)), |     "sgn"       to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE), | ||||||
|     "abs__float"    to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)), |     "sqrt16"    to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE), | ||||||
|     "len"           to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)), |     "divmod"    to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), | ||||||
|     "sizeof"        to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())), |     "divmodw"   to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), | ||||||
|     "offsetof"      to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)), |     "any"       to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), | ||||||
|     "sgn"           to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)), |     "all"       to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), | ||||||
|     "sqrt"          to FSignature(true, null, FParam("value", *NumericDatatypes)), |     "lsb"       to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), | ||||||
|     "sqrt__ubyte"   to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)), |     "msb"       to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), | ||||||
|     "sqrt__uword"   to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)), |     "mkword"    to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD), | ||||||
|     "sqrt__float"   to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)), |     "peek"      to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), | ||||||
|     "divmod"        to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)), |     "peekw"     to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||||
|     "divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)), |     "poke"      to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), | ||||||
|     "divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)), |     "pokemon"   to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), | ||||||
|     "lsb"           to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)), |     "pokew"     to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), | ||||||
|     "lsb__long"     to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.LONG)), |     "pop"       to FSignature(false, listOf(FParam("target", ByteDatatypes)), null), | ||||||
|     "msb"           to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)), |     "popw"      to FSignature(false, listOf(FParam("target", WordDatatypes)), null), | ||||||
|     "msb__long"     to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.LONG)), |     "push"      to FSignature(false, listOf(FParam("value", ByteDatatypes)), null), | ||||||
|     "lsw"           to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)), |     "pushw"     to FSignature(false, listOf(FParam("value", WordDatatypes)), null), | ||||||
|     "msw"           to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)), |     "rsave"     to FSignature(false, emptyList(), null), | ||||||
|     "mkword"        to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)), |     "rsavex"    to FSignature(false, emptyList(), null), | ||||||
|     "mklong"        to FSignature(true, BaseDataType.LONG, FParam("msb", BaseDataType.UBYTE), FParam("b2", BaseDataType.UBYTE), FParam("b1", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)), |     "rrestore"  to FSignature(false, emptyList(), null), | ||||||
|     "mklong2"       to FSignature(true, BaseDataType.LONG, FParam("msw", BaseDataType.UWORD), FParam("lsw", BaseDataType.UWORD)), |     "rrestorex" to FSignature(false, emptyList(), null), | ||||||
|     "clamp"         to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)), |     "memory"    to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||||
|     "clamp__byte"   to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)), |     "callfar"   to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD), | ||||||
|     "clamp__ubyte"  to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)), |  | ||||||
|     "clamp__word"   to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)), |  | ||||||
|     "clamp__uword"  to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)), |  | ||||||
|     "clamp__long"   to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG), FParam("minimum", BaseDataType.LONG), FParam("maximum", BaseDataType.LONG)), |  | ||||||
|     "min"           to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)), |  | ||||||
|     "min__byte"     to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)), |  | ||||||
|     "min__ubyte"    to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)), |  | ||||||
|     "min__word"     to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)), |  | ||||||
|     "min__uword"    to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)), |  | ||||||
|     "min__long"     to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)), |  | ||||||
|     "max"           to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)), |  | ||||||
|     "max__byte"     to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)), |  | ||||||
|     "max__ubyte"    to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)), |  | ||||||
|     "max__word"     to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)), |  | ||||||
|     "max__uword"    to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)), |  | ||||||
|     "max__long"     to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)), |  | ||||||
|     "peek"          to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)), |  | ||||||
|     "peekbool"      to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)), |  | ||||||
|     "peekw"         to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)), |  | ||||||
|     "peekl"         to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)), |  | ||||||
|     "peekf"         to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)), |  | ||||||
|     "poke"          to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE, BaseDataType.BYTE)), |  | ||||||
|     "pokebool"      to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)), |  | ||||||
|     "pokebowl"      to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)), |  | ||||||
|     "pokew"         to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD, BaseDataType.WORD)), |  | ||||||
|     "pokel"         to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)), |  | ||||||
|     "pokef"         to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)), |  | ||||||
|     "pokemon"       to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)), |  | ||||||
|     "rsave"         to FSignature(false, null), |  | ||||||
|     "rrestore"      to FSignature(false, null), |  | ||||||
|     "memory"        to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)), |  | ||||||
|     "callfar"       to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)), |  | ||||||
|     "callfar2"      to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)), |  | ||||||
|     "call"          to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)), |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| val InplaceModifyingBuiltinFunctions = setOf( | val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse") | ||||||
|     "setlsb", "setmsb", |  | ||||||
|     "rol", "ror", "rol2", "ror2", |  | ||||||
|     "divmod", "divmod__ubyte", "divmod__uword" |  | ||||||
| ) |  | ||||||
|   | |||||||
| @@ -8,38 +8,24 @@ class CompilationOptions(val output: OutputType, | |||||||
|                          val launcher: CbmPrgLauncherType, |                          val launcher: CbmPrgLauncherType, | ||||||
|                          val zeropage: ZeropageType, |                          val zeropage: ZeropageType, | ||||||
|                          val zpReserved: List<UIntRange>, |                          val zpReserved: List<UIntRange>, | ||||||
|                          val zpAllowed: List<UIntRange>, |  | ||||||
|                          val floats: Boolean, |                          val floats: Boolean, | ||||||
|                          val noSysInit: Boolean, |                          val noSysInit: Boolean, | ||||||
|                          val romable: Boolean, |  | ||||||
|                          val compTarget: ICompilationTarget, |                          val compTarget: ICompilationTarget, | ||||||
|                          val compilerVersion: String, |  | ||||||
|                          // these are set later, based on command line arguments or options in the source code: |                          // these are set later, based on command line arguments or options in the source code: | ||||||
|                          var loadAddress: UInt, |                          var loadAddress: UInt, | ||||||
|                          var memtopAddress: UInt, |                          var slowCodegenWarnings: Boolean = false, | ||||||
|                          var warnSymbolShadowing: Boolean = false, |  | ||||||
|                          var optimize: Boolean = false, |                          var optimize: Boolean = false, | ||||||
|  |                          var optimizeFloatExpressions: Boolean = false, | ||||||
|                          var asmQuiet: Boolean = false, |                          var asmQuiet: Boolean = false, | ||||||
|                          var asmListfile: Boolean = false, |                          var asmListfile: Boolean = false, | ||||||
|                          var includeSourcelines: Boolean = false, |  | ||||||
|                          var dumpVariables: Boolean = false, |  | ||||||
|                          var dumpSymbols: Boolean = false, |  | ||||||
|                          var experimentalCodegen: Boolean = false, |                          var experimentalCodegen: Boolean = false, | ||||||
|                          var varsHighBank: Int? = null, |                          var varsHigh: Boolean = false, | ||||||
|                          var varsGolden: Boolean = false, |                          var useNewExprCode: Boolean = false, | ||||||
|                          var slabsHighBank: Int? = null, |                          var evalStackBaseAddress: UInt? = null, | ||||||
|                          var slabsGolden: Boolean = false, |  | ||||||
|                          var breakpointCpuInstruction: String? = null, |  | ||||||
|                          var ignoreFootguns: Boolean = false, |  | ||||||
|                          var outputDir: Path = Path(""), |                          var outputDir: Path = Path(""), | ||||||
|                          var quiet: Boolean = false, |  | ||||||
|                          var symbolDefs: Map<String, String> = emptyMap() |                          var symbolDefs: Map<String, String> = emptyMap() | ||||||
| ) { | ) { | ||||||
|     init { |     init { | ||||||
|         compTarget.initializeMemoryAreas(this) |         compTarget.machine.initializeMemoryAreas(this) | ||||||
|     } |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|         val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,26 +1,20 @@ | |||||||
| package prog8.code.core | package prog8.code.core | ||||||
|  |  | ||||||
| import kotlin.math.abs | import kotlin.math.abs | ||||||
| import kotlin.math.pow |  | ||||||
|  |  | ||||||
| val powersOfTwoFloat = (0..16).map { (2.0).pow(it) }.toTypedArray() |  | ||||||
| val negativePowersOfTwoFloat = powersOfTwoFloat.map { -it }.toTypedArray() |  | ||||||
| val powersOfTwoInt = (0..16).map { 2.0.pow(it).toInt() }.toTypedArray() |  | ||||||
|  |  | ||||||
| fun Number.toHex(): String { | fun Number.toHex(): String { | ||||||
|     //  0..15 -> "0".."15" |     //  0..15 -> "0".."15" | ||||||
|     //  16..255 -> "$10".."$ff" |     //  16..255 -> "$10".."$ff" | ||||||
|     //  256..65536 -> "$0100".."$ffff" |     //  256..65536 -> "$0100".."$ffff" | ||||||
|     //  larger -> "$12345678" |  | ||||||
|     // negative values are prefixed with '-'. |     // negative values are prefixed with '-'. | ||||||
|     val integer = this.toLong() |     val integer = this.toInt() | ||||||
|     if(integer<0) |     if(integer<0) | ||||||
|         return '-' + abs(integer).toHex() |         return '-' + abs(integer).toHex() | ||||||
|     return when (integer) { |     return when (integer) { | ||||||
|         in 0 until 16 -> integer.toString() |         in 0 until 16 -> integer.toString() | ||||||
|         in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0') |         in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0') | ||||||
|         in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0') |         in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0') | ||||||
|         else -> "$"+integer.toString(16).padStart(8,'0') |         else -> throw IllegalArgumentException("number too large for 16 bits $this") | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -28,12 +22,11 @@ fun UInt.toHex(): String { | |||||||
|     //  0..15 -> "0".."15" |     //  0..15 -> "0".."15" | ||||||
|     //  16..255 -> "$10".."$ff" |     //  16..255 -> "$10".."$ff" | ||||||
|     //  256..65536 -> "$0100".."$ffff" |     //  256..65536 -> "$0100".."$ffff" | ||||||
|     //  larger -> "$12345678" |  | ||||||
|     return when (this) { |     return when (this) { | ||||||
|         in 0u until 16u -> this.toString() |         in 0u until 16u -> this.toString() | ||||||
|         in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0') |         in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0') | ||||||
|         in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0') |         in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0') | ||||||
|         else -> "$"+this.toString(16).padStart(8,'0') |         else -> throw IllegalArgumentException("number too large for 16 bits $this") | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -65,7 +58,6 @@ fun String.unescape(): String { | |||||||
|                 '\\' -> '\\' |                 '\\' -> '\\' | ||||||
|                 'n' -> '\n' |                 'n' -> '\n' | ||||||
|                 'r' -> '\r' |                 'r' -> '\r' | ||||||
|                 't' -> '\t' |  | ||||||
|                 '"' -> '"' |                 '"' -> '"' | ||||||
|                 '\'' -> '\'' |                 '\'' -> '\'' | ||||||
|                 'u' -> { |                 'u' -> { | ||||||
|   | |||||||
| @@ -1,388 +1,58 @@ | |||||||
| package prog8.code.core | package prog8.code.core | ||||||
|  |  | ||||||
| import java.util.* | enum class DataType { | ||||||
|  |     UBYTE,              // pass by value | ||||||
| enum class BaseDataType { |     BYTE,               // pass by value | ||||||
|     UBYTE,              // pass by value            8 bits unsigned |     UWORD,              // pass by value | ||||||
|     BYTE,               // pass by value            8 bits signed |     WORD,               // pass by value | ||||||
|     UWORD,              // pass by value            16 bits unsigned |     FLOAT,              // pass by value | ||||||
|     WORD,               // pass by value            16 bits signed |     BOOL,               // pass by value | ||||||
|     LONG,               // pass by value            32 bits signed |  | ||||||
|     FLOAT,              // pass by value            machine dependent |  | ||||||
|     BOOL,               // pass by value            bit 0 of an 8-bit byte |  | ||||||
|     STR,                // pass by reference |     STR,                // pass by reference | ||||||
|     ARRAY,              // pass by reference, subtype is the element type |     ARRAY_UB,           // pass by reference | ||||||
|     ARRAY_SPLITW,       // pass by reference, split word layout, subtype is the element type (restricted to word types) |     ARRAY_B,            // pass by reference | ||||||
|     POINTER,            // typed pointer, subtype is whatever type is pointed to |     ARRAY_UW,           // pass by reference | ||||||
|     STRUCT_INSTANCE,    // the actual instance of a struct (not directly supported in the language yet, but we need its type) |     ARRAY_W,            // pass by reference | ||||||
|     ARRAY_POINTER,      // array of pointers (uwords), subtype is whatever type each element points to |     ARRAY_F,            // pass by reference | ||||||
|  |     ARRAY_BOOL,         // pass by reference | ||||||
|     UNDEFINED; |     UNDEFINED; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * is the type assignable to the given other type (perhaps via a typecast) without loss of precision? | ||||||
|  |      */ | ||||||
|  |     infix fun isAssignableTo(targetType: DataType) = | ||||||
|  |         when(this) { | ||||||
|  |             BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT) | ||||||
|  |             UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL) | ||||||
|  |             BYTE -> targetType.oneOf(BYTE, WORD, FLOAT) | ||||||
|  |             UWORD -> targetType.oneOf(UWORD, FLOAT) | ||||||
|  |             WORD -> targetType.oneOf(WORD, FLOAT) | ||||||
|  |             FLOAT -> targetType.oneOf(FLOAT) | ||||||
|  |             STR -> targetType.oneOf(STR, UWORD) | ||||||
|  |             in ArrayDatatypes -> targetType == this | ||||||
|  |             else -> false | ||||||
|  |         } | ||||||
|  |  | ||||||
|     fun largerSizeThan(other: BaseDataType) = |     fun oneOf(vararg types: DataType) = this in types | ||||||
|  |  | ||||||
|  |     infix fun largerThan(other: DataType) = | ||||||
|         when { |         when { | ||||||
|             this == other -> false |             this == other -> false | ||||||
|             this.isByteOrBool -> false |             this in ByteDatatypes -> false | ||||||
|             this.isWord -> other.isByteOrBool |             this in WordDatatypes -> other in ByteDatatypes | ||||||
|             this == LONG -> other.isByteOrBool || other.isWord |  | ||||||
|             this== STR && other== UWORD || this== UWORD && other== STR -> false |             this== STR && other== UWORD || this== UWORD && other== STR -> false | ||||||
|             this.isArray && other.isArray -> false |  | ||||||
|             this.isArray -> other != FLOAT |  | ||||||
|             this == STR -> other != FLOAT |  | ||||||
|             this.isPointer -> other.isByteOrBool |  | ||||||
|             else -> true |             else -> true | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     fun equalsSize(other: BaseDataType) = |     infix fun equalsSize(other: DataType) = | ||||||
|         when { |         when { | ||||||
|             this == other -> true |             this == other -> true | ||||||
|             this.isArray && other.isArray -> true |             this in ByteDatatypes -> other in ByteDatatypes | ||||||
|             this.isByteOrBool -> other.isByteOrBool |             this in WordDatatypes -> other in WordDatatypes | ||||||
|             this.isWord -> other.isWord || other.isPointer |  | ||||||
|             this.isPointer -> other.isWord |  | ||||||
|             this== STR && other== UWORD || this== UWORD && other== STR -> true |             this== STR && other== UWORD || this== UWORD && other== STR -> true | ||||||
|             this == STR && other.isArray -> true |  | ||||||
|             this.isArray && other == STR -> true |  | ||||||
|             else -> false |             else -> false | ||||||
|         } |         } | ||||||
| } | } | ||||||
|  |  | ||||||
| val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE) |  | ||||||
| val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL) |  | ||||||
| val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD) |  | ||||||
| val BaseDataType.isLong get() = this == BaseDataType.LONG |  | ||||||
| val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG) |  | ||||||
| val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL) |  | ||||||
| val BaseDataType.isWordOrByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.BOOL) |  | ||||||
| val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger |  | ||||||
| val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric |  | ||||||
| val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) |  | ||||||
| val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER |  | ||||||
| val BaseDataType.isPointer get() = this == BaseDataType.POINTER |  | ||||||
| val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE |  | ||||||
| val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER |  | ||||||
| val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER       // pointer arrays are also always stored as split uwords |  | ||||||
| val BaseDataType.isIterable get() =  this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER) |  | ||||||
| val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer |  | ||||||
| val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer |  | ||||||
|  |  | ||||||
|  |  | ||||||
| interface ISubType { |  | ||||||
|     val scopedNameString: String |  | ||||||
|     fun memsize(sizer: IMemSizer): Int |  | ||||||
|     fun sameas(other: ISubType): Boolean |  | ||||||
|     fun getFieldType(name: String): DataType? |  | ||||||
| } |  | ||||||
|  |  | ||||||
| class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List<String>?=null) { |  | ||||||
|  |  | ||||||
|     init { |  | ||||||
|         when { |  | ||||||
|             base.isPointerArray -> { |  | ||||||
|                 require(sub!=null || subType!=null || subTypeFromAntlr!=null) |  | ||||||
|             } |  | ||||||
|             base.isArray -> { |  | ||||||
|                 require(sub != null && subType==null && subTypeFromAntlr==null) |  | ||||||
|                 if(base.isSplitWordArray) |  | ||||||
|                     require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD) |  | ||||||
|             } |  | ||||||
|             base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" } |  | ||||||
|             base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"} |  | ||||||
|             else -> { |  | ||||||
|                 require(sub == null || (subType == null && subTypeFromAntlr == null)) { |  | ||||||
|                     "sub and subtype can't both be set" |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun equals(other: Any?): Boolean { |  | ||||||
|         if (this === other) return true |  | ||||||
|         if (other !is DataType) return false |  | ||||||
|         return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!)) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun hashCode(): Int = Objects.hash(base, sub, subType) |  | ||||||
|  |  | ||||||
|     fun setActualSubType(actualSubType: ISubType) { |  | ||||||
|         subType = actualSubType |  | ||||||
|         subTypeFromAntlr = null |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|  |  | ||||||
|         val UBYTE = DataType(BaseDataType.UBYTE, null, null) |  | ||||||
|         val BYTE = DataType(BaseDataType.BYTE, null, null) |  | ||||||
|         val UWORD = DataType(BaseDataType.UWORD, null, null) |  | ||||||
|         val WORD = DataType(BaseDataType.WORD, null, null) |  | ||||||
|         val LONG = DataType(BaseDataType.LONG, null, null) |  | ||||||
|         val FLOAT = DataType(BaseDataType.FLOAT, null, null) |  | ||||||
|         val BOOL = DataType(BaseDataType.BOOL, null, null) |  | ||||||
|         val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null) |  | ||||||
|         val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null) |  | ||||||
|  |  | ||||||
|         private val simpletypes = mapOf( |  | ||||||
|             BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null), |  | ||||||
|             BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null), |  | ||||||
|             BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null), |  | ||||||
|             BaseDataType.WORD to DataType(BaseDataType.WORD, null, null), |  | ||||||
|             BaseDataType.LONG to DataType(BaseDataType.LONG, null, null), |  | ||||||
|             BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null), |  | ||||||
|             BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null), |  | ||||||
|             BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null), |  | ||||||
|             BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null) |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         fun forDt(dt: BaseDataType): DataType { |  | ||||||
|             if(dt.isStructInstance) |  | ||||||
|                 TODO("cannot use struct instance as a data type (yet) - use a pointer instead") |  | ||||||
|             return simpletypes.getValue(dt) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType { |  | ||||||
|             require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" } |  | ||||||
|             val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt      // array of strings is actually just an array of UWORD pointers |  | ||||||
|             return if(splitwordarray && actualElementDt.isWord) |  | ||||||
|                 DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null) |  | ||||||
|             else { |  | ||||||
|                 if(actualElementDt.isNumericOrBool) |  | ||||||
|                     DataType(BaseDataType.ARRAY, actualElementDt, null) |  | ||||||
|                 else |  | ||||||
|                     throw NoSuchElementException("invalid basic element dt $elementDt") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null) |  | ||||||
|         fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType) |  | ||||||
|         fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType = |  | ||||||
|             DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier) |  | ||||||
|  |  | ||||||
|         fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null) |  | ||||||
|         fun pointer(dt: DataType): DataType = if(dt.isBasic) |  | ||||||
|                 DataType(BaseDataType.POINTER, dt.base, null) |  | ||||||
|             else |  | ||||||
|                 DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr) |  | ||||||
|         fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType) |  | ||||||
|         fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier) |  | ||||||
|         fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type) |  | ||||||
|         fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     fun elementToArray(splitwords: Boolean = true): DataType { |  | ||||||
|         return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords) |  | ||||||
|         else arrayFor(base, false) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun elementType(): DataType = |  | ||||||
|         when { |  | ||||||
|             isPointerArray -> DataType(BaseDataType.POINTER, sub, subType) |  | ||||||
|             base.isArray || base==BaseDataType.STR -> forDt(sub!!) |  | ||||||
|             else -> throw IllegalArgumentException("not an array") |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     fun typeForAddressOf(msb: Boolean): DataType { |  | ||||||
|         if (isUndefined) |  | ||||||
|             return if(msb) pointer(BaseDataType.UBYTE) else UWORD |  | ||||||
|         else { |  | ||||||
|             if (isBasic) |  | ||||||
|                 return pointer(base) |  | ||||||
|             if (isString) |  | ||||||
|                 return pointer(BaseDataType.UBYTE) |  | ||||||
|             if (isPointer) |  | ||||||
|                 return UWORD |  | ||||||
|             if (isArray) { |  | ||||||
|                 if (msb || isSplitWordArray) |  | ||||||
|                     return pointer(BaseDataType.UBYTE) |  | ||||||
|                 val elementDt = elementType() |  | ||||||
|                 require(elementDt.isBasic) |  | ||||||
|                 return pointer(elementDt) |  | ||||||
|             } |  | ||||||
|             if (subType != null) |  | ||||||
|                 return pointer(this) |  | ||||||
|             return UWORD |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun dereference(): DataType { |  | ||||||
|         require(isPointer || isUnsignedWord) |  | ||||||
|         return when { |  | ||||||
|             isUnsignedWord -> forDt(BaseDataType.UBYTE) |  | ||||||
|             sub!=null -> forDt(sub) |  | ||||||
|             subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType) |  | ||||||
|             subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr) |  | ||||||
|             else -> throw IllegalArgumentException("cannot dereference this pointer type") |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun toString(): String = when(base) { |  | ||||||
|         BaseDataType.ARRAY -> { |  | ||||||
|             when(sub) { |  | ||||||
|                 BaseDataType.BOOL -> "bool[]" |  | ||||||
|                 BaseDataType.FLOAT -> "float[]" |  | ||||||
|                 BaseDataType.BYTE -> "byte[]" |  | ||||||
|                 BaseDataType.WORD -> "word[]" |  | ||||||
|                 BaseDataType.UBYTE -> "ubyte[]" |  | ||||||
|                 BaseDataType.UWORD -> "uword[]" |  | ||||||
|                 BaseDataType.LONG -> "long[]" |  | ||||||
|                 else -> throw IllegalArgumentException("invalid sub type") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         BaseDataType.ARRAY_SPLITW -> { |  | ||||||
|             when(sub) { |  | ||||||
|                 BaseDataType.WORD -> "word[] (split)" |  | ||||||
|                 BaseDataType.UWORD -> "uword[] (split)" |  | ||||||
|                 else -> throw IllegalArgumentException("invalid sub type") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         BaseDataType.POINTER -> { |  | ||||||
|             if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}" |  | ||||||
|         } |  | ||||||
|         BaseDataType.ARRAY_POINTER -> { |  | ||||||
|             if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)" |  | ||||||
|         } |  | ||||||
|         BaseDataType.STRUCT_INSTANCE -> { |  | ||||||
|             sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr" |  | ||||||
|         } |  | ||||||
|         else -> base.name.lowercase() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun sourceString(): String = when (base) { |  | ||||||
|         BaseDataType.BOOL -> "bool" |  | ||||||
|         BaseDataType.UBYTE -> "ubyte" |  | ||||||
|         BaseDataType.BYTE -> "byte" |  | ||||||
|         BaseDataType.UWORD -> "uword" |  | ||||||
|         BaseDataType.WORD -> "word" |  | ||||||
|         BaseDataType.LONG -> "long" |  | ||||||
|         BaseDataType.FLOAT -> "float" |  | ||||||
|         BaseDataType.STR -> "str" |  | ||||||
|         BaseDataType.POINTER -> { |  | ||||||
|             when { |  | ||||||
|                 sub!=null -> "^^${sub.name.lowercase()}" |  | ||||||
|                 subType!=null -> "^^${subType!!.scopedNameString}" |  | ||||||
|                 subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}" |  | ||||||
|                 else -> "?????" |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         BaseDataType.STRUCT_INSTANCE -> { |  | ||||||
|             when { |  | ||||||
|                 sub!=null -> sub.name.lowercase() |  | ||||||
|                 subType!=null -> subType!!.scopedNameString |  | ||||||
|                 subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".") |  | ||||||
|                 else -> "?????" |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         BaseDataType.ARRAY_POINTER -> { |  | ||||||
|             when { |  | ||||||
|                 sub!=null -> "^^${sub.name.lowercase()}[" |  | ||||||
|                 subType!=null -> "^^${subType!!.scopedNameString}[" |  | ||||||
|                 subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}[" |  | ||||||
|                 else -> "????? [" |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         BaseDataType.ARRAY -> { |  | ||||||
|             when(sub) { |  | ||||||
|                 BaseDataType.UBYTE -> "ubyte[" |  | ||||||
|                 BaseDataType.UWORD -> "@nosplit uword[" |  | ||||||
|                 BaseDataType.BOOL -> "bool[" |  | ||||||
|                 BaseDataType.BYTE -> "byte[" |  | ||||||
|                 BaseDataType.WORD -> "@nosplit word[" |  | ||||||
|                 BaseDataType.LONG -> "long[" |  | ||||||
|                 BaseDataType.FLOAT -> "float[" |  | ||||||
|                 else -> throw IllegalArgumentException("invalid sub type") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         BaseDataType.ARRAY_SPLITW -> { |  | ||||||
|             when(sub) { |  | ||||||
|                 BaseDataType.UWORD -> "uword[" |  | ||||||
|                 BaseDataType.WORD -> "word[" |  | ||||||
|                 else -> throw IllegalArgumentException("invalid sub type") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // is the type assignable to the given other type (perhaps via a typecast) without loss of precision? |  | ||||||
|     infix fun isAssignableTo(targetType: DataType) = |  | ||||||
|         when(base) { |  | ||||||
|             BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL |  | ||||||
|             BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT) |  | ||||||
|             BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) |  | ||||||
|             BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER) |  | ||||||
|             BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) |  | ||||||
|             BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT) |  | ||||||
|             BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT) |  | ||||||
|             BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE) |  | ||||||
|             BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub |  | ||||||
|             BaseDataType.POINTER -> { |  | ||||||
|                 when { |  | ||||||
|                     targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true |  | ||||||
|                     targetType.isPointer -> this.isUnsignedWord || this == targetType |  | ||||||
|                     else -> false |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             BaseDataType.STRUCT_INSTANCE -> false        // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it) |  | ||||||
|             BaseDataType.ARRAY_POINTER -> false |  | ||||||
|             BaseDataType.UNDEFINED -> false |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base) |  | ||||||
|     fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base) |  | ||||||
|  |  | ||||||
|     // note: for pointer types, size() doesn't return the size of the pointer itself but the size of the thing it points to |  | ||||||
|     fun size(memsizer: IMemSizer): Int = if(sub!=null) { |  | ||||||
|             memsizer.memorySize(sub) |  | ||||||
|         } else if(subType!=null) { |  | ||||||
|             subType!!.memsize(memsizer) |  | ||||||
|         } else { |  | ||||||
|             memsizer.memorySize(base) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     val isBasic = sub==null && subType==null && subTypeFromAntlr==null |  | ||||||
|     val isUndefined = base == BaseDataType.UNDEFINED |  | ||||||
|     val isByte = base.isByte |  | ||||||
|     val isUnsignedByte = base == BaseDataType.UBYTE |  | ||||||
|     val isSignedByte = base == BaseDataType.BYTE |  | ||||||
|     val isByteOrBool = base.isByteOrBool |  | ||||||
|     val isWord = base.isWord |  | ||||||
|     val isUnsignedWord =  base == BaseDataType.UWORD |  | ||||||
|     val isSignedWord =  base == BaseDataType.WORD |  | ||||||
|     val isInteger = base.isInteger |  | ||||||
|     val isWordOrByteOrBool = base.isWordOrByteOrBool |  | ||||||
|     val isIntegerOrBool = base.isIntegerOrBool |  | ||||||
|     val isNumeric = base.isNumeric |  | ||||||
|     val isNumericOrBool = base.isNumericOrBool |  | ||||||
|     val isSigned = base.isSigned |  | ||||||
|     val isUnsigned = !base.isSigned |  | ||||||
|     val isArray = base.isArray |  | ||||||
|     val isPointer = base.isPointer |  | ||||||
|     val isPointerToByte = base.isPointer && sub?.isByteOrBool==true |  | ||||||
|     val isPointerToWord = base.isPointer && sub?.isWord==true |  | ||||||
|     val isStructInstance = base.isStructInstance |  | ||||||
|     val isPointerArray = base.isPointerArray |  | ||||||
|     val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL |  | ||||||
|     val isByteArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE) |  | ||||||
|     val isUnsignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UBYTE |  | ||||||
|     val isSignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BYTE |  | ||||||
|     val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD) |  | ||||||
|     val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD |  | ||||||
|     val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD |  | ||||||
|     val isLongArray = base.isArray && sub == BaseDataType.LONG |  | ||||||
|     val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT |  | ||||||
|     val isString = base == BaseDataType.STR |  | ||||||
|     val isBool = base == BaseDataType.BOOL |  | ||||||
|     val isFloat = base == BaseDataType.FLOAT |  | ||||||
|     val isLong = base == BaseDataType.LONG |  | ||||||
|     val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)) |  | ||||||
|     val isSplitWordArray = base.isSplitWordArray |  | ||||||
|     val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD |  | ||||||
|     val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD |  | ||||||
|     val isIterable =  base.isIterable |  | ||||||
|     val isPassByRef = base.isPassByRef |  | ||||||
|     val isPassByValue = base.isPassByValue |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| enum class CpuRegister { | enum class CpuRegister { | ||||||
|     A, |     A, | ||||||
|     X, |     X, | ||||||
| @@ -400,31 +70,10 @@ enum class RegisterOrPair { | |||||||
|     FAC2, |     FAC2, | ||||||
|     // cx16 virtual registers: |     // cx16 virtual registers: | ||||||
|     R0, R1, R2, R3, R4, R5, R6, R7, |     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 { |     companion object { | ||||||
|         val names by lazy { entries.map { it.toString()} } |         val names by lazy { values().map { it.toString()} } | ||||||
|         fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair { |  | ||||||
|             return when(cpu) { |  | ||||||
|                 CpuRegister.A -> A |  | ||||||
|                 CpuRegister.X -> X |  | ||||||
|                 CpuRegister.Y -> Y |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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) { |     fun asCpuRegister(): CpuRegister = when(this) { | ||||||
| @@ -434,21 +83,6 @@ enum class RegisterOrPair { | |||||||
|         else -> throw IllegalArgumentException("no cpu hardware register for $this") |         else -> throw IllegalArgumentException("no cpu hardware register for $this") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun asScopedNameVirtualReg(type: DataType?): List<String> { |  | ||||||
|         require(this in Cx16VirtualRegisters) |  | ||||||
|         val suffix = when(type?.base) { |  | ||||||
|             BaseDataType.UBYTE, BaseDataType.BOOL -> "L" |  | ||||||
|             BaseDataType.BYTE -> "sL" |  | ||||||
|             BaseDataType.WORD -> "s" |  | ||||||
|             BaseDataType.UWORD, BaseDataType.POINTER, null -> "" |  | ||||||
|             else -> throw IllegalArgumentException("invalid register param type for cx16 virtual reg") |  | ||||||
|         } |  | ||||||
|         return listOf("cx16", name.lowercase()+suffix) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters |  | ||||||
|     fun isLong() = this in combinedLongRegisters |  | ||||||
|  |  | ||||||
| }       // only used in parameter and return value specs in asm subroutines | }       // only used in parameter and return value specs in asm subroutines | ||||||
|  |  | ||||||
| enum class Statusflag { | enum class Statusflag { | ||||||
| @@ -458,7 +92,7 @@ enum class Statusflag { | |||||||
|     Pn;     // don't use |     Pn;     // don't use | ||||||
|  |  | ||||||
|     companion object { |     companion object { | ||||||
|         val names by lazy { entries.map { it.toString()} } |         val names by lazy { values().map { it.toString()} } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -474,9 +108,44 @@ enum class BranchCondition { | |||||||
|     PL,     // PL == POS |     PL,     // PL == POS | ||||||
|     POS, |     POS, | ||||||
|     VS, |     VS, | ||||||
|     VC |     VC, | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL) | ||||||
|  | val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD) | ||||||
|  | val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL) | ||||||
|  | val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD) | ||||||
|  | val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL) | ||||||
|  | val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT) | ||||||
|  | val SignedDatatypes =  arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT) | ||||||
|  | val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL) | ||||||
|  | val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD) | ||||||
|  | val IterableDatatypes = arrayOf( | ||||||
|  |     DataType.STR, | ||||||
|  |     DataType.ARRAY_UB, DataType.ARRAY_B, | ||||||
|  |     DataType.ARRAY_UW, DataType.ARRAY_W, | ||||||
|  |     DataType.ARRAY_F, DataType.ARRAY_BOOL | ||||||
|  | ) | ||||||
|  | val PassByValueDatatypes = NumericDatatypes | ||||||
|  | val PassByReferenceDatatypes = IterableDatatypes | ||||||
|  | val ArrayToElementTypes = mapOf( | ||||||
|  |     DataType.STR to DataType.UBYTE, | ||||||
|  |     DataType.ARRAY_B to DataType.BYTE, | ||||||
|  |     DataType.ARRAY_UB to DataType.UBYTE, | ||||||
|  |     DataType.ARRAY_W to DataType.WORD, | ||||||
|  |     DataType.ARRAY_UW to DataType.UWORD, | ||||||
|  |     DataType.ARRAY_F to DataType.FLOAT, | ||||||
|  |     DataType.ARRAY_BOOL to DataType.BOOL | ||||||
|  | ) | ||||||
|  | val ElementToArrayTypes = mapOf( | ||||||
|  |     DataType.BYTE to DataType.ARRAY_B, | ||||||
|  |     DataType.UBYTE to DataType.ARRAY_UB, | ||||||
|  |     DataType.WORD to DataType.ARRAY_W, | ||||||
|  |     DataType.UWORD to DataType.ARRAY_UW, | ||||||
|  |     DataType.FLOAT to DataType.ARRAY_F, | ||||||
|  |     DataType.BOOL to DataType.ARRAY_BOOL | ||||||
|  | ) | ||||||
| val Cx16VirtualRegisters = arrayOf( | val Cx16VirtualRegisters = arrayOf( | ||||||
|     RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3, |     RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3, | ||||||
|     RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7, |     RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7, | ||||||
| @@ -484,28 +153,12 @@ val Cx16VirtualRegisters = arrayOf( | |||||||
|     RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15 |     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 |  | ||||||
| ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| enum class OutputType { | enum class OutputType { | ||||||
|     RAW, |     RAW, | ||||||
|     PRG, |     PRG, | ||||||
|     XEX, |     XEX | ||||||
|     LIBRARY |  | ||||||
| } | } | ||||||
|  |  | ||||||
| enum class CbmPrgLauncherType { | enum class CbmPrgLauncherType { | ||||||
| @@ -527,8 +180,3 @@ enum class ZeropageWish { | |||||||
|     DONTCARE, |     DONTCARE, | ||||||
|     NOT_IN_ZEROPAGE |     NOT_IN_ZEROPAGE | ||||||
| } | } | ||||||
|  |  | ||||||
| enum class SplitWish { |  | ||||||
|     DONTCARE, |  | ||||||
|     NOSPLIT |  | ||||||
| } |  | ||||||
| @@ -1,15 +1,13 @@ | |||||||
| package prog8.code | package prog8.code.core | ||||||
| 
 | 
 | ||||||
|  | import prog8.code.SymbolTable | ||||||
| import prog8.code.ast.PtProgram | import prog8.code.ast.PtProgram | ||||||
| import prog8.code.core.CompilationOptions |  | ||||||
| import prog8.code.core.IErrorReporter |  | ||||||
| 
 | 
 | ||||||
| interface ICodeGeneratorBackend { | interface ICodeGeneratorBackend { | ||||||
|     fun generate(program: PtProgram, |     fun generate(program: PtProgram, | ||||||
|                  symbolTable: SymbolTable, |                  symbolTable: SymbolTable, | ||||||
|                  options: CompilationOptions, |                  options: CompilationOptions, | ||||||
|                  errors: IErrorReporter |                  errors: IErrorReporter): IAssemblyProgram? | ||||||
|     ): IAssemblyProgram? |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -1,43 +1,10 @@ | |||||||
| package prog8.code.core | package prog8.code.core | ||||||
|  |  | ||||||
| import java.nio.file.Path |  | ||||||
|  |  | ||||||
| enum class CpuType { |  | ||||||
|     CPU6502, |  | ||||||
|     CPU65C02, |  | ||||||
|     VIRTUAL |  | ||||||
| } |  | ||||||
|  |  | ||||||
| interface ICompilationTarget: IStringEncoding, IMemSizer { | interface ICompilationTarget: IStringEncoding, IMemSizer { | ||||||
|     val name: String |     val name: String | ||||||
|  |     val machine: IMachineDefinition | ||||||
|     val FLOAT_MAX_NEGATIVE: Double |     val supportedEncodings: Set<Encoding> | ||||||
|     val FLOAT_MAX_POSITIVE: Double |     val defaultEncoding: Encoding | ||||||
|     val FLOAT_MEM_SIZE: Int |  | ||||||
|     val STARTUP_CODE_RESERVED_SIZE: UInt        // this is here, so that certain compiler targets are able to tune this |  | ||||||
|     val PROGRAM_LOAD_ADDRESS : UInt |  | ||||||
|     val PROGRAM_MEMTOP_ADDRESS: UInt |  | ||||||
|     val BSSHIGHRAM_START: UInt |  | ||||||
|     val BSSHIGHRAM_END: UInt |  | ||||||
|     val BSSGOLDENRAM_START: UInt |  | ||||||
|     val BSSGOLDENRAM_END: UInt |  | ||||||
|  |  | ||||||
|     val cpu: CpuType |  | ||||||
|     var zeropage: Zeropage |  | ||||||
|     var golden: GoldenRam |  | ||||||
|     val libraryPath: Path? |  | ||||||
|     val customLauncher: List<String> |  | ||||||
|     val additionalAssemblerOptions: List<String> |  | ||||||
|     val defaultOutputType: OutputType |  | ||||||
|  |  | ||||||
|     fun initializeMemoryAreas(compilerOptions: CompilationOptions) |  | ||||||
|     fun getFloatAsmBytes(num: Number): String |  | ||||||
|  |  | ||||||
|     fun convertFloatToBytes(num: Double): List<UByte> |  | ||||||
|     fun convertBytesToFloat(bytes: List<UByte>): Double |  | ||||||
|  |  | ||||||
|     fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) |  | ||||||
|     fun isIOAddress(address: UInt): Boolean |  | ||||||
|  |  | ||||||
|     override fun encodeString(str: String, encoding: Encoding): List<UByte> |     override fun encodeString(str: String, encoding: Encoding): List<UByte> | ||||||
|     override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String |     override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String | ||||||
|   | |||||||
| @@ -3,16 +3,10 @@ package prog8.code.core | |||||||
| interface IErrorReporter { | interface IErrorReporter { | ||||||
|     fun err(msg: String, position: Position) |     fun err(msg: String, position: Position) | ||||||
|     fun warn(msg: String, position: Position) |     fun warn(msg: String, position: Position) | ||||||
|     fun info(msg: String, position: Position) |  | ||||||
|     fun undefined(symbol: List<String>, position: Position) |  | ||||||
|     fun noErrors(): Boolean |     fun noErrors(): Boolean | ||||||
|     fun report() |     fun report() | ||||||
|     fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) { |     fun finalizeNumErrors(numErrors: Int, numWarnings: Int) { | ||||||
|         if(numErrors>0) |         if(numErrors>0) | ||||||
|             throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.") |             throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.") | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun noErrorForLine(position: Position): Boolean |  | ||||||
|  |  | ||||||
|     fun printSingleError(errormessage: String) |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								codeCore/src/prog8/code/core/IMachineDefinition.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								codeCore/src/prog8/code/core/IMachineDefinition.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | package prog8.code.core | ||||||
|  |  | ||||||
|  | import java.nio.file.Path | ||||||
|  |  | ||||||
|  |  | ||||||
|  | enum class CpuType { | ||||||
|  |     CPU6502, | ||||||
|  |     CPU65c02, | ||||||
|  |     VIRTUAL | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface IMachineDefinition { | ||||||
|  |     val FLOAT_MAX_NEGATIVE: Double | ||||||
|  |     val FLOAT_MAX_POSITIVE: Double | ||||||
|  |     val FLOAT_MEM_SIZE: Int | ||||||
|  |     var ESTACK_LO: UInt | ||||||
|  |     var ESTACK_HI: UInt | ||||||
|  |     val PROGRAM_LOAD_ADDRESS : UInt | ||||||
|  |     val BSSHIGHRAM_START: UInt | ||||||
|  |     val BSSHIGHRAM_END: UInt | ||||||
|  |  | ||||||
|  |     val cpu: CpuType | ||||||
|  |     var zeropage: Zeropage | ||||||
|  |     var golden: GoldenRam | ||||||
|  |  | ||||||
|  |     fun initializeMemoryAreas(compilerOptions: CompilationOptions) | ||||||
|  |     fun getFloatAsmBytes(num: Number): String | ||||||
|  |  | ||||||
|  |     fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> | ||||||
|  |     fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) | ||||||
|  |     fun isIOAddress(address: UInt): Boolean | ||||||
|  |     fun overrideEvalStack(evalStackBaseAddress: UInt) { | ||||||
|  |         require(evalStackBaseAddress and 255u == 0u) | ||||||
|  |         ESTACK_LO = evalStackBaseAddress | ||||||
|  |         ESTACK_HI = evalStackBaseAddress + 256u | ||||||
|  |         require(ESTACK_LO !in golden.region && ESTACK_HI !in golden.region) { "user-set ESTACK can't be in GOLDEN ram" } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,15 +1,6 @@ | |||||||
| package prog8.code.core | package prog8.code.core | ||||||
|  |  | ||||||
| interface IMemSizer { | interface IMemSizer { | ||||||
|     fun memorySize(dt: DataType, numElements: Int?): Int |     fun memorySize(dt: DataType): Int | ||||||
|  |     fun memorySize(arrayDt: DataType, numElements: Int): Int | ||||||
|     fun memorySize(dt: BaseDataType): Int { |  | ||||||
|         if(dt.isPassByRef) |  | ||||||
|             return memorySize(DataType.UWORD, null)      // a pointer size |  | ||||||
|         try { |  | ||||||
|             return memorySize(DataType.forDt(dt), null) |  | ||||||
|         } catch (x: NoSuchElementException) { |  | ||||||
|             throw IllegalArgumentException(x.message) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,17 +5,10 @@ enum class Encoding(val prefix: String) { | |||||||
|     PETSCII("petscii"),         // c64/c128/cx16 |     PETSCII("petscii"),         // c64/c128/cx16 | ||||||
|     SCREENCODES("sc"),          // c64/c128/cx16 |     SCREENCODES("sc"),          // c64/c128/cx16 | ||||||
|     ATASCII("atascii"),         // atari |     ATASCII("atascii"),         // atari | ||||||
|     ISO("iso"),                 // cx16  (iso-8859-15) |     ISO("iso")                  // cx16 | ||||||
|     ISO5("iso5"),               // cx16  (iso-8859-5, cyrillic) |  | ||||||
|     ISO16("iso16"),             // cx16  (iso-8859-16, eastern european) |  | ||||||
|     CP437("cp437"),             // cx16  (ibm pc, codepage 437) |  | ||||||
|     KATAKANA("kata"),           // cx16  (katakana) |  | ||||||
|     C64OS("c64os")              // c64 (C64 OS) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| interface IStringEncoding { | interface IStringEncoding { | ||||||
|     val defaultEncoding: Encoding |  | ||||||
|  |  | ||||||
|     fun encodeString(str: String, encoding: Encoding): List<UByte> |     fun encodeString(str: String, encoding: Encoding): List<UByte> | ||||||
|     fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String |     fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,10 +22,9 @@ abstract class MemoryAllocator(protected val options: CompilationOptions) { | |||||||
| abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | ||||||
|  |  | ||||||
|     abstract val SCRATCH_B1 : UInt      // temp storage for a single byte |     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_REG : UInt     // temp storage for a register, must be B1+1 | ||||||
|     abstract val SCRATCH_W1 : UInt      // temp storage 1 for a word |     abstract val SCRATCH_W1 : UInt      // temp storage 1 for a word  $fb+$fc | ||||||
|     abstract val SCRATCH_W2 : UInt      // temp storage 2 for a word |     abstract val SCRATCH_W2 : UInt      // temp storage 2 for a word  $fb+$fc | ||||||
|     abstract val SCRATCH_PTR : UInt     // temp storage for a pointer |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // the variables allocated into Zeropage. |     // the variables allocated into Zeropage. | ||||||
| @@ -39,14 +38,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | |||||||
|             for (reserved in options.zpReserved) |             for (reserved in options.zpReserved) | ||||||
|                 reserve(reserved) |                 reserve(reserved) | ||||||
|  |  | ||||||
|             free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u)) |             free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u)) | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun retainAllowed() { |  | ||||||
|         synchronized(this) { |  | ||||||
|             for(allowed in options.zpAllowed) |  | ||||||
|                 free.retainAll { it in allowed } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -71,10 +63,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | |||||||
|             return Err(MemAllocationError("zero page usage has been disabled")) |             return Err(MemAllocationError("zero page usage has been disabled")) | ||||||
|  |  | ||||||
|         val size: Int = |         val size: Int = | ||||||
|                 when { |                 when (datatype) { | ||||||
|                     datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null) |                     in IntegerDatatypes -> options.compTarget.memorySize(datatype) | ||||||
|                     datatype.isPointer -> options.compTarget.memorySize(datatype, null) |                     DataType.STR, in ArrayDatatypes  -> { | ||||||
|                     datatype.isString || datatype.isArray -> { |  | ||||||
|                         val memsize = options.compTarget.memorySize(datatype, numElements!!) |                         val memsize = options.compTarget.memorySize(datatype, numElements!!) | ||||||
|                         if(position!=null) |                         if(position!=null) | ||||||
|                             errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position) |                             errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position) | ||||||
| @@ -82,9 +73,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | |||||||
|                             errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY) |                             errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY) | ||||||
|                         memsize |                         memsize | ||||||
|                     } |                     } | ||||||
|                     datatype.isFloat -> { |                     DataType.FLOAT -> { | ||||||
|                         if (options.floats) { |                         if (options.floats) { | ||||||
|                             val memsize = options.compTarget.memorySize(DataType.FLOAT, null) |                             val memsize = options.compTarget.memorySize(DataType.FLOAT) | ||||||
|                             if(position!=null) |                             if(position!=null) | ||||||
|                                 errors.warn("allocating a large value in zeropage; float $memsize bytes", position) |                                 errors.warn("allocating a large value in zeropage; float $memsize bytes", position) | ||||||
|                             else |                             else | ||||||
| @@ -96,7 +87,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|         synchronized(this) { |         synchronized(this) { | ||||||
|             if(free.isNotEmpty()) { |             if(free.size > 0) { | ||||||
|                 if(size==1) { |                 if(size==1) { | ||||||
|                     for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) { |                     for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) { | ||||||
|                         if(oneSeparateByteFree(candidate)) |                         if(oneSeparateByteFree(candidate)) | ||||||
| @@ -120,11 +111,10 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | |||||||
|         require(size>=0) |         require(size>=0) | ||||||
|         free.removeAll(address until address+size.toUInt()) |         free.removeAll(address until address+size.toUInt()) | ||||||
|         if(name.isNotEmpty()) { |         if(name.isNotEmpty()) { | ||||||
|             allocatedVariables[name] = when { |             allocatedVariables[name] = when(datatype) { | ||||||
|                 datatype.isNumericOrBool -> VarAllocation(address, datatype, size)        // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments |                 in NumericDatatypes -> VarAllocation(address, datatype, size)        // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments | ||||||
|                 datatype.isString -> VarAllocation(address, datatype, size) |                 DataType.STR -> VarAllocation(address, datatype, size) | ||||||
|                 datatype.isArray -> VarAllocation(address, datatype, size) |                 in ArrayDatatypes -> VarAllocation(address, datatype, size) | ||||||
|                 datatype.isPointer -> VarAllocation(address, datatype, size) |  | ||||||
|                 else -> throw AssemblyError("invalid dt") |                 else -> throw AssemblyError("invalid dt") | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -136,6 +126,8 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { | |||||||
|         require(size>0) |         require(size>0) | ||||||
|         return free.containsAll((address until address+size.toUInt()).toList()) |         return free.containsAll((address until address+size.toUInt()).toList()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     abstract fun allocateCx16VirtualRegisters() | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -151,13 +143,14 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc | |||||||
|         errors: IErrorReporter): Result<VarAllocation, MemAllocationError> { |         errors: IErrorReporter): Result<VarAllocation, MemAllocationError> { | ||||||
|  |  | ||||||
|         val size: Int = |         val size: Int = | ||||||
|             when { |             when (datatype) { | ||||||
|                 datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null) |                 in IntegerDatatypes -> options.compTarget.memorySize(datatype) | ||||||
|                 datatype.isString -> numElements!! |                 DataType.STR, in ArrayDatatypes  -> { | ||||||
|                 datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!) |                     options.compTarget.memorySize(datatype, numElements!!) | ||||||
|                 datatype.isFloat -> { |                 } | ||||||
|  |                 DataType.FLOAT -> { | ||||||
|                     if (options.floats) { |                     if (options.floats) { | ||||||
|                         options.compTarget.memorySize(DataType.FLOAT, null) |                         options.compTarget.memorySize(DataType.FLOAT) | ||||||
|                     } else return Err(MemAllocationError("floating point option not enabled")) |                     } else return Err(MemAllocationError("floating point option not enabled")) | ||||||
|                 } |                 } | ||||||
|                 else -> throw MemAllocationError("weird dt") |                 else -> throw MemAllocationError("weird dt") | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
| package prog8.code.core | package prog8.code.core | ||||||
|  |  | ||||||
| val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor")       // note: and,or are not associative because of Shortcircuit/McCarthy evaluation | val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=") | ||||||
| val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=") | val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") | ||||||
| val LogicalOperators = arrayOf("and", "or", "xor", "not", "in") | val LogicalOperators = setOf("and", "or", "xor", "not") | ||||||
| val BitwiseOperators = arrayOf("&", "|", "^", "~") | val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor") | ||||||
| val PrefixOperators = arrayOf("+", "-", "~", "not") | val BitwiseOperators = setOf("&", "|", "^", "~") | ||||||
|  | val PrefixOperators = setOf("+", "-", "~", "not") | ||||||
|  | // val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators | ||||||
|  |  | ||||||
| fun invertedComparisonOperator(operator: String) = | fun invertedComparisonOperator(operator: String) = | ||||||
|     when (operator) { |     when (operator) { | ||||||
|   | |||||||
| @@ -1,21 +1,21 @@ | |||||||
| package prog8.code.core | package prog8.code.core | ||||||
|  |  | ||||||
| import prog8.code.sanitize | import prog8.code.core.SourceCode.Companion.libraryFilePrefix | ||||||
| import prog8.code.source.SourceCode |  | ||||||
| import java.nio.file.InvalidPathException | import java.nio.file.InvalidPathException | ||||||
| import kotlin.io.path.Path | import kotlin.io.path.Path | ||||||
|  | import kotlin.io.path.absolute | ||||||
|  |  | ||||||
| data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) { | data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) { | ||||||
|     override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]" |     override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]" | ||||||
|     fun toClickableStr(): String { |     fun toClickableStr(): String { | ||||||
|         if(this===DUMMY) |         if(this===DUMMY) | ||||||
|             return "" |             return "" | ||||||
|         if(SourceCode.isLibraryResource(file)) |         if(file.startsWith(libraryFilePrefix)) | ||||||
|             return "$file:$line:$startCol:" |             return "$file:$line:$startCol:" | ||||||
|         return try { |         return try { | ||||||
|             val path = Path(file).sanitize().toString() |             val path = Path(file).absolute().normalize().toString() | ||||||
|             "file://$path:$line:$startCol:" |             "file://$path:$line:$startCol:" | ||||||
|         } catch(_: InvalidPathException) { |         } catch(x: InvalidPathException) { | ||||||
|             // this can occur on Windows when the source origin contains "invalid" characters such as ':' |             // this can occur on Windows when the source origin contains "invalid" characters such as ':' | ||||||
|             "file://$file:$line:$startCol:" |             "file://$file:$line:$startCol:" | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,13 +1,15 @@ | |||||||
| package prog8.code.source | package prog8.code.core | ||||||
| 
 | 
 | ||||||
| import prog8.code.sanitize | import java.io.File | ||||||
| import java.io.IOException | import java.io.IOException | ||||||
| import java.nio.file.Path | import java.nio.file.Path | ||||||
| import java.text.Normalizer |  | ||||||
| import kotlin.io.path.Path | import kotlin.io.path.Path | ||||||
| import kotlin.io.path.absolute |  | ||||||
| import kotlin.io.path.readText | import kotlin.io.path.readText | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | const val internedStringsModuleName = "prog8_interned_strings" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Encapsulates - and ties together - actual source code (=text) and its [origin]. |  * Encapsulates - and ties together - actual source code (=text) and its [origin]. | ||||||
|  */ |  */ | ||||||
| @@ -23,11 +25,6 @@ sealed class SourceCode { | |||||||
|      */ |      */ | ||||||
|     abstract val isFromFilesystem: Boolean |     abstract val isFromFilesystem: Boolean | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Whether this [SourceCode] instance was created from a library module file |  | ||||||
|      */ |  | ||||||
|     abstract val isFromLibrary: Boolean |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * The logical name of the source code unit. Usually the module's name. |      * The logical name of the source code unit. Usually the module's name. | ||||||
|      */ |      */ | ||||||
| @@ -57,46 +54,34 @@ sealed class SourceCode { | |||||||
|         /** |         /** | ||||||
|          * filename prefix to designate library files that will be retreived from internal resources rather than disk |          * filename prefix to designate library files that will be retreived from internal resources rather than disk | ||||||
|          */ |          */ | ||||||
|         private const val LIBRARYFILEPREFIX = "library:" |         const val libraryFilePrefix = "library:" | ||||||
|         private const val STRINGSOURCEPREFIX = "string:" |         const val stringSourcePrefix = "string:" | ||||||
|         val curdir: Path = Path(".").absolute() |         val curdir: Path = Path(".").toAbsolutePath() | ||||||
|         fun relative(path: Path): Path = curdir.relativize(path.sanitize()) |         fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath()) | ||||||
|         fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString) |         fun isRegularFilesystemPath(pathString: String) = | ||||||
|         fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX) |             !(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix)) | ||||||
|         fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX) |  | ||||||
|         fun withoutPrefix(path: String): String { |  | ||||||
|             return if(isLibraryResource(path)) |  | ||||||
|                 path.removePrefix(LIBRARYFILEPREFIX) |  | ||||||
|             else if(isStringResource(path)) |  | ||||||
|                 path.removePrefix(STRINGSOURCEPREFIX) |  | ||||||
|             else |  | ||||||
|                 path |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Turn a plain String into a [SourceCode] object. |      * Turn a plain String into a [SourceCode] object. | ||||||
|      * [origin] will be something like `string:44c56085`. |      * [origin] will be something like `string:44c56085`. | ||||||
|      */ |      */ | ||||||
|     class Text(origText: String): SourceCode() { |     class Text(override val text: String): SourceCode() { | ||||||
|         override val text = origText.replace("\\R".toRegex(), "\n")      // normalize line endings |  | ||||||
|         override val isFromResources = false |         override val isFromResources = false | ||||||
|         override val isFromFilesystem = false |         override val isFromFilesystem = false | ||||||
|         override val isFromLibrary = false |         override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}" | ||||||
|         override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}" |  | ||||||
|         override val name = "<unnamed-text>" |         override val name = "<unnamed-text>" | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Get [SourceCode] from the file represented by the specified Path. |      * Get [SourceCode] from the file represented by the specified Path. | ||||||
|      * This immediately reads the file fully into memory. |      * This immediately reads the file fully into memory. | ||||||
|      * You can only get an instance of this via the ImportFileSystem object. |  | ||||||
|      * |      * | ||||||
|      * [origin] will be the given path in absolute and normalized form. |      * [origin] will be the given path in absolute and normalized form. | ||||||
|      * @throws NoSuchFileException if the file does not exist |      * @throws NoSuchFileException if the file does not exist | ||||||
|      * @throws FileSystemException if the file cannot be read |      * @throws FileSystemException if the file cannot be read | ||||||
|      */ |      */ | ||||||
|     internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() { |     class File(path: Path): SourceCode() { | ||||||
|         override val text: String |         override val text: String | ||||||
|         override val origin: String |         override val origin: String | ||||||
|         override val name: String |         override val name: String | ||||||
| @@ -107,8 +92,7 @@ sealed class SourceCode { | |||||||
|             val normalized = path.normalize() |             val normalized = path.normalize() | ||||||
|             origin = relative(normalized).toString() |             origin = relative(normalized).toString() | ||||||
|             try { |             try { | ||||||
|                 val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC) |                 text = normalized.readText() | ||||||
|                 text = contents.replace("\\R".toRegex(), "\n")      // normalize line endings |  | ||||||
|                 name = normalized.toFile().nameWithoutExtension |                 name = normalized.toFile().nameWithoutExtension | ||||||
|             } catch (nfx: java.nio.file.NoSuchFileException) { |             } catch (nfx: java.nio.file.NoSuchFileException) { | ||||||
|                 throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) } |                 throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) } | ||||||
| @@ -120,15 +104,13 @@ sealed class SourceCode { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8" |      * [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8" | ||||||
|      * You can only get an instance of this via the ImportFileSystem object. |  | ||||||
|      */ |      */ | ||||||
|     internal class Resource(pathString: String): SourceCode() { |     class Resource(pathString: String): SourceCode() { | ||||||
|         private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/") |         private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/") | ||||||
| 
 | 
 | ||||||
|         override val isFromResources = true |         override val isFromResources = true | ||||||
|         override val isFromFilesystem = false |         override val isFromFilesystem = false | ||||||
|         override val isFromLibrary = true |         override val origin = "$libraryFilePrefix$normalized" | ||||||
|         override val origin = "$LIBRARYFILEPREFIX$normalized" |  | ||||||
|         override val text: String |         override val text: String | ||||||
|         override val name: String |         override val name: String | ||||||
| 
 | 
 | ||||||
| @@ -137,13 +119,12 @@ sealed class SourceCode { | |||||||
|             if (rscURL == null) { |             if (rscURL == null) { | ||||||
|                 val rscRoot = object {}.javaClass.getResource("/") |                 val rscRoot = object {}.javaClass.getResource("/") | ||||||
|                 throw NoSuchFileException( |                 throw NoSuchFileException( | ||||||
|                     java.io.File(normalized), |                     File(normalized), | ||||||
|                     reason = "looked in resources rooted at $rscRoot" |                     reason = "looked in resources rooted at $rscRoot" | ||||||
|                 ) |                 ) | ||||||
|             } |             } | ||||||
|             val stream = object {}.javaClass.getResourceAsStream(normalized) |             val stream = object {}.javaClass.getResourceAsStream(normalized) | ||||||
|             val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) } |             text = stream!!.reader().use { it.readText() } | ||||||
|             text = contents.replace("\\R".toRegex(), "\n")      // normalize line endings |  | ||||||
|             name = Path(pathString).toFile().nameWithoutExtension |             name = Path(pathString).toFile().nameWithoutExtension | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -154,7 +135,6 @@ sealed class SourceCode { | |||||||
|     class Generated(override val name: String) : SourceCode() { |     class Generated(override val name: String) : SourceCode() { | ||||||
|         override val isFromResources: Boolean = false |         override val isFromResources: Boolean = false | ||||||
|         override val isFromFilesystem: Boolean = false |         override val isFromFilesystem: Boolean = false | ||||||
|         override val isFromLibrary: Boolean = false |  | ||||||
|         override val origin: String = name |         override val origin: String = name | ||||||
|         override val text: String = "<generated code node, no text representation>" |         override val text: String = "<generated code node, no text representation>" | ||||||
|     } |     } | ||||||
| @@ -1,74 +0,0 @@ | |||||||
| package prog8.code.source |  | ||||||
|  |  | ||||||
| import prog8.code.core.Position |  | ||||||
| import prog8.code.sanitize |  | ||||||
| import java.nio.file.Path |  | ||||||
| import java.util.* |  | ||||||
| import kotlin.io.path.Path |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Resource caching "filesystem". |  | ||||||
| // Note that it leaves the decision to load a resource or an actual disk file to the caller. |  | ||||||
|  |  | ||||||
| object ImportFileSystem { |  | ||||||
|  |  | ||||||
|     fun expandTilde(path: String): String = if (path.startsWith("~")) { |  | ||||||
|         val userHome = System.getProperty("user.home") |  | ||||||
|         userHome + path.drop(1) |  | ||||||
|     } else { |  | ||||||
|         path |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun expandTilde(path: Path): Path = Path(expandTilde(path.toString())) |  | ||||||
|  |  | ||||||
|     fun getFile(path: Path, isLibrary: Boolean=false): SourceCode { |  | ||||||
|         val normalized = path.sanitize() |  | ||||||
|         val cached = cache[normalized.toString()] |  | ||||||
|         if (cached != null) |  | ||||||
|             return cached |  | ||||||
|         val file = SourceCode.File(normalized, isLibrary) |  | ||||||
|         cache[normalized.toString()] = file |  | ||||||
|         return file |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun getResource(name: String): SourceCode { |  | ||||||
|         val cached = cache[name] |  | ||||||
|         if (cached != null) return cached |  | ||||||
|         val resource = SourceCode.Resource(name) |  | ||||||
|         cache[name] = resource |  | ||||||
|         return resource |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun retrieveSourceLine(position: Position): String { |  | ||||||
|         if(SourceCode.isLibraryResource(position.file)) { |  | ||||||
|             val cached = cache[SourceCode.withoutPrefix(position.file)] |  | ||||||
|             if(cached != null) |  | ||||||
|                 return getLine(cached, position.line) |  | ||||||
|         } |  | ||||||
|         val cached = cache[position.file] |  | ||||||
|         if(cached != null) |  | ||||||
|             return getLine(cached, position.line) |  | ||||||
|         val path = Path(position.file).sanitize() |  | ||||||
|         val cached2 = cache[path.toString()] |  | ||||||
|         if(cached2 != null) |  | ||||||
|             return getLine(cached2, position.line) |  | ||||||
|         throw NoSuchElementException("cannot get source line $position, with path $path") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private fun getLine(code: SourceCode, lineIndex: Int): String { |  | ||||||
|         var spans = lineSpanCache[code] |  | ||||||
|         if(spans==null) { |  | ||||||
|             val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first } |  | ||||||
|             val ends = lineSpans.drop(1) + code.text.length |  | ||||||
|             spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray() |  | ||||||
|             lineSpanCache[code] = spans |  | ||||||
|         } |  | ||||||
|         val span = spans[lineIndex - 1] |  | ||||||
|         return code.text.substring(span.start, span.end).trim() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private class LineSpan(val start: Int, val end: Int) |  | ||||||
|  |  | ||||||
|     private val cache = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER) |  | ||||||
|     private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>() |  | ||||||
| } |  | ||||||
							
								
								
									
										28
									
								
								codeCore/src/prog8/code/target/AtariTarget.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								codeCore/src/prog8/code/target/AtariTarget.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | package prog8.code.target | ||||||
|  |  | ||||||
|  | import prog8.code.core.* | ||||||
|  | import prog8.code.target.atari.AtariMachineDefinition | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer { | ||||||
|  |     override val name = NAME | ||||||
|  |     override val machine = AtariMachineDefinition() | ||||||
|  |     override val supportedEncodings = setOf(Encoding.ATASCII) | ||||||
|  |     override val defaultEncoding = Encoding.ATASCII | ||||||
|  |  | ||||||
|  |     companion object { | ||||||
|  |         const val NAME = "atari" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun memorySize(dt: DataType): Int { | ||||||
|  |         return when(dt) { | ||||||
|  |             in ByteDatatypes -> 1 | ||||||
|  |             in WordDatatypes, in PassByReferenceDatatypes -> 2 | ||||||
|  |             DataType.FLOAT -> machine.FLOAT_MEM_SIZE | ||||||
|  |             else -> Int.MIN_VALUE | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun memorySize(arrayDt: DataType, numElements: Int) = | ||||||
|  |         memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements | ||||||
|  | } | ||||||
| @@ -1,81 +1,20 @@ | |||||||
| package prog8.code.target | package prog8.code.target | ||||||
|  |  | ||||||
| import prog8.code.core.* | import prog8.code.core.Encoding | ||||||
| import prog8.code.target.encodings.Encoder | import prog8.code.core.ICompilationTarget | ||||||
| import prog8.code.target.zp.C128Zeropage | import prog8.code.core.IMemSizer | ||||||
| import java.nio.file.Path | import prog8.code.core.IStringEncoding | ||||||
|  | import prog8.code.target.c128.C128MachineDefinition | ||||||
|  | import prog8.code.target.cbm.CbmMemorySizer | ||||||
|  |  | ||||||
|  |  | ||||||
| class C128Target: ICompilationTarget, | class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { | ||||||
|     IStringEncoding by Encoder(true), |  | ||||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { |  | ||||||
|  |  | ||||||
|     override val name = NAME |     override val name = NAME | ||||||
|  |     override val machine = C128MachineDefinition() | ||||||
|  |     override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES) | ||||||
|     override val defaultEncoding = Encoding.PETSCII |     override val defaultEncoding = Encoding.PETSCII | ||||||
|     override val libraryPath = null |  | ||||||
|     override val customLauncher = emptyList<String>() |  | ||||||
|     override val additionalAssemblerOptions = emptyList<String>() |  | ||||||
|     override val defaultOutputType = OutputType.PRG |  | ||||||
|  |  | ||||||
|     companion object { |     companion object { | ||||||
|         const val NAME = "c128" |         const val NAME = "c128" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     override val cpu = CpuType.CPU6502 |  | ||||||
|  |  | ||||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE |  | ||||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE |  | ||||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE |  | ||||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u |  | ||||||
|     override val PROGRAM_LOAD_ADDRESS = 0x1c01u |  | ||||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0xc000u |  | ||||||
|  |  | ||||||
|     override val BSSHIGHRAM_START = 0u    // TODO address? |  | ||||||
|     override val BSSHIGHRAM_END = 0u      // TODO address? |  | ||||||
|     override val BSSGOLDENRAM_START = 0u  // TODO address? |  | ||||||
|     override val BSSGOLDENRAM_END = 0u    // TODO address? |  | ||||||
|  |  | ||||||
|     override lateinit var zeropage: Zeropage |  | ||||||
|     override lateinit var golden: GoldenRam |  | ||||||
|  |  | ||||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() |  | ||||||
|  |  | ||||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { |  | ||||||
|         val m5 = Mflpt5.fromNumber(num) |  | ||||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { |  | ||||||
|         require(bytes.size==5) { "need 5 bytes" } |  | ||||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) |  | ||||||
|         return m5.toDouble() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { |  | ||||||
|         if(selectedEmulator!=1) { |  | ||||||
|             System.err.println("The c128 target only supports the main emulator (Vice).") |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(!quiet) |  | ||||||
|             println("\nStarting C-128 emulator x128...") |  | ||||||
|  |  | ||||||
|         val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) |  | ||||||
|         val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist, |  | ||||||
|             "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") |  | ||||||
|         val processb = ProcessBuilder(cmdline) |  | ||||||
|         if(!quiet) |  | ||||||
|             processb.inheritIO() |  | ||||||
|         val process: Process = processb.start() |  | ||||||
|         process.waitFor() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu |  | ||||||
|  |  | ||||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { |  | ||||||
|         zeropage = C128Zeropage(compilerOptions) |  | ||||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY)    // TODO does the c128 have some of this somewhere? |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,111 +1,22 @@ | |||||||
| package prog8.code.target | package prog8.code.target | ||||||
|  |  | ||||||
| import prog8.code.core.* | import prog8.code.core.Encoding | ||||||
| import prog8.code.target.encodings.Encoder | import prog8.code.core.ICompilationTarget | ||||||
| import prog8.code.target.zp.C64Zeropage | import prog8.code.core.IMemSizer | ||||||
| import java.io.IOException | import prog8.code.core.IStringEncoding | ||||||
| import java.nio.file.Path | import prog8.code.target.c64.C64MachineDefinition | ||||||
|  | import prog8.code.target.cbm.CbmMemorySizer | ||||||
|  |  | ||||||
|  |  | ||||||
| class C64Target: ICompilationTarget, | class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { | ||||||
|     IStringEncoding by Encoder(true), |  | ||||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { |  | ||||||
|  |  | ||||||
|     override val name = NAME |     override val name = NAME | ||||||
|  |     override val machine = C64MachineDefinition() | ||||||
|  |     override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES) | ||||||
|     override val defaultEncoding = Encoding.PETSCII |     override val defaultEncoding = Encoding.PETSCII | ||||||
|     override val libraryPath = null |  | ||||||
|     override val customLauncher = emptyList<String>() |  | ||||||
|     override val additionalAssemblerOptions = emptyList<String>() |  | ||||||
|     override val defaultOutputType = OutputType.PRG |  | ||||||
|  |  | ||||||
|     companion object { |     companion object { | ||||||
|         const val NAME = "c64" |         const val NAME = "c64" | ||||||
|  |  | ||||||
|         fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list" |         fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     override val cpu = CpuType.CPU6502 |  | ||||||
|  |  | ||||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE |  | ||||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE |  | ||||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE |  | ||||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u |  | ||||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0801u |  | ||||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u      // $a000  if floats are used |  | ||||||
|     // note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15 |  | ||||||
|  |  | ||||||
|     override val BSSHIGHRAM_START = 0xc000u |  | ||||||
|     override val BSSHIGHRAM_END = 0xcfdfu |  | ||||||
|     override val BSSGOLDENRAM_START = 0u        // no golden ram on C64 |  | ||||||
|     override val BSSGOLDENRAM_END = 0u |  | ||||||
|  |  | ||||||
|     override lateinit var zeropage: Zeropage |  | ||||||
|     override lateinit var golden: GoldenRam |  | ||||||
|  |  | ||||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() |  | ||||||
|  |  | ||||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { |  | ||||||
|         val m5 = Mflpt5.fromNumber(num) |  | ||||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { |  | ||||||
|         require(bytes.size==5) { "need 5 bytes" } |  | ||||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) |  | ||||||
|         return m5.toDouble() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { |  | ||||||
|         if(selectedEmulator!=1) { |  | ||||||
|             System.err.println("The c64 target only supports the main emulator (Vice).") |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for(emulator in listOf("x64sc", "x64")) { |  | ||||||
|             if(!quiet) |  | ||||||
|                 println("\nStarting C-64 emulator $emulator...") |  | ||||||
|  |  | ||||||
|             val viceMonlist = viceMonListName(programNameWithPath.toString()) |  | ||||||
|             val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist, |  | ||||||
|                 "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") |  | ||||||
|             val processb = ProcessBuilder(cmdline) |  | ||||||
|             if(!quiet) |  | ||||||
|                 processb.inheritIO() |  | ||||||
|             val process: Process |  | ||||||
|             try { |  | ||||||
|                 process=processb.start() |  | ||||||
|             } catch(_: IOException) { |  | ||||||
|                 continue  // try the next emulator executable |  | ||||||
|             } |  | ||||||
|             process.waitFor() |  | ||||||
|             break |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu |  | ||||||
|  |  | ||||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { |  | ||||||
|         zeropage = C64Zeropage(compilerOptions) |  | ||||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| val CompilationTargets = listOf( |  | ||||||
|     C64Target.NAME, |  | ||||||
|     C128Target.NAME, |  | ||||||
|     Cx16Target.NAME, |  | ||||||
|     PETTarget.NAME, |  | ||||||
|     VMTarget.NAME |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| fun getCompilationTargetByName(name: String) = when(name.lowercase()) { |  | ||||||
|     C64Target.NAME -> C64Target() |  | ||||||
|     C128Target.NAME -> C128Target() |  | ||||||
|     Cx16Target.NAME -> Cx16Target() |  | ||||||
|     PETTarget.NAME -> PETTarget() |  | ||||||
|     VMTarget.NAME -> VMTarget() |  | ||||||
|     else -> throw IllegalArgumentException("invalid compilation target") |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,173 +0,0 @@ | |||||||
| package prog8.code.target |  | ||||||
|  |  | ||||||
| import prog8.code.core.* |  | ||||||
| import prog8.code.source.ImportFileSystem.expandTilde |  | ||||||
| import prog8.code.target.encodings.Encoder |  | ||||||
| import prog8.code.target.zp.ConfigurableZeropage |  | ||||||
| import java.io.IOException |  | ||||||
| import java.nio.file.Path |  | ||||||
| import java.util.* |  | ||||||
| import kotlin.io.path.Path |  | ||||||
| import kotlin.io.path.inputStream |  | ||||||
| import kotlin.io.path.isDirectory |  | ||||||
| import kotlin.io.path.nameWithoutExtension |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ConfigFileTarget( |  | ||||||
|     override val name: String, |  | ||||||
|     override val defaultEncoding: Encoding, |  | ||||||
|     override val cpu: CpuType, |  | ||||||
|     override val PROGRAM_LOAD_ADDRESS: UInt, |  | ||||||
|     override val PROGRAM_MEMTOP_ADDRESS: UInt, |  | ||||||
|     override val STARTUP_CODE_RESERVED_SIZE: UInt, |  | ||||||
|     override val BSSHIGHRAM_START: UInt, |  | ||||||
|     override val BSSHIGHRAM_END: UInt, |  | ||||||
|     override val BSSGOLDENRAM_START: UInt, |  | ||||||
|     override val BSSGOLDENRAM_END: UInt, |  | ||||||
|     override val defaultOutputType: OutputType, |  | ||||||
|     override val libraryPath: Path, |  | ||||||
|     override val customLauncher: List<String>, |  | ||||||
|     override val additionalAssemblerOptions: List<String>, |  | ||||||
|     val ioAddresses: List<UIntRange>, |  | ||||||
|     val zpScratchB1: UInt, |  | ||||||
|     val zpScratchReg: UInt, |  | ||||||
|     val zpScratchW1: UInt, |  | ||||||
|     val zpScratchW2: UInt, |  | ||||||
|     val zpScratchPtr: UInt, |  | ||||||
|     val virtualregistersStart: UInt, |  | ||||||
|     val zpFullsafe: List<UIntRange>, |  | ||||||
|     val zpKernalsafe: List<UIntRange>, |  | ||||||
|     val zpBasicsafe: List<UIntRange> |  | ||||||
| ): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) { |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|  |  | ||||||
|         private fun Properties.getString(property: String): String { |  | ||||||
|             val value = this.getProperty(property, null) |  | ||||||
|             if(value!=null) |  | ||||||
|                 return value |  | ||||||
|             throw NoSuchElementException("string property '$property' not found in config file") |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private fun Properties.getInteger(property: String): UInt { |  | ||||||
|             val value = this.getProperty(property, null) |  | ||||||
|             if(value!=null) return parseInt(value) |  | ||||||
|             throw NoSuchElementException("integer property '$property' not found in config file") |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private fun parseInt(value: String): UInt { |  | ||||||
|             if(value.startsWith("0x")) |  | ||||||
|                 return value.drop(2).toUInt(16) |  | ||||||
|             if(value.startsWith("$")) |  | ||||||
|                 return value.drop(1).toUInt(16) |  | ||||||
|             if(value.startsWith("%")) |  | ||||||
|                 return value.drop(1).toUInt(2) |  | ||||||
|             return value.toUInt() |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> { |  | ||||||
|             val rangesStr = props.getString(key) |  | ||||||
|             if(rangesStr.isBlank()) |  | ||||||
|                 return emptyList() |  | ||||||
|             val result = mutableListOf<UIntRange>() |  | ||||||
|             val ranges = rangesStr.split(",").map { it.trim() } |  | ||||||
|             for(r in ranges) { |  | ||||||
|                 if ('-' in r) { |  | ||||||
|                     val (fromStr, toStr) = r.split("-") |  | ||||||
|                     val from = parseInt(fromStr.trim()) |  | ||||||
|                     val to = parseInt(toStr.trim()) |  | ||||||
|                     result.add(from..to) |  | ||||||
|                 } else { |  | ||||||
|                     val address = parseInt(r) |  | ||||||
|                     result.add(address..address) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return result |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fun fromConfigFile(configfile: Path): ConfigFileTarget { |  | ||||||
|             val props = Properties() |  | ||||||
|             props.load(configfile.inputStream()) |  | ||||||
|  |  | ||||||
|             val cpuString = props.getString("cpu").uppercase() |  | ||||||
|             val cpuType = try { |  | ||||||
|                 CpuType.valueOf(cpuString) |  | ||||||
|             } catch (_: IllegalArgumentException) { |  | ||||||
|                 CpuType.valueOf("CPU$cpuString") |  | ||||||
|             } |  | ||||||
|             val ioAddresses = parseAddressRanges("io_regions", props) |  | ||||||
|             val zpFullsafe = parseAddressRanges("zp_fullsafe", props) |  | ||||||
|             val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props) |  | ||||||
|             val zpBasicsafe = parseAddressRanges("zp_basicsafe", props) |  | ||||||
|  |  | ||||||
|             val libraryPath = expandTilde(Path(props.getString("library"))) |  | ||||||
|             if(!libraryPath.isDirectory()) |  | ||||||
|                 throw IOException("invalid library path: $libraryPath") |  | ||||||
|  |  | ||||||
|             val customLauncherStr = props.getProperty("custom_launcher_code", null) |  | ||||||
|             val customLauncher = |  | ||||||
|                 if(customLauncherStr?.isNotBlank()==true) |  | ||||||
|                     (customLauncherStr+"\n").lines().map { it.trimEnd() } |  | ||||||
|                 else emptyList() |  | ||||||
|             val assemblerOptionsStr = props.getProperty("assembler_options", "").trim() |  | ||||||
|             val outputTypeString = props.getProperty("output_type", "PRG") |  | ||||||
|             val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase()) |  | ||||||
|  |  | ||||||
|             return ConfigFileTarget( |  | ||||||
|                 configfile.nameWithoutExtension, |  | ||||||
|                 Encoding.entries.first { it.prefix==props.getString("encoding") }, |  | ||||||
|                 cpuType, |  | ||||||
|                 props.getInteger("load_address"), |  | ||||||
|                 props.getInteger("memtop"), |  | ||||||
|                 0u,         // used only in a very specific error condition check in a certain scenario... |  | ||||||
|                 props.getInteger("bss_highram_start"), |  | ||||||
|                 props.getInteger("bss_highram_end"), |  | ||||||
|                 props.getInteger("bss_goldenram_start"), |  | ||||||
|                 props.getInteger("bss_goldenram_end"), |  | ||||||
|                 defaultOutputType, |  | ||||||
|                 libraryPath, |  | ||||||
|                 customLauncher, |  | ||||||
|                 if(assemblerOptionsStr=="") emptyList() else assemblerOptionsStr.split(" "), |  | ||||||
|                 ioAddresses, |  | ||||||
|                 props.getInteger("zp_scratch_b1"), |  | ||||||
|                 props.getInteger("zp_scratch_reg"), |  | ||||||
|                 props.getInteger("zp_scratch_w1"), |  | ||||||
|                 props.getInteger("zp_scratch_w2"), |  | ||||||
|                 props.getInteger("zp_scratch_ptr"), |  | ||||||
|                 props.getInteger("virtual_registers"), |  | ||||||
|                 zpFullsafe, |  | ||||||
|                 zpKernalsafe, |  | ||||||
|                 zpBasicsafe, |  | ||||||
|             ) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // TODO floats are not yet supported here, just enter some values |  | ||||||
|     override val FLOAT_MAX_POSITIVE = 9.999999999e97 |  | ||||||
|     override val FLOAT_MAX_NEGATIVE = -9.999999999e97 |  | ||||||
|     override val FLOAT_MEM_SIZE = 8 |  | ||||||
|  |  | ||||||
|     override lateinit var zeropage: Zeropage |  | ||||||
|     override lateinit var golden: GoldenRam     // TODO this is not yet used |  | ||||||
|  |  | ||||||
|     override fun getFloatAsmBytes(num: Number) = TODO("floats") |  | ||||||
|     override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats") |  | ||||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats") |  | ||||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { |  | ||||||
|         throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it } |  | ||||||
|  |  | ||||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { |  | ||||||
|         zeropage = ConfigurableZeropage( |  | ||||||
|             zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr, |  | ||||||
|             virtualregistersStart, |  | ||||||
|             zpBasicsafe, |  | ||||||
|             zpKernalsafe, |  | ||||||
|             zpFullsafe, |  | ||||||
|             compilerOptions |  | ||||||
|         ) |  | ||||||
|         // note: there's no golden ram yet |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,94 +1,20 @@ | |||||||
| package prog8.code.target | package prog8.code.target | ||||||
|  |  | ||||||
| import prog8.code.core.* | import prog8.code.core.Encoding | ||||||
| import prog8.code.target.encodings.Encoder | import prog8.code.core.ICompilationTarget | ||||||
| import prog8.code.target.zp.CX16Zeropage | import prog8.code.core.IMemSizer | ||||||
| import java.nio.file.Path | import prog8.code.core.IStringEncoding | ||||||
|  | import prog8.code.target.cbm.CbmMemorySizer | ||||||
|  | import prog8.code.target.cx16.CX16MachineDefinition | ||||||
|  |  | ||||||
|  |  | ||||||
| class Cx16Target: ICompilationTarget, | class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { | ||||||
|     IStringEncoding by Encoder(true), |  | ||||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { |  | ||||||
|  |  | ||||||
|     override val name = NAME |     override val name = NAME | ||||||
|  |     override val machine = CX16MachineDefinition() | ||||||
|  |     override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO) | ||||||
|     override val defaultEncoding = Encoding.PETSCII |     override val defaultEncoding = Encoding.PETSCII | ||||||
|     override val libraryPath = null |  | ||||||
|     override val customLauncher = emptyList<String>() |  | ||||||
|     override val additionalAssemblerOptions = emptyList<String>() |  | ||||||
|     override val defaultOutputType = OutputType.PRG |  | ||||||
|  |  | ||||||
|     companion object { |     companion object { | ||||||
|         const val NAME = "cx16" |         const val NAME = "cx16" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     override val cpu = CpuType.CPU65C02 |  | ||||||
|  |  | ||||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE |  | ||||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE |  | ||||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE |  | ||||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u |  | ||||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0801u |  | ||||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u |  | ||||||
|  |  | ||||||
|     override val BSSHIGHRAM_START = 0xa000u     // hiram bank 1, 8Kb, assumed to be active |  | ||||||
|     override val BSSHIGHRAM_END = 0xbfffu       // Rom starts at $c000 |  | ||||||
|     override val BSSGOLDENRAM_START = 0x0400u |  | ||||||
|     override val BSSGOLDENRAM_END = 0x07ffu |  | ||||||
|  |  | ||||||
|     override lateinit var zeropage: Zeropage |  | ||||||
|     override lateinit var golden: GoldenRam |  | ||||||
|  |  | ||||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() |  | ||||||
|  |  | ||||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { |  | ||||||
|         val m5 = Mflpt5.fromNumber(num) |  | ||||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { |  | ||||||
|         require(bytes.size==5) { "need 5 bytes" } |  | ||||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) |  | ||||||
|         return m5.toDouble() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { |  | ||||||
|         val emulator: String |  | ||||||
|         val extraArgs: List<String> |  | ||||||
|  |  | ||||||
|         when(selectedEmulator) { |  | ||||||
|             1 -> { |  | ||||||
|                 emulator = "x16emu" |  | ||||||
|                 extraArgs = listOf("-debug") |  | ||||||
|             } |  | ||||||
|             2 -> { |  | ||||||
|                 emulator = "box16" |  | ||||||
|                 extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString())) |  | ||||||
|             } |  | ||||||
|             else -> { |  | ||||||
|                 System.err.println("Cx16 target only supports x16emu and box16 emulators.") |  | ||||||
|                 return |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(!quiet) |  | ||||||
|             println("\nStarting Commander X16 emulator $emulator...") |  | ||||||
|  |  | ||||||
|         val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs |  | ||||||
|         val processb = ProcessBuilder(cmdline) |  | ||||||
|         if(!quiet) |  | ||||||
|             processb.inheritIO() |  | ||||||
|         processb.environment()["PULSE_LATENCY_MSEC"] = "10" |  | ||||||
|         val process: Process = processb.start() |  | ||||||
|         process.waitFor() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu |  | ||||||
|  |  | ||||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { |  | ||||||
|         zeropage = CX16Zeropage(compilerOptions) |  | ||||||
|         golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,24 +1,21 @@ | |||||||
| package prog8.code.target.encodings | package prog8.code.target | ||||||
| 
 | 
 | ||||||
| import com.github.michaelbull.result.fold | import com.github.michaelbull.result.fold | ||||||
| import prog8.code.core.Encoding | import prog8.code.core.Encoding | ||||||
| import prog8.code.core.IStringEncoding | import prog8.code.core.IStringEncoding | ||||||
| import prog8.code.core.InternalCompilerException | import prog8.code.core.InternalCompilerException | ||||||
|  | import prog8.code.target.cbm.AtasciiEncoding | ||||||
|  | import prog8.code.target.cbm.IsoEncoding | ||||||
|  | import prog8.code.target.cbm.PetsciiEncoding | ||||||
| 
 | 
 | ||||||
| class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding { |  | ||||||
|     override val defaultEncoding: Encoding = Encoding.ISO |  | ||||||
| 
 | 
 | ||||||
|  | object Encoder: IStringEncoding { | ||||||
|     override fun encodeString(str: String, encoding: Encoding): List<UByte> { |     override fun encodeString(str: String, encoding: Encoding): List<UByte> { | ||||||
|         val coded = when(encoding) { |         val coded = when(encoding) { | ||||||
|             Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true) |             Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true) | ||||||
|             Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true) |             Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true) | ||||||
|             Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn) |             Encoding.ISO -> IsoEncoding.encode(str) | ||||||
|             Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn) |  | ||||||
|             Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn) |  | ||||||
|             Encoding.CP437 -> Cp437Encoding.encode(str) |  | ||||||
|             Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn) |  | ||||||
|             Encoding.ATASCII -> AtasciiEncoding.encode(str) |             Encoding.ATASCII -> AtasciiEncoding.encode(str) | ||||||
|             Encoding.C64OS -> C64osEncoding.encode(str) |  | ||||||
|             else -> throw InternalCompilerException("unsupported encoding $encoding") |             else -> throw InternalCompilerException("unsupported encoding $encoding") | ||||||
|         } |         } | ||||||
|         return coded.fold( |         return coded.fold( | ||||||
| @@ -30,13 +27,8 @@ class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding { | |||||||
|         val decoded = when(encoding) { |         val decoded = when(encoding) { | ||||||
|             Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true) |             Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true) | ||||||
|             Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true) |             Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true) | ||||||
|             Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn) |             Encoding.ISO -> IsoEncoding.decode(bytes) | ||||||
|             Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn) |  | ||||||
|             Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn) |  | ||||||
|             Encoding.CP437 -> Cp437Encoding.decode(bytes) |  | ||||||
|             Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn) |  | ||||||
|             Encoding.ATASCII -> AtasciiEncoding.decode(bytes) |             Encoding.ATASCII -> AtasciiEncoding.decode(bytes) | ||||||
|             Encoding.C64OS -> C64osEncoding.decode(bytes) |  | ||||||
|             else -> throw InternalCompilerException("unsupported encoding $encoding") |             else -> throw InternalCompilerException("unsupported encoding $encoding") | ||||||
|         } |         } | ||||||
|         return decoded.fold( |         return decoded.fold( | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| package prog8.code.target |  | ||||||
|  |  | ||||||
| import prog8.code.core.BaseDataType |  | ||||||
| import prog8.code.core.DataType |  | ||||||
| import prog8.code.core.IMemSizer |  | ||||||
|  |  | ||||||
| internal class NormalMemSizer(val floatsize: Int): IMemSizer { |  | ||||||
|  |  | ||||||
|     override fun memorySize(dt: DataType, numElements: Int?): Int { |  | ||||||
|         if(dt.isPointerArray) |  | ||||||
|             return 2 * numElements!!        // array of pointers is just array of uwords |  | ||||||
|         else if(dt.isArray) { |  | ||||||
|             if(numElements==null) return 2      // treat it as a pointer size |  | ||||||
|             return when(dt.sub) { |  | ||||||
|                 BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements |  | ||||||
|                 BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2 |  | ||||||
|                 BaseDataType.LONG -> numElements * 4 |  | ||||||
|                 BaseDataType.FLOAT-> numElements * floatsize |  | ||||||
|                 BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size") |  | ||||||
|                 else -> throw IllegalArgumentException("invalid sub type") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         else if (dt.isString) { |  | ||||||
|             return numElements        // treat it as the size of the given string with the length |  | ||||||
|                 ?: 2    // treat it as the size to store a string pointer |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return when { |  | ||||||
|             dt.isByteOrBool -> 1 * (numElements ?: 1) |  | ||||||
|             dt.isFloat -> floatsize * (numElements ?: 1) |  | ||||||
|             dt.isLong -> 4 * (numElements ?: 1) |  | ||||||
|             dt.isPointer -> 2  // pointer is just a uword |  | ||||||
|             dt.isStructInstance -> dt.subType!!.memsize(this) |  | ||||||
|             dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size") |  | ||||||
|             else -> 2 * (numElements ?: 1) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| package prog8.code.target |  | ||||||
|  |  | ||||||
| import prog8.code.core.* |  | ||||||
| import prog8.code.target.encodings.Encoder |  | ||||||
| import prog8.code.target.zp.PETZeropage |  | ||||||
| import java.nio.file.Path |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class PETTarget: ICompilationTarget, |  | ||||||
|     IStringEncoding by Encoder(true), |  | ||||||
|     IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { |  | ||||||
|  |  | ||||||
|     override val name = NAME |  | ||||||
|     override val defaultEncoding = Encoding.PETSCII |  | ||||||
|     override val libraryPath = null |  | ||||||
|     override val customLauncher = emptyList<String>() |  | ||||||
|     override val additionalAssemblerOptions = emptyList<String>() |  | ||||||
|     override val defaultOutputType = OutputType.PRG |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|         const val NAME = "pet32" |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override val cpu = CpuType.CPU6502 |  | ||||||
|  |  | ||||||
|     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE |  | ||||||
|     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE |  | ||||||
|     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE |  | ||||||
|     override val STARTUP_CODE_RESERVED_SIZE = 20u |  | ||||||
|     override val PROGRAM_LOAD_ADDRESS = 0x0401u |  | ||||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0x8000u |  | ||||||
|  |  | ||||||
|     override val BSSHIGHRAM_START = 0u |  | ||||||
|     override val BSSHIGHRAM_END = 0u |  | ||||||
|     override val BSSGOLDENRAM_START = 0u |  | ||||||
|     override val BSSGOLDENRAM_END = 0u |  | ||||||
|  |  | ||||||
|     override lateinit var zeropage: Zeropage |  | ||||||
|     override lateinit var golden: GoldenRam |  | ||||||
|  |  | ||||||
|     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() |  | ||||||
|  |  | ||||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { |  | ||||||
|         val m5 = Mflpt5.fromNumber(num) |  | ||||||
|         return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { |  | ||||||
|         require(bytes.size==5) { "need 5 bytes" } |  | ||||||
|         val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]) |  | ||||||
|         return m5.toDouble() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { |  | ||||||
|         if(selectedEmulator!=1) { |  | ||||||
|             System.err.println("The pet target only supports the main emulator (Vice).") |  | ||||||
|             return |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(!quiet) |  | ||||||
|             println("\nStarting PET emulator...") |  | ||||||
|  |  | ||||||
|         val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) |  | ||||||
|         val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist, |  | ||||||
|             "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") |  | ||||||
|         val processb = ProcessBuilder(cmdline) |  | ||||||
|         if(!quiet) |  | ||||||
|             processb.inheritIO() |  | ||||||
|         val process=processb.start() |  | ||||||
|         process.waitFor() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu |  | ||||||
|  |  | ||||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { |  | ||||||
|         zeropage = PETZeropage(compilerOptions) |  | ||||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,109 +1,27 @@ | |||||||
| package prog8.code.target | package prog8.code.target | ||||||
|  |  | ||||||
| import prog8.code.core.* | import prog8.code.core.* | ||||||
| import prog8.code.target.encodings.Encoder | import prog8.code.target.virtual.VirtualMachineDefinition | ||||||
| import java.nio.file.Path |  | ||||||
| import kotlin.io.path.extension |  | ||||||
| import kotlin.io.path.isReadable |  | ||||||
| import kotlin.io.path.name |  | ||||||
| import kotlin.io.path.readText |  | ||||||
|  |  | ||||||
| class VMTarget: ICompilationTarget, |  | ||||||
|     IStringEncoding by Encoder(false), |  | ||||||
|     IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) { |  | ||||||
|  |  | ||||||
|  | class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer { | ||||||
|     override val name = NAME |     override val name = NAME | ||||||
|  |     override val machine = VirtualMachineDefinition() | ||||||
|  |     override val supportedEncodings = setOf(Encoding.ISO) | ||||||
|     override val defaultEncoding = Encoding.ISO |     override val defaultEncoding = Encoding.ISO | ||||||
|     override val libraryPath = null |  | ||||||
|     override val customLauncher = emptyList<String>() |  | ||||||
|     override val additionalAssemblerOptions = emptyList<String>() |  | ||||||
|     override val defaultOutputType = OutputType.PRG |  | ||||||
|  |  | ||||||
|     companion object { |     companion object { | ||||||
|         const val NAME = "virtual" |         const val NAME = "virtual" | ||||||
|         const val FLOAT_MEM_SIZE = 8             // 64-bits double |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override val cpu = CpuType.VIRTUAL |     override fun memorySize(dt: DataType): Int { | ||||||
|  |         return when(dt) { | ||||||
|     override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE |             in ByteDatatypes -> 1 | ||||||
|     override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE |             in WordDatatypes, in PassByReferenceDatatypes -> 2 | ||||||
|     override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE |             DataType.FLOAT -> machine.FLOAT_MEM_SIZE | ||||||
|     override val STARTUP_CODE_RESERVED_SIZE = 0u  // not actually used |             else -> Int.MIN_VALUE | ||||||
|     override val PROGRAM_LOAD_ADDRESS = 0u      // not actually used |  | ||||||
|     override val PROGRAM_MEMTOP_ADDRESS = 0xffffu  // not actually used |  | ||||||
|  |  | ||||||
|     override val BSSHIGHRAM_START = 0u          // not actually used |  | ||||||
|     override val BSSHIGHRAM_END = 0u            // not actually used |  | ||||||
|     override val BSSGOLDENRAM_START = 0u        // not actually used |  | ||||||
|     override val BSSGOLDENRAM_END = 0u          // not actually used |  | ||||||
|     override lateinit var zeropage: Zeropage    // not actually used |  | ||||||
|     override lateinit var golden: GoldenRam     // not actually used |  | ||||||
|  |  | ||||||
|     override fun getFloatAsmBytes(num: Number): String { |  | ||||||
|         // little endian binary representation |  | ||||||
|         val bits = num.toDouble().toBits().toULong() |  | ||||||
|         val hexStr = bits.toString(16).padStart(16, '0') |  | ||||||
|         val parts = hexStr.chunked(2).map { "$$it" } |  | ||||||
|         return parts.joinToString(", ") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun convertFloatToBytes(num: Double): List<UByte> { |  | ||||||
|         val bits = num.toBits().toULong() |  | ||||||
|         val hexStr = bits.toString(16).padStart(16, '0') |  | ||||||
|         val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() } |  | ||||||
|         return parts |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun convertBytesToFloat(bytes: List<UByte>): Double { |  | ||||||
|         require(bytes.size==8) { "need 8 bytes" } |  | ||||||
|         val b0 = bytes[0].toLong() shl (8*7) |  | ||||||
|         val b1 = bytes[1].toLong() shl (8*6) |  | ||||||
|         val b2 = bytes[2].toLong() shl (8*5) |  | ||||||
|         val b3 = bytes[3].toLong() shl (8*4) |  | ||||||
|         val b4 = bytes[4].toLong() shl (8*3) |  | ||||||
|         val b5 = bytes[5].toLong() shl (8*2) |  | ||||||
|         val b6 = bytes[6].toLong() shl (8*1) |  | ||||||
|         val b7 = bytes[7].toLong() shl (8*0) |  | ||||||
|         return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) { |  | ||||||
|         if(!quiet) |  | ||||||
|             println("\nStarting Virtual Machine...") |  | ||||||
|  |  | ||||||
|         // to not have external module dependencies in our own module, we launch the virtual machine via reflection |  | ||||||
|         val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner |  | ||||||
|         val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir") |  | ||||||
|         if(withExt.isReadable()) |  | ||||||
|             vm.runProgram(withExt.readText(), quiet) |  | ||||||
|         else |  | ||||||
|             throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun isIOAddress(address: UInt): Boolean = false |  | ||||||
|  |  | ||||||
|     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { |  | ||||||
|         zeropage = VirtualZeropage(compilerOptions) |  | ||||||
|         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     override fun memorySize(arrayDt: DataType, numElements: Int) = | ||||||
|  |         memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements | ||||||
| interface IVirtualMachineRunner { |  | ||||||
|     fun runProgram(irSource: String, quiet: Boolean) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| private class VirtualZeropage(options: CompilationOptions): Zeropage(options) { |  | ||||||
|     override val SCRATCH_B1: UInt |  | ||||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") |  | ||||||
|     override val SCRATCH_REG: UInt |  | ||||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") |  | ||||||
|     override val SCRATCH_W1: UInt |  | ||||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") |  | ||||||
|     override val SCRATCH_W2: UInt |  | ||||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") |  | ||||||
|     override val SCRATCH_PTR: UInt |  | ||||||
|         get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") |  | ||||||
| } | } | ||||||
| @@ -0,0 +1,67 @@ | |||||||
|  | package prog8.code.target.atari | ||||||
|  |  | ||||||
|  | import prog8.code.core.* | ||||||
|  | import java.nio.file.Path | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AtariMachineDefinition: IMachineDefinition { | ||||||
|  |  | ||||||
|  |     override val cpu = CpuType.CPU6502 | ||||||
|  |  | ||||||
|  |     override val FLOAT_MAX_POSITIVE = 9.999999999e97 | ||||||
|  |     override val FLOAT_MAX_NEGATIVE = -9.999999999e97 | ||||||
|  |     override val FLOAT_MEM_SIZE = 6 | ||||||
|  |     override val PROGRAM_LOAD_ADDRESS = 0x2000u | ||||||
|  |  | ||||||
|  |     // the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations) | ||||||
|  |     override var ESTACK_LO = 0x1b00u     //  $1b00-$1b7f inclusive      // TODO | ||||||
|  |     override var ESTACK_HI = 0x1b80u     //  $1b80-$1bff inclusive      // TODO | ||||||
|  |  | ||||||
|  |     override val BSSHIGHRAM_START = 0u    // TODO | ||||||
|  |     override val BSSHIGHRAM_END = 0u      // TODO | ||||||
|  |  | ||||||
|  |     override lateinit var zeropage: Zeropage | ||||||
|  |     override lateinit var golden: GoldenRam | ||||||
|  |  | ||||||
|  |     override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number") | ||||||
|  |  | ||||||
|  |     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||||
|  |         return if (compilerOptions.output == OutputType.XEX) | ||||||
|  |             listOf("syslib") | ||||||
|  |         else | ||||||
|  |             emptyList() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||||
|  |         val emulatorName: String | ||||||
|  |         val cmdline: List<String> | ||||||
|  |         when(selectedEmulator) { | ||||||
|  |             1 -> { | ||||||
|  |                 emulatorName = "atari800" | ||||||
|  |                 cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex") | ||||||
|  |             } | ||||||
|  |             2 -> { | ||||||
|  |                 emulatorName = "altirra" | ||||||
|  |                 cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex") | ||||||
|  |             } | ||||||
|  |             else -> { | ||||||
|  |                 System.err.println("Atari target only supports atari800 and altirra emulators.") | ||||||
|  |                 return | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // TODO monlist? | ||||||
|  |  | ||||||
|  |         println("\nStarting Atari800XL emulator $emulatorName...") | ||||||
|  |         val processb = ProcessBuilder(cmdline).inheritIO() | ||||||
|  |         val process: Process = processb.start() | ||||||
|  |         process.waitFor() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu        // TODO | ||||||
|  |  | ||||||
|  |     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||||
|  |         zeropage = AtariZeropage(compilerOptions) | ||||||
|  |         golden = GoldenRam(compilerOptions, UIntRange.EMPTY) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								codeCore/src/prog8/code/target/atari/AtariZeropage.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								codeCore/src/prog8/code/target/atari/AtariZeropage.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | package prog8.code.target.atari | ||||||
|  |  | ||||||
|  | import prog8.code.core.CompilationOptions | ||||||
|  | import prog8.code.core.InternalCompilerException | ||||||
|  | import prog8.code.core.Zeropage | ||||||
|  | import prog8.code.core.ZeropageType | ||||||
|  |  | ||||||
|  | class AtariZeropage(options: CompilationOptions) : Zeropage(options) { | ||||||
|  |  | ||||||
|  |     override val SCRATCH_B1 = 0xcbu      // temp storage for a single byte | ||||||
|  |     override val SCRATCH_REG = 0xccu     // temp storage for a register, must be B1+1 | ||||||
|  |     override val SCRATCH_W1 = 0xcdu      // temp storage 1 for a word  $cd+$ce | ||||||
|  |     override val SCRATCH_W2 = 0xcfu      // temp storage 2 for a word  $cf+$d0        TODO is $d0 okay to use? | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         if (options.floats) { | ||||||
|  |             throw InternalCompilerException("Atari target doesn't yet support floating point routines") | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (options.floats && options.zeropage !in arrayOf( | ||||||
|  |                 ZeropageType.FLOATSAFE, | ||||||
|  |                 ZeropageType.BASICSAFE, | ||||||
|  |                 ZeropageType.DONTUSE | ||||||
|  |             )) | ||||||
|  |             throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'") | ||||||
|  |  | ||||||
|  |         when (options.zeropage) { | ||||||
|  |             ZeropageType.FULL -> { | ||||||
|  |                 // TODO all atari usable zero page locations, except the ones used by the system's IRQ routine | ||||||
|  |                 free.addAll(0x00u..0xffu) | ||||||
|  |                 // TODO atari  free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u))        // these are updated by IRQ | ||||||
|  |             } | ||||||
|  |             ZeropageType.KERNALSAFE -> { | ||||||
|  |                 free.addAll(0x80u..0xffu)       // TODO | ||||||
|  |             } | ||||||
|  |             ZeropageType.BASICSAFE, | ||||||
|  |             ZeropageType.FLOATSAFE -> { | ||||||
|  |                 free.addAll(0x80u..0xffu)       // TODO | ||||||
|  |                 free.removeAll(0xd4u .. 0xefu)      // floating point storage | ||||||
|  |             } | ||||||
|  |             ZeropageType.DONTUSE -> { | ||||||
|  |                 free.clear()  // don't use zeropage at all | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val distinctFree = free.distinct() | ||||||
|  |         free.clear() | ||||||
|  |         free.addAll(distinctFree) | ||||||
|  |  | ||||||
|  |         removeReservedFromFreePool() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun allocateCx16VirtualRegisters() { | ||||||
|  |         TODO("Not known if atari can put the virtual regs in ZP") | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								codeCore/src/prog8/code/target/c128/C128MachineDefinition.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | package prog8.code.target.c128 | ||||||
|  |  | ||||||
|  | import prog8.code.core.* | ||||||
|  | import prog8.code.target.C64Target | ||||||
|  | import prog8.code.target.cbm.Mflpt5 | ||||||
|  | import java.nio.file.Path | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class C128MachineDefinition: IMachineDefinition { | ||||||
|  |  | ||||||
|  |     override val cpu = CpuType.CPU6502 | ||||||
|  |  | ||||||
|  |     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||||
|  |     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||||
|  |     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||||
|  |     override val PROGRAM_LOAD_ADDRESS = 0x1c01u | ||||||
|  |  | ||||||
|  |     // the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations) | ||||||
|  |     override var ESTACK_LO = 0x1b00u     //  $1b00-$1b7f inclusive | ||||||
|  |     override var ESTACK_HI = 0x1b80u     //  $1b80-$1bff inclusive | ||||||
|  |  | ||||||
|  |     override val BSSHIGHRAM_START = 0u    // TODO | ||||||
|  |     override val BSSHIGHRAM_END = 0u      // TODO | ||||||
|  |  | ||||||
|  |     override lateinit var zeropage: Zeropage | ||||||
|  |     override lateinit var golden: GoldenRam | ||||||
|  |  | ||||||
|  |     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||||
|  |  | ||||||
|  |     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||||
|  |         return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) | ||||||
|  |             listOf("syslib") | ||||||
|  |         else | ||||||
|  |             emptyList() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||||
|  |         if(selectedEmulator!=1) { | ||||||
|  |             System.err.println("The c128 target only supports the main emulator (Vice).") | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         println("\nStarting C-128 emulator x128...") | ||||||
|  |         val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) | ||||||
|  |         val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist, | ||||||
|  |                 "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||||
|  |         val processb = ProcessBuilder(cmdline).inheritIO() | ||||||
|  |         val process: Process = processb.start() | ||||||
|  |         process.waitFor() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu | ||||||
|  |  | ||||||
|  |     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||||
|  |         zeropage = C128Zeropage(compilerOptions) | ||||||
|  |         golden = GoldenRam(compilerOptions, UIntRange.EMPTY)    // TODO does the c128 have some of this somewhere? | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package prog8.code.target.zp | package prog8.code.target.c128 | ||||||
| 
 | 
 | ||||||
| import prog8.code.core.CompilationOptions | import prog8.code.core.CompilationOptions | ||||||
| import prog8.code.core.InternalCompilerException | import prog8.code.core.InternalCompilerException | ||||||
| @@ -11,12 +11,17 @@ import prog8.code.core.ZeropageType | |||||||
| class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | ||||||
| 
 | 
 | ||||||
|     override val SCRATCH_B1 = 0x74u      // temp storage for a single byte |     override val SCRATCH_B1 = 0x74u      // temp storage for a single byte | ||||||
|     override val SCRATCH_REG = 0x75u     // temp storage for a register byte, must be B1+1 |     override val SCRATCH_REG = 0x75u     // temp storage for a register, must be B1+1 | ||||||
|     override val SCRATCH_W1 = 0xfbu      // temp storage 1 for a word  $fb+$fc |     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_W2 = 0xfdu      // temp storage 2 for a word  $fd+$fe | ||||||
|     override val SCRATCH_PTR = 0x0bu     // temp storage for a pointer $0b+$0c |  | ||||||
| 
 | 
 | ||||||
|     init { |     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( |         if (options.floats && options.zeropage !in arrayOf( | ||||||
|                 ZeropageType.FLOATSAFE, |                 ZeropageType.FLOATSAFE, | ||||||
|                 ZeropageType.BASICSAFE, |                 ZeropageType.BASICSAFE, | ||||||
| @@ -28,18 +33,18 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | |||||||
|             ZeropageType.FULL -> { |             ZeropageType.FULL -> { | ||||||
|                 // $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such |                 // $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such | ||||||
|                 free.addAll(0x0au..0xffu) |                 free.addAll(0x0au..0xffu) | ||||||
|                 free.removeAll(arrayOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u))        // these are updated/used by IRQ |                 free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u))        // these are updated/used by IRQ | ||||||
|             } |             } | ||||||
|             ZeropageType.KERNALSAFE -> { |             ZeropageType.KERNALSAFE -> { | ||||||
|                 free.addAll(0x0au..0x8fu)       // BASIC variables |                 free.addAll(0x0au..0x8fu)       // BASIC variables | ||||||
|                 free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, |                 free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, | ||||||
|                     0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u)) |                     0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u)) | ||||||
|             } |             } | ||||||
|             ZeropageType.FLOATSAFE, |             ZeropageType.FLOATSAFE, | ||||||
|             ZeropageType.BASICSAFE -> { |             ZeropageType.BASICSAFE -> { | ||||||
|                 free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au)) |                 free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au)) | ||||||
|                 free.addAll(0x1bu..0x23u) |                 free.addAll(0x1bu..0x23u) | ||||||
|                 free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu, |                 free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu, | ||||||
|                     0x55u, 0x56u, 0x57u, 0x58u, |                     0x55u, 0x56u, 0x57u, 0x58u, | ||||||
|                     0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, |                     0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, | ||||||
|                     0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, |                     0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, | ||||||
| @@ -48,7 +53,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | |||||||
| 
 | 
 | ||||||
|                 // if(options.zeropage==ZeropageType.BASICSAFE) { |                 // if(options.zeropage==ZeropageType.BASICSAFE) { | ||||||
|                     // can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0 |                     // can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0 | ||||||
|                     free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu, |                     free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu, | ||||||
|                         0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u, |                         0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u, | ||||||
|                         0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u, |                         0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u, | ||||||
|                         0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u)) |                         0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u)) | ||||||
| @@ -64,6 +69,9 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) { | |||||||
|         free.addAll(distinctFree) |         free.addAll(distinctFree) | ||||||
| 
 | 
 | ||||||
|         removeReservedFromFreePool() |         removeReservedFromFreePool() | ||||||
|         retainAllowed() |     } | ||||||
|  | 
 | ||||||
|  |     override fun allocateCx16VirtualRegisters() { | ||||||
|  |         TODO("Not known if C128 can put the virtual regs in ZP") | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										68
									
								
								codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								codeCore/src/prog8/code/target/c64/C64MachineDefinition.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  | package prog8.code.target.c64 | ||||||
|  |  | ||||||
|  | import prog8.code.core.* | ||||||
|  | import prog8.code.target.C64Target | ||||||
|  | import prog8.code.target.cbm.Mflpt5 | ||||||
|  | import java.io.IOException | ||||||
|  | import java.nio.file.Path | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class C64MachineDefinition: IMachineDefinition { | ||||||
|  |  | ||||||
|  |     override val cpu = CpuType.CPU6502 | ||||||
|  |  | ||||||
|  |     override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE | ||||||
|  |     override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE | ||||||
|  |     override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE | ||||||
|  |     override val PROGRAM_LOAD_ADDRESS = 0x0801u | ||||||
|  |  | ||||||
|  |     // the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations) | ||||||
|  |     override var ESTACK_LO = 0xcf00u     //  $cf00-$cf7f inclusive | ||||||
|  |     override var ESTACK_HI = 0xcf80u     //  $cf80-$cfff inclusive | ||||||
|  |  | ||||||
|  |     override val BSSHIGHRAM_START = 0xc000u | ||||||
|  |     override val BSSHIGHRAM_END = ESTACK_LO | ||||||
|  |  | ||||||
|  |     override lateinit var zeropage: Zeropage | ||||||
|  |     override lateinit var golden: GoldenRam | ||||||
|  |  | ||||||
|  |     override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() | ||||||
|  |  | ||||||
|  |     override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { | ||||||
|  |         return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) | ||||||
|  |             listOf("syslib") | ||||||
|  |         else | ||||||
|  |             emptyList() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { | ||||||
|  |         if(selectedEmulator!=1) { | ||||||
|  |             System.err.println("The c64 target only supports the main emulator (Vice).") | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for(emulator in listOf("x64sc", "x64")) { | ||||||
|  |             println("\nStarting C-64 emulator $emulator...") | ||||||
|  |             val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) | ||||||
|  |             val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist, | ||||||
|  |                     "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") | ||||||
|  |             val processb = ProcessBuilder(cmdline).inheritIO() | ||||||
|  |             val process: Process | ||||||
|  |             try { | ||||||
|  |                 process=processb.start() | ||||||
|  |             } catch(x: IOException) { | ||||||
|  |                 continue  // try the next emulator executable | ||||||
|  |             } | ||||||
|  |             process.waitFor() | ||||||
|  |             break | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu | ||||||
|  |  | ||||||
|  |     override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { | ||||||
|  |         zeropage = C64Zeropage(compilerOptions) | ||||||
|  |         golden = GoldenRam(compilerOptions, 0xc000u until ESTACK_LO) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package prog8.code.target.zp | package prog8.code.target.c64 | ||||||
| 
 | 
 | ||||||
| import prog8.code.core.* | import prog8.code.core.* | ||||||
| 
 | 
 | ||||||
| @@ -6,10 +6,9 @@ import prog8.code.core.* | |||||||
| class C64Zeropage(options: CompilationOptions) : Zeropage(options) { | class C64Zeropage(options: CompilationOptions) : Zeropage(options) { | ||||||
| 
 | 
 | ||||||
|     override val SCRATCH_B1 = 0x02u      // temp storage for a single byte |     override val SCRATCH_B1 = 0x02u      // temp storage for a single byte | ||||||
|     override val SCRATCH_REG = 0x03u     // temp storage for a register byte, must be B1+1 |     override val SCRATCH_REG = 0x03u     // temp storage for a register, must be B1+1 | ||||||
|     override val SCRATCH_W1 = 0xfbu      // temp storage 1 for a word  $fb+$fc |     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_W2 = 0xfdu      // temp storage 2 for a word  $fd+$fe | ||||||
|     override val SCRATCH_PTR = 0x9bu     // temp storage for a pointer $9b+$9c |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     init { |     init { | ||||||
| @@ -22,10 +21,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { | |||||||
| 
 | 
 | ||||||
|         if (options.zeropage == ZeropageType.FULL) { |         if (options.zeropage == ZeropageType.FULL) { | ||||||
|             free.addAll(0x02u..0xffu) |             free.addAll(0x02u..0xffu) | ||||||
|             free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u))        // these are updated by IRQ |             free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u)) | ||||||
|  |             free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u))        // these are updated by IRQ | ||||||
|         } else { |         } else { | ||||||
|             if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) { |             if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) { | ||||||
|                 free.addAll(arrayOf( |                 free.addAll(listOf( | ||||||
|                         0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |                         0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, | ||||||
|                         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |                         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, | ||||||
|                         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, |                         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, | ||||||
| @@ -43,7 +43,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { | |||||||
| 
 | 
 | ||||||
|             if (options.zeropage == ZeropageType.FLOATSAFE) { |             if (options.zeropage == ZeropageType.FLOATSAFE) { | ||||||
|                 // remove the zeropage locations used for floating point operations from the free list |                 // remove the zeropage locations used for floating point operations from the free list | ||||||
|                 free.removeAll(arrayOf( |                 free.removeAll(listOf( | ||||||
|                         0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12, |                         0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12, | ||||||
|                         0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, |                         0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, | ||||||
|                         0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, |                         0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, | ||||||
| @@ -56,8 +56,8 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { | |||||||
|             if(options.zeropage!= ZeropageType.DONTUSE) { |             if(options.zeropage!= ZeropageType.DONTUSE) { | ||||||
|                 // add the free Zp addresses |                 // add the free Zp addresses | ||||||
|                 // these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O* |                 // these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O* | ||||||
|                 free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e, |                 free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e, | ||||||
|                         0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6, |                         0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6, | ||||||
|                         0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()}) |                         0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()}) | ||||||
|             } else { |             } else { | ||||||
|                 // don't use the zeropage at all |                 // don't use the zeropage at all | ||||||
| @@ -75,11 +75,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { | |||||||
|             // in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well. |             // in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well. | ||||||
|             allocateCx16VirtualRegisters() |             allocateCx16VirtualRegisters() | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         retainAllowed() |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun allocateCx16VirtualRegisters() { |     override fun allocateCx16VirtualRegisters() { | ||||||
|         // Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses. |         // Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses. | ||||||
|         // However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well. |         // However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well. | ||||||
|         // This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer) |         // This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer) | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package prog8.code.target.encodings | package prog8.code.target.cbm | ||||||
| 
 | 
 | ||||||
| import com.github.michaelbull.result.Ok | import com.github.michaelbull.result.Ok | ||||||
| import com.github.michaelbull.result.Result | import com.github.michaelbull.result.Result | ||||||
| @@ -197,7 +197,6 @@ object AtasciiEncoding { | |||||||
|     fun encode(str: String): Result<List<UByte>, CharConversionException> { |     fun encode(str: String): Result<List<UByte>, CharConversionException> { | ||||||
|         val mapped = str.map { chr -> |         val mapped = str.map { chr -> | ||||||
|             when (chr) { |             when (chr) { | ||||||
|                 '\r' -> 0x9bu |  | ||||||
|                 '\u0000' -> 0u |                 '\u0000' -> 0u | ||||||
|                 in '\u8000'..'\u80ff' -> { |                 in '\u8000'..'\u80ff' -> { | ||||||
|                     // special case: take the lower 8 bit hex value directly |                     // special case: take the lower 8 bit hex value directly | ||||||
							
								
								
									
										18
									
								
								codeCore/src/prog8/code/target/cbm/CbmMemorySizer.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								codeCore/src/prog8/code/target/cbm/CbmMemorySizer.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | package prog8.code.target.cbm | ||||||
|  |  | ||||||
|  | import prog8.code.core.* | ||||||
|  |  | ||||||
|  |  | ||||||
|  | internal object CbmMemorySizer: IMemSizer { | ||||||
|  |     override fun memorySize(dt: DataType): Int { | ||||||
|  |         return when(dt) { | ||||||
|  |             in ByteDatatypes -> 1 | ||||||
|  |             in WordDatatypes, in PassByReferenceDatatypes -> 2 | ||||||
|  |             DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE | ||||||
|  |             else -> Int.MIN_VALUE | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun memorySize(arrayDt: DataType, numElements: Int) = | ||||||
|  |         memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package prog8.code.target.encodings | package prog8.code.target.cbm | ||||||
| 
 | 
 | ||||||
| import com.github.michaelbull.result.Err | import com.github.michaelbull.result.Err | ||||||
| import com.github.michaelbull.result.Ok | import com.github.michaelbull.result.Ok | ||||||
| @@ -6,16 +6,14 @@ import com.github.michaelbull.result.Result | |||||||
| import java.io.CharConversionException | import java.io.CharConversionException | ||||||
| import java.nio.charset.Charset | import java.nio.charset.Charset | ||||||
| 
 | 
 | ||||||
|  | object IsoEncoding { | ||||||
|  |     val charset: Charset = Charset.forName("ISO-8859-15") | ||||||
| 
 | 
 | ||||||
| open class IsoEncodingBase(charsetName: String) { |     fun encode(str: String): Result<List<UByte>, CharConversionException> { | ||||||
|     val charset: Charset = Charset.forName(charsetName) |  | ||||||
| 
 |  | ||||||
|     fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> { |  | ||||||
|         return try { |         return try { | ||||||
|             val mapped = str.map { chr -> |             val mapped = str.map { chr -> | ||||||
|                 when (chr) { |                 when (chr) { | ||||||
|                     '\u0000' -> 0u |                     '\u0000' -> 0u | ||||||
|                     '\n' -> if(newlineToCarriageReturn) 13u else 10u |  | ||||||
|                     in '\u8000'..'\u80ff' -> { |                     in '\u8000'..'\u80ff' -> { | ||||||
|                         // special case: take the lower 8 bit hex value directly |                         // special case: take the lower 8 bit hex value directly | ||||||
|                         (chr.code - 0x8000).toUByte() |                         (chr.code - 0x8000).toUByte() | ||||||
| @@ -29,21 +27,11 @@ open class IsoEncodingBase(charsetName: String) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> { |     fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> { | ||||||
|         return try { |         return try { | ||||||
|             Ok(String(bytes.map { |             Ok(String(bytes.map { it.toByte() }.toByteArray(), charset)) | ||||||
|                 when(it) { |  | ||||||
|                     13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13 |  | ||||||
|                     else -> it.toByte() |  | ||||||
|                 } |  | ||||||
|             }.toByteArray(), charset)) |  | ||||||
|         } catch (ce: CharConversionException) { |         } catch (ce: CharConversionException) { | ||||||
|             Err(ce) |             Err(ce) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| object IsoEncoding: IsoEncodingBase("ISO-8859-15") |  | ||||||
| object IsoCyrillicEncoding: IsoEncodingBase("ISO-8859-5") |  | ||||||
| object IsoEasternEncoding: IsoEncodingBase("ISO-8859-16") |  | ||||||
| @@ -1,9 +1,10 @@ | |||||||
| package prog8.code.target | package prog8.code.target.cbm | ||||||
| 
 | 
 | ||||||
| import prog8.code.core.InternalCompilerException | import prog8.code.core.InternalCompilerException | ||||||
| import kotlin.math.absoluteValue | import kotlin.math.absoluteValue | ||||||
| import kotlin.math.pow | import kotlin.math.pow | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) { | data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) { | ||||||
| 
 | 
 | ||||||
|     companion object { |     companion object { | ||||||
| @@ -18,7 +19,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 |             // and https://en.wikipedia.org/wiki/IEEE_754-1985 | ||||||
| 
 | 
 | ||||||
|             val flt = num.toDouble() |             val flt = num.toDouble() | ||||||
|             if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE) |             if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE) | ||||||
|                 throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this") |                 throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this") | ||||||
|             if (flt == 0.0) |             if (flt == 0.0) | ||||||
|                 return zero |                 return zero | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package prog8.code.target.encodings | package prog8.code.target.cbm | ||||||
| 
 | 
 | ||||||
| import com.github.michaelbull.result.Err | import com.github.michaelbull.result.Err | ||||||
| import com.github.michaelbull.result.Ok | import com.github.michaelbull.result.Ok | ||||||
| @@ -7,7 +7,7 @@ import java.io.CharConversionException | |||||||
| 
 | 
 | ||||||
| object PetsciiEncoding { | object PetsciiEncoding { | ||||||
| 
 | 
 | ||||||
|     // decoding:  from Petscii/Screencodes (0-255) to Unicode |     // decoding:  from Petscii/Screencodes (0-255) to unicode | ||||||
|     // character tables used from https://github.com/irmen/cbmcodecs2 |     // character tables used from https://github.com/irmen/cbmcodecs2 | ||||||
| 
 | 
 | ||||||
|     private val decodingPetsciiLowercase = charArrayOf( |     private val decodingPetsciiLowercase = charArrayOf( | ||||||
| @@ -21,10 +21,10 @@ object PetsciiEncoding { | |||||||
|         '\ufffe',    //       0x07 -> UNDEFINED |         '\ufffe',    //       0x07 -> UNDEFINED | ||||||
|         '\uf118',    //       0x08 -> DISABLE CHARACTER SET SWITCHING (CUS) |         '\uf118',    //       0x08 -> DISABLE CHARACTER SET SWITCHING (CUS) | ||||||
|         '\uf119',    //       0x09 -> ENABLE CHARACTER SET SWITCHING (CUS) |         '\uf119',    //       0x09 -> ENABLE CHARACTER SET SWITCHING (CUS) | ||||||
|         '\n',        //       0x0A -> LINE FEED (RETURN) |         '\ufffe',    //       0x0A -> UNDEFINED | ||||||
|         '\ufffe',    //       0x0B -> UNDEFINED |         '\ufffe',    //       0x0B -> UNDEFINED | ||||||
|         '\ufffe',    //       0x0C -> UNDEFINED |         '\ufffe',    //       0x0C -> UNDEFINED | ||||||
|         '\n'    ,    //       0x0D -> LINE FEED (RETURN) |         '\r'    ,    //       0x0D -> CARRIAGE RETURN | ||||||
|         '\u000e',    //       0x0E -> SHIFT OUT |         '\u000e',    //       0x0E -> SHIFT OUT | ||||||
|         '\ufffe',    //       0x0F -> UNDEFINED |         '\ufffe',    //       0x0F -> UNDEFINED | ||||||
|         '\ufffe',    //       0x10 -> UNDEFINED |         '\ufffe',    //       0x10 -> UNDEFINED | ||||||
| @@ -152,7 +152,7 @@ object PetsciiEncoding { | |||||||
|         '\uf113',    //      0x8A -> FUNCTION KEY 4 (CUS) |         '\uf113',    //      0x8A -> FUNCTION KEY 4 (CUS) | ||||||
|         '\uf115',    //      0x8B -> FUNCTION KEY 6 (CUS) |         '\uf115',    //      0x8B -> FUNCTION KEY 6 (CUS) | ||||||
|         '\uf117',    //      0x8C -> FUNCTION KEY 8 (CUS) |         '\uf117',    //      0x8C -> FUNCTION KEY 8 (CUS) | ||||||
|         '\r'    ,    //       0x8D -> CARRIAGE RETURN (SHIFT-RETURN) |         '\n'    ,    //       0x8D -> LINE FEED | ||||||
|         '\u000f',    //      0x8E -> SHIFT IN |         '\u000f',    //      0x8E -> SHIFT IN | ||||||
|         '\ufffe',    //       0x8F -> UNDEFINED |         '\ufffe',    //       0x8F -> UNDEFINED | ||||||
|         '\uf105',    //       0x90 -> BLACK COLOR SWITCH (CUS) |         '\uf105',    //       0x90 -> BLACK COLOR SWITCH (CUS) | ||||||
| @@ -283,7 +283,7 @@ object PetsciiEncoding { | |||||||
|         '\ufffe',    //       0x0A -> UNDEFINED |         '\ufffe',    //       0x0A -> UNDEFINED | ||||||
|         '\ufffe',    //       0x0B -> UNDEFINED |         '\ufffe',    //       0x0B -> UNDEFINED | ||||||
|         '\ufffe',    //       0x0C -> UNDEFINED |         '\ufffe',    //       0x0C -> UNDEFINED | ||||||
|         '\n'    ,    //       0x0D -> LINE FEED (RETURN) |         '\r'    ,    //       0x0D -> CARRIAGE RETURN | ||||||
|         '\u000e',    //       0x0E -> SHIFT OUT |         '\u000e',    //       0x0E -> SHIFT OUT | ||||||
|         '\ufffe',    //       0x0F -> UNDEFINED |         '\ufffe',    //       0x0F -> UNDEFINED | ||||||
|         '\ufffe',    //       0x10 -> UNDEFINED |         '\ufffe',    //       0x10 -> UNDEFINED | ||||||
| @@ -411,7 +411,7 @@ object PetsciiEncoding { | |||||||
|         '\uf113',    //       0x8A -> FUNCTION KEY 4 (CUS) |         '\uf113',    //       0x8A -> FUNCTION KEY 4 (CUS) | ||||||
|         '\uf115',    //       0x8B -> FUNCTION KEY 6 (CUS) |         '\uf115',    //       0x8B -> FUNCTION KEY 6 (CUS) | ||||||
|         '\uf117',    //       0x8C -> FUNCTION KEY 8 (CUS) |         '\uf117',    //       0x8C -> FUNCTION KEY 8 (CUS) | ||||||
|         '\r'    ,    //       0x8D -> CARRIAGE RETURN (SHIFT-RETURN) |         '\n'    ,    //       0x8D -> LINE FEED | ||||||
|         '\u000f',    //       0x8E -> SHIFT IN |         '\u000f',    //       0x8E -> SHIFT IN | ||||||
|         '\ufffe',    //       0x8F -> UNDEFINED |         '\ufffe',    //       0x8F -> UNDEFINED | ||||||
|         '\uf105',    //       0x90 -> BLACK COLOR SWITCH (CUS) |         '\uf105',    //       0x90 -> BLACK COLOR SWITCH (CUS) | ||||||
| @@ -1061,7 +1061,6 @@ object PetsciiEncoding { | |||||||
|             '}' -> '├' |             '}' -> '├' | ||||||
|             '|' -> '│' |             '|' -> '│' | ||||||
|             '\\' -> '╲' |             '\\' -> '╲' | ||||||
|             '\r' -> '\n'        // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d) |  | ||||||
|             else -> chr |             else -> chr | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @@ -1077,10 +1076,7 @@ object PetsciiEncoding { | |||||||
|                 } |                 } | ||||||
|                 else -> { |                 else -> { | ||||||
|                     val case = if (lowercase) "lower" else "upper" |                     val case = if (lowercase) "lower" else "upper" | ||||||
|                     if(chr.isISOControl()) |                     throw CharConversionException("no ${case}Petscii character for '${chr}' (${chr.code})") | ||||||
|                         throw CharConversionException("no ${case}Petscii character for char #${chr.code}") |  | ||||||
|                     else |  | ||||||
|                         throw CharConversionException("no ${case}Petscii character for char #${chr.code} '${chr}'") |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1089,7 +1085,7 @@ object PetsciiEncoding { | |||||||
|             Ok(text.map { |             Ok(text.map { | ||||||
|                 try { |                 try { | ||||||
|                     encodeChar(it, lowercase) |                     encodeChar(it, lowercase) | ||||||
|                 } catch (_: CharConversionException) { |                 } catch (x: CharConversionException) { | ||||||
|                     encodeChar(it, !lowercase) |                     encodeChar(it, !lowercase) | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
| @@ -1117,18 +1113,13 @@ object PetsciiEncoding { | |||||||
|             val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr] |             val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr] | ||||||
|             return screencode?.toUByte() ?: when (chr) { |             return screencode?.toUByte() ?: when (chr) { | ||||||
|                 '\u0000' -> 0u |                 '\u0000' -> 0u | ||||||
|                 '\n' -> 141u |  | ||||||
|                 '\r' -> 141u |  | ||||||
|                 in '\u8000'..'\u80ff' -> { |                 in '\u8000'..'\u80ff' -> { | ||||||
|                     // special case: take the lower 8 bit hex value directly |                     // special case: take the lower 8 bit hex value directly | ||||||
|                     (chr.code - 0x8000).toUByte() |                     (chr.code - 0x8000).toUByte() | ||||||
|                 } |                 } | ||||||
|                 else -> { |                 else -> { | ||||||
|                     val case = if (lowercase) "lower" else "upper" |                     val case = if (lowercase) "lower" else "upper" | ||||||
|                     if(chr.isISOControl()) |                     throw CharConversionException("no ${case}Screencode character for '${chr}' (${chr.code})") | ||||||
|                         throw CharConversionException("no ${case}Screencode character for char #${chr.code}") |  | ||||||
|                     else |  | ||||||
|                         throw CharConversionException("no ${case}Screencode character for char #${chr.code} '${chr}'") |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1137,7 +1128,7 @@ object PetsciiEncoding { | |||||||
|             Ok(text.map { |             Ok(text.map { | ||||||
|                 try { |                 try { | ||||||
|                     encodeChar(it, lowercase) |                     encodeChar(it, lowercase) | ||||||
|                 } catch (_: CharConversionException) { |                 } catch (x: CharConversionException) { | ||||||
|                     encodeChar(it, !lowercase) |                     encodeChar(it, !lowercase) | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
| @@ -1159,16 +1150,16 @@ object PetsciiEncoding { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> { |     fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> { | ||||||
|         val code: UInt = when { |         val code: UInt = when { | ||||||
|             petsciicode <= 0x1fu -> petsciicode + 128u |             petscii_code <= 0x1fu -> petscii_code + 128u | ||||||
|             petsciicode <= 0x3fu -> petsciicode.toUInt() |             petscii_code <= 0x3fu -> petscii_code.toUInt() | ||||||
|             petsciicode <= 0x5fu -> petsciicode - 64u |             petscii_code <= 0x5fu -> petscii_code - 64u | ||||||
|             petsciicode <= 0x7fu -> petsciicode - 32u |             petscii_code <= 0x7fu -> petscii_code - 32u | ||||||
|             petsciicode <= 0x9fu -> petsciicode + 64u |             petscii_code <= 0x9fu -> petscii_code + 64u | ||||||
|             petsciicode <= 0xbfu -> petsciicode - 64u |             petscii_code <= 0xbfu -> petscii_code - 64u | ||||||
|             petsciicode <= 0xfeu -> petsciicode - 128u |             petscii_code <= 0xfeu -> petscii_code - 128u | ||||||
|             petsciicode == 255.toUByte() -> 95u |             petscii_code == 255.toUByte() -> 95u | ||||||
|             else -> return Err(CharConversionException("petscii code out of range")) |             else -> return Err(CharConversionException("petscii code out of range")) | ||||||
|         } |         } | ||||||
|         if(inverseVideo) { |         if(inverseVideo) { | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user