mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 01:23:34 +00:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
e55a675d2e |
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']
|
|
28
.github/workflows/all-ci.yml
vendored
28
.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 11
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
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
|
|
||||||
|
|
||||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,12 +1,8 @@
|
|||||||
.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/
|
|
||||||
.*cache/
|
.*cache/
|
||||||
*.directory
|
*.directory
|
||||||
*.prg
|
*.prg
|
||||||
@ -15,17 +11,16 @@ out/
|
|||||||
*.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
|
||||||
@ -34,6 +29,6 @@ parsetab.py
|
|||||||
compiler/lib/
|
compiler/lib/
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
|
/prog8compiler.jar
|
||||||
sd*.img
|
sd*.img
|
||||||
*.d64
|
*.d64
|
||||||
|
|
||||||
|
1457
.idea/inspectionProfiles/Project_Default.xml
generated
1457
.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.1" />
|
|
||||||
<option name="languageVersion" value="2.1" />
|
|
||||||
</component>
|
|
||||||
<component name="KotlinCompilerSettings">
|
|
||||||
<option name="additionalArguments" value="-Xwhen-guards" />
|
|
||||||
</component>
|
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="2.1.10" />
|
<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.1.20" />
|
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.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.1.20/kotlin-stdlib-jdk7-2.1.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.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
|
||||||
</JAVADOC>
|
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" />
|
<root url="jar://$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.1.20/kotlin-stdlib-jdk7-2.1.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.12.1/gson-2.12.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
|
|
||||||
</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>
|
25
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
25
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
@ -1,18 +1,21 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
|
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.5.5" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-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/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.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/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-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$/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-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.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-core-jvm/1.8.0/kotlinx-coroutines-core-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$/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>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
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.0.1" />
|
<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.0.1/kotlin-result-jvm-2.0.1.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/1.9.22/kotlin-stdlib-1.9.22.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.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
|
<SOURCES />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
|
||||||
</JAVADOC>
|
|
||||||
<SOURCES>
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
|
||||||
</SOURCES>
|
|
||||||
</library>
|
</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>
|
10
.idea/misc.xml
generated
10
.idea/misc.xml
generated
@ -4,26 +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>
|
||||||
</project>
|
</project>
|
6
.idea/modules.xml
generated
6
.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,12 +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$/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"
|
|
||||||
|
|
74
README.md
74
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,32 +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)
|
||||||
- floating point math is supported on certain targets
|
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||||
- 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:*
|
||||||
|
|
||||||
@ -92,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, C64OS, ...
|
|
||||||
- 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)
|
||||||
|
|
||||||
|
|
||||||
@ -129,13 +91,14 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
%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 {
|
||||||
@ -171,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:
|
||||||
|
|
||||||

|

|
||||||
|
@ -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,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,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,117 +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
|
|
||||||
|
|
||||||
%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")
|
|
||||||
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, 300)
|
|
||||||
benchmark_number++
|
|
||||||
|
|
||||||
; announce_benchmark("circles with kernal")
|
|
||||||
; benchmark_score[benchmark_number] = circles.draw(true, 300)
|
|
||||||
; benchmark_number++
|
|
||||||
|
|
||||||
announce_benchmark("text-elite")
|
|
||||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
|
||||||
benchmark_number++
|
|
||||||
|
|
||||||
announce_benchmark("sprites-coroutines-defer")
|
|
||||||
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
|
||||||
benchmark_number++
|
|
||||||
|
|
||||||
benchmark_names[benchmark_number] = 0
|
|
||||||
benchmark_score[benchmark_number] = 0
|
|
||||||
|
|
||||||
cx16.set_screen_mode(3)
|
|
||||||
txt.uppercase()
|
|
||||||
txt.color2(1, 6)
|
|
||||||
uword final_score
|
|
||||||
benchmark_number = 0
|
|
||||||
txt.print("\nscore benchmark\n\n")
|
|
||||||
do {
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(benchmark_score[benchmark_number])
|
|
||||||
txt.column(6)
|
|
||||||
txt.print(benchmark_names[benchmark_number])
|
|
||||||
final_score += benchmark_score[benchmark_number]
|
|
||||||
txt.nl()
|
|
||||||
benchmark_number++
|
|
||||||
} until benchmark_names[benchmark_number]==0
|
|
||||||
|
|
||||||
txt.print("\n\nfinal score : ")
|
|
||||||
txt.print_uw(final_score)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
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,34 +0,0 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
|
||||||
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
kotlin("jvm") version "2.1.20"
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
apply(plugin="kotlin")
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
compilerOptions {
|
|
||||||
freeCompilerArgs = listOf("-Xwhen-guards")
|
|
||||||
jvmTarget = JvmTarget.JVM_11
|
|
||||||
}
|
|
||||||
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,24 +0,0 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
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.0.1")
|
|
||||||
}
|
|
||||||
|
|
||||||
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,31 +0,0 @@
|
|||||||
package prog8.code
|
|
||||||
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.absolute
|
|
||||||
|
|
||||||
|
|
||||||
// the automatically generated module where all string literals are interned to:
|
|
||||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
|
||||||
|
|
||||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
|
||||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the absolute path of the given path,
|
|
||||||
* where links are replaced by the actual directories,
|
|
||||||
* and containing no redundant path elements.
|
|
||||||
* If the path doesn't refer to an existing directory or file on the file system,
|
|
||||||
* it is returned unchanged.
|
|
||||||
*/
|
|
||||||
fun Path.sanitize(): Path {
|
|
||||||
return try {
|
|
||||||
this.toRealPath().normalize()
|
|
||||||
} catch (_: java.nio.file.NoSuchFileException) {
|
|
||||||
this.absolute().normalize()
|
|
||||||
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
|
||||||
} catch (iox: IOException) {
|
|
||||||
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
package prog8.code
|
package prog8.code
|
||||||
|
|
||||||
import prog8.code.ast.PtAsmSub
|
|
||||||
import prog8.code.ast.PtNode
|
import prog8.code.ast.PtNode
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
@ -39,7 +38,6 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
}
|
}
|
||||||
|
|
||||||
val allVariables: Collection<StStaticVariable> by lazy {
|
val allVariables: Collection<StStaticVariable> by lazy {
|
||||||
// can't be done with a generic function because those don't support local recursive functions yet
|
|
||||||
val vars = mutableListOf<StStaticVariable>()
|
val vars = mutableListOf<StStaticVariable>()
|
||||||
fun collect(node: StNode) {
|
fun collect(node: StNode) {
|
||||||
for(child in node.children) {
|
for(child in node.children) {
|
||||||
@ -54,7 +52,6 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
}
|
}
|
||||||
|
|
||||||
val allMemMappedVariables: Collection<StMemVar> by lazy {
|
val allMemMappedVariables: Collection<StMemVar> by lazy {
|
||||||
// can't be done with a generic function because those don't support local recursive functions yet
|
|
||||||
val vars = mutableListOf<StMemVar>()
|
val vars = mutableListOf<StMemVar>()
|
||||||
fun collect(node: StNode) {
|
fun collect(node: StNode) {
|
||||||
for(child in node.children) {
|
for(child in node.children) {
|
||||||
@ -69,7 +66,6 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
}
|
}
|
||||||
|
|
||||||
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
||||||
// can't be done with a generic function because those don't support local recursive functions yet
|
|
||||||
val vars = mutableListOf<StMemorySlab>()
|
val vars = mutableListOf<StMemorySlab>()
|
||||||
fun collect(node: StNode) {
|
fun collect(node: StNode) {
|
||||||
for(child in node.children) {
|
for(child in node.children) {
|
||||||
@ -84,15 +80,6 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: String) = flat[scopedName]
|
override fun lookup(scopedName: String) = flat[scopedName]
|
||||||
|
|
||||||
fun getLength(name: String): Int? {
|
|
||||||
return when(val node = flat[name]) {
|
|
||||||
is StMemVar -> node.length
|
|
||||||
is StMemorySlab -> node.size.toInt()
|
|
||||||
is StStaticVariable -> node.length
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,7 +88,7 @@ enum class StNodeType {
|
|||||||
// MODULE, // not used with current scoping rules
|
// MODULE, // not used with current scoping rules
|
||||||
BLOCK,
|
BLOCK,
|
||||||
SUBROUTINE,
|
SUBROUTINE,
|
||||||
EXTSUB,
|
ROMSUB,
|
||||||
LABEL,
|
LABEL,
|
||||||
STATICVAR,
|
STATICVAR,
|
||||||
MEMVAR,
|
MEMVAR,
|
||||||
@ -113,7 +100,7 @@ enum class StNodeType {
|
|||||||
|
|
||||||
open class StNode(val name: String,
|
open class StNode(val name: String,
|
||||||
val type: StNodeType,
|
val type: StNodeType,
|
||||||
val astNode: PtNode?,
|
val astNode: PtNode,
|
||||||
val children: MutableMap<String, StNode> = mutableMapOf()
|
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -182,71 +169,54 @@ open class StNode(val name: String,
|
|||||||
|
|
||||||
class StStaticVariable(name: String,
|
class StStaticVariable(name: String,
|
||||||
val dt: DataType,
|
val dt: DataType,
|
||||||
val initializationStringValue: StString?,
|
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
|
||||||
val initializationArrayValue: StArray?,
|
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 length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
val zpwish: ZeropageWish, // used in the variable allocator
|
val zpwish: ZeropageWish, // used in the variable allocator
|
||||||
val align: Int,
|
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
|
||||||
val dirty: Boolean,
|
|
||||||
astNode: PtNode?) : StNode(name, StNodeType.STATICVAR, astNode) {
|
|
||||||
|
|
||||||
var initializationNumericValue: Double? = null
|
val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null
|
||||||
private set
|
|
||||||
|
|
||||||
fun setOnetimeInitNumeric(number: Double) {
|
|
||||||
// In certain cases the init value of an existing var should be updated,
|
|
||||||
// so we can't ask this as a constructor parameter.
|
|
||||||
// This has to do with the way Prog8 does the (re)initialization of such variables: via code assignment statements.
|
|
||||||
// Certain codegens might want to put them back into the variable directly.
|
|
||||||
// For strings and arrays this doesn't occur - these are always already specced at creation time.
|
|
||||||
|
|
||||||
require(number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear" }
|
|
||||||
initializationNumericValue = number
|
|
||||||
}
|
|
||||||
|
|
||||||
val uninitialized: Boolean
|
|
||||||
get() = initializationArrayValue==null && initializationStringValue==null && initializationNumericValue==null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(length!=null) {
|
if(length!=null) {
|
||||||
require(initializationNumericValue == null)
|
require(onetimeInitializationNumericValue == null)
|
||||||
if(initializationArrayValue!=null)
|
if(onetimeInitializationArrayValue!=null)
|
||||||
require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length)
|
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
|
||||||
}
|
}
|
||||||
if(initializationNumericValue!=null) {
|
if(onetimeInitializationNumericValue!=null) {
|
||||||
require(dt.isNumericOrBool)
|
require(dt in NumericDatatypes)
|
||||||
|
require(onetimeInitializationNumericValue!=0.0) { "zero as init value should just remain uninitialized"}
|
||||||
}
|
}
|
||||||
if(initializationArrayValue!=null) {
|
if(onetimeInitializationArrayValue!=null) {
|
||||||
require(dt.isArray)
|
require(dt in ArrayDatatypes)
|
||||||
require(length == initializationArrayValue.size)
|
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(initializationStringValue!=null) {
|
if(onetimeInitializationStringValue!=null) {
|
||||||
require(dt.isString)
|
require(dt == DataType.STR)
|
||||||
require(length == initializationStringValue.first.length + 1)
|
require(length == onetimeInitializationStringValue.first.length+1)
|
||||||
}
|
|
||||||
if(align > 0) {
|
|
||||||
require(dt.isString || dt.isArray)
|
|
||||||
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode?) :
|
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
|
||||||
StNode(name, StNodeType.CONSTANT, astNode)
|
StNode(name, StNodeType.CONSTANT, astNode) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class StMemVar(name: String,
|
class StMemVar(name: String,
|
||||||
val dt: DataType,
|
val dt: DataType,
|
||||||
val address: UInt,
|
val address: UInt,
|
||||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
astNode: PtNode?) :
|
astNode: PtNode) :
|
||||||
StNode(name, StNodeType.MEMVAR, astNode) {
|
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||||
|
|
||||||
init{
|
init{
|
||||||
require(!dt.isString)
|
if(dt in ArrayDatatypes || dt == DataType.STR)
|
||||||
if(dt.isStringly && !dt.isWord)
|
require(length!=null) { "memory mapped array or string must have known length" }
|
||||||
requireNotNull(length)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,33 +224,28 @@ class StMemorySlab(
|
|||||||
name: String,
|
name: String,
|
||||||
val size: UInt,
|
val size: UInt,
|
||||||
val align: UInt,
|
val align: UInt,
|
||||||
astNode: PtNode?
|
astNode: PtNode
|
||||||
):
|
):
|
||||||
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
StNode(name, StNodeType.MEMORYSLAB, astNode) {
|
||||||
|
|
||||||
|
|
||||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returns: List<DataType>, astNode: PtNode) :
|
|
||||||
StNode(name, StNodeType.SUBROUTINE, astNode)
|
|
||||||
|
|
||||||
|
|
||||||
class StExtSub(name: String,
|
|
||||||
val address: PtAsmSub.Address?, // null in case of asmsub, specified in case of extsub.
|
|
||||||
val parameters: List<StExtSubParameter>,
|
|
||||||
val returns: List<StExtSubParameter>,
|
|
||||||
astNode: PtNode) :
|
|
||||||
StNode(name, StNodeType.EXTSUB, astNode)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class StSubroutineParameter(val name: String, val type: DataType, val register: RegisterOrPair?)
|
|
||||||
class StExtSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
|
||||||
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?) {
|
|
||||||
init {
|
|
||||||
if(number!=null) require(addressOfSymbol==null && boolean==null)
|
|
||||||
if(addressOfSymbol!=null) require(number==null && boolean==null)
|
|
||||||
if(boolean!=null) require(addressOfSymbol==null && number==null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 StString = Pair<String, Encoding>
|
||||||
typealias StArray = List<StArrayElement>
|
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,10 +22,12 @@ 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?.toInt(), it))
|
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||||
@ -36,19 +37,18 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
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)
|
||||||
@ -57,8 +57,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
|
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
|
||||||
}
|
}
|
||||||
is PtSub -> {
|
is PtSub -> {
|
||||||
val params = node.parameters.map {StSubroutineParameter(it.name, it.type, it.register) }
|
val params = node.parameters.map {StSubroutineParameter(it.name, it.type) }
|
||||||
StSub(node.name, params, node.returns, node)
|
StSub(node.name, params, node.returntype, node)
|
||||||
}
|
}
|
||||||
is PtVariable -> {
|
is PtVariable -> {
|
||||||
val initialNumeric: Double?
|
val initialNumeric: Double?
|
||||||
@ -67,26 +67,24 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
val numElements: Int?
|
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 // include the terminating 0-byte
|
||||||
}
|
}
|
||||||
is PtArray -> {
|
is PtArray -> {
|
||||||
initialArray = makeInitialArray(value)
|
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
|
|
||||||
require(node.arraySize?.toInt()==numElements)
|
require(node.arraySize?.toInt()==numElements)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
require(value is PtNumber)
|
|
||||||
initialString = null
|
initialString = null
|
||||||
initialArray = null
|
initialArray = null
|
||||||
val number = value.number
|
|
||||||
initialNumeric = number
|
|
||||||
numElements = node.arraySize?.toInt()
|
numElements = node.arraySize?.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,13 +94,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
initialString = null
|
initialString = null
|
||||||
numElements = node.arraySize?.toInt()
|
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.toInt(), node.dirty,node)
|
|
||||||
if(initialNumeric!=null)
|
|
||||||
stVar.setOnetimeInitNumeric(initialNumeric)
|
|
||||||
stVar
|
|
||||||
}
|
}
|
||||||
is PtBuiltinFunctionCall -> {
|
is PtBuiltinFunctionCall -> {
|
||||||
if(node.name=="memory") {
|
if(node.name=="memory") {
|
||||||
@ -112,7 +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("prog8_memoryslab_$slabname", size, align, node))
|
scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||||
}
|
}
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -120,26 +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 makeInitialArray(value: PtArray): List<StArrayElement> {
|
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
|
||||||
return value.children.map {
|
return value.children.map {
|
||||||
when(it) {
|
when(it) {
|
||||||
is PtAddressOf -> {
|
is PtAddressOf -> StArrayElement(null, it.identifier.name)
|
||||||
if(it.isFromArrayElement)
|
is PtIdentifier -> StArrayElement(null, it.name)
|
||||||
TODO("address-of array element $it in initial array value")
|
is PtNumber -> StArrayElement(it.number, null)
|
||||||
StArrayElement(null, it.identifier.name, null)
|
|
||||||
}
|
|
||||||
is PtNumber -> StArrayElement(it.number, null, null)
|
|
||||||
is PtBool -> StArrayElement(null, null, it.value)
|
|
||||||
else -> throw AssemblyError("invalid array element $it")
|
else -> throw AssemblyError("invalid array element $it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,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.
|
||||||
@ -37,17 +37,16 @@ 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
|
if(namedParent is PtProgram)
|
||||||
return if(namedParent is PtProgram)
|
name
|
||||||
name
|
else {
|
||||||
else {
|
while (namedParent !is PtNamedNode)
|
||||||
while (namedParent !is PtNamedNode)
|
namedParent = namedParent.parent
|
||||||
namedParent = namedParent.parent
|
namedParent.scopedName + "." + name
|
||||||
namedParent.scopedName + "." + name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,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)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -99,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)
|
||||||
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
|||||||
package prog8.code.ast
|
package prog8.code.ast
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.Encoding
|
||||||
|
import prog8.code.core.NumericDatatypes
|
||||||
|
import prog8.code.core.Position
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.truncate
|
import kotlin.math.round
|
||||||
|
|
||||||
|
|
||||||
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(type.isUndefined) {
|
if(type==DataType.BOOL)
|
||||||
|
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||||
|
if(type==DataType.UNDEFINED) {
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
when(this) {
|
when(this) {
|
||||||
is PtBuiltinFunctionCall -> { /* void function call */ }
|
is PtBuiltinFunctionCall -> { /* void function call */ }
|
||||||
@ -22,43 +27,14 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
|
|
||||||
infix fun isSameAs(other: PtExpression): Boolean {
|
infix fun isSameAs(other: PtExpression): Boolean {
|
||||||
return when(this) {
|
return when(this) {
|
||||||
is PtAddressOf -> {
|
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
||||||
if(other !is PtAddressOf)
|
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
|
||||||
return false
|
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||||
if (other.type!==type || !(other.identifier isSameAs identifier))
|
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||||
return false
|
|
||||||
if(other.children.size!=children.size)
|
|
||||||
return false
|
|
||||||
if(children.size==1)
|
|
||||||
return true
|
|
||||||
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
|
|
||||||
}
|
|
||||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
|
|
||||||
is PtBinaryExpression -> {
|
|
||||||
if(other !is PtBinaryExpression || other.operator!=operator)
|
|
||||||
false
|
|
||||||
else if(operator in AssociativeOperators)
|
|
||||||
(other.left isSameAs left && other.right isSameAs right) || (other.left isSameAs right && other.right isSameAs left)
|
|
||||||
else
|
|
||||||
other.left isSameAs left && other.right isSameAs right
|
|
||||||
}
|
|
||||||
is PtContainmentCheck -> {
|
|
||||||
if(other !is PtContainmentCheck || other.type != type || !(other.needle isSameAs needle))
|
|
||||||
false
|
|
||||||
else {
|
|
||||||
if(haystackHeapVar!=null)
|
|
||||||
other.haystackHeapVar!=null && other.haystackHeapVar!! isSameAs haystackHeapVar!!
|
|
||||||
else if(haystackValues!=null)
|
|
||||||
other.haystackValues!=null && other.haystackValues!! isSameAs haystackValues!!
|
|
||||||
else
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||||
is PtIrRegister -> other is PtIrRegister && other.type==type && other.register==register
|
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
|
||||||
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
|
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
|
||||||
is PtNumber -> other is PtNumber && other.type==type && other.number==number
|
is PtNumber -> other is PtNumber && other.type==type && other.number==number
|
||||||
is PtBool -> other is PtBool && other.value==value
|
|
||||||
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
|
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 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
|
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
|
||||||
@ -66,46 +42,40 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun isSameAs(target: PtAssignTarget): Boolean = when {
|
infix fun isSameAs(target: PtAssignTarget): Boolean {
|
||||||
target.memory != null && this is PtMemoryByte -> {
|
return when {
|
||||||
target.memory!!.address isSameAs this.address
|
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
|
||||||
}
|
}
|
||||||
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 && this.splitWords==target.array!!.splitWords
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
|
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt()
|
||||||
fun asConstValue(): Double? = (this as? PtNumber)?.number ?: (this as? PtBool)?.asInt()?.toDouble()
|
|
||||||
|
|
||||||
fun isSimple(): Boolean {
|
fun isSimple(): Boolean {
|
||||||
return when(this) {
|
return when(this) {
|
||||||
is PtAddressOf -> this.arrayIndexExpr==null || this.arrayIndexExpr?.isSimple()==true
|
is PtAddressOf -> true
|
||||||
is PtArray -> true
|
is PtArray -> true
|
||||||
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
||||||
is PtBinaryExpression -> false
|
is PtBinaryExpression -> false
|
||||||
is PtBuiltinFunctionCall -> {
|
is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
|
||||||
when (name) {
|
|
||||||
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is PtContainmentCheck -> false
|
is PtContainmentCheck -> false
|
||||||
is PtFunctionCall -> false
|
is PtFunctionCall -> false
|
||||||
is PtIdentifier -> true
|
is PtIdentifier -> true
|
||||||
is PtIrRegister -> true
|
is PtMachineRegister -> true
|
||||||
is PtMemoryByte -> address is PtNumber
|
is PtMemoryByte -> address is PtNumber || address is PtIdentifier
|
||||||
is PtBool -> true
|
|
||||||
is PtNumber -> true
|
is PtNumber -> true
|
||||||
is PtPrefix -> value.isSimple()
|
is PtPrefix -> value.isSimple()
|
||||||
is PtRange -> true
|
is PtRange -> true
|
||||||
is PtString -> true
|
is PtString -> true
|
||||||
is PtTypeCast -> value.isSimple()
|
is PtTypeCast -> value.isSimple()
|
||||||
is PtIfExpression -> condition.isSimple() && truevalue.isSimple() && falsevalue.isSimple()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +97,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
|
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
|
||||||
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
|
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
|
||||||
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
|
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
|
||||||
is PtBool -> return withClonedChildrenFrom(this, PtBool(value, position))
|
|
||||||
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
|
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
|
||||||
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
|
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
|
||||||
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
|
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
|
||||||
@ -137,14 +106,9 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
class PtAddressOf(position: Position, val isMsbForSplitArray: Boolean=false) : PtExpression(DataType.UWORD, position) {
|
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||||
val identifier: PtIdentifier
|
val identifier: PtIdentifier
|
||||||
get() = children[0] as PtIdentifier
|
get() = children.single() as PtIdentifier
|
||||||
val arrayIndexExpr: PtExpression?
|
|
||||||
get() = if(children.size==2) children[1] as PtExpression else null
|
|
||||||
|
|
||||||
val isFromArrayElement: Boolean
|
|
||||||
get() = children.size==2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -153,17 +117,14 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
|||||||
get() = children[0] as PtIdentifier
|
get() = children[0] as PtIdentifier
|
||||||
val index: PtExpression
|
val index: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
val splitWords: Boolean
|
|
||||||
get() = variable.type.isSplitWordArray
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(elementType.isNumericOrBool)
|
require(elementType in NumericDatatypes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
|
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
|
||||||
// children are always one of 3 types: PtBool, PtNumber or PtAddressOf.
|
|
||||||
override fun hashCode(): Int = Objects.hash(children, type)
|
override fun hashCode(): Int = Objects.hash(children, type)
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is PtArray)
|
if(other==null || other !is PtArray)
|
||||||
@ -183,7 +144,7 @@ class PtBuiltinFunctionCall(val name: String,
|
|||||||
position: Position) : PtExpression(type, position) {
|
position: Position) : PtExpression(type, position) {
|
||||||
init {
|
init {
|
||||||
if(!void)
|
if(!void)
|
||||||
require(!type.isUndefined)
|
require(type!=DataType.UNDEFINED)
|
||||||
}
|
}
|
||||||
|
|
||||||
val args: List<PtExpression>
|
val args: List<PtExpression>
|
||||||
@ -192,42 +153,19 @@ class PtBuiltinFunctionCall(val name: String,
|
|||||||
|
|
||||||
|
|
||||||
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
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
|
val left: PtExpression
|
||||||
get() = children[0] as PtExpression
|
get() = children[0] as PtExpression
|
||||||
val right: PtExpression
|
val right: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
init {
|
|
||||||
if(operator in ComparisonOperators + LogicalOperators)
|
|
||||||
require(type.isBool)
|
|
||||||
else
|
|
||||||
require(!type.isBool) { "no bool allowed for this operator $operator"}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtIfExpression(type: DataType, position: Position): PtExpression(type, position) {
|
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
|
||||||
val condition: PtExpression
|
val element: PtExpression
|
||||||
get() = children[0] as PtExpression
|
get() = children[0] as PtExpression
|
||||||
val truevalue: PtExpression
|
val iterable: PtIdentifier
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtIdentifier
|
||||||
val falsevalue: PtExpression
|
|
||||||
get() = children[2] as PtExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
|
|
||||||
val needle: PtExpression
|
|
||||||
get() = children[0] as PtExpression
|
|
||||||
val haystackHeapVar: PtIdentifier?
|
|
||||||
get() = children[1] as? PtIdentifier
|
|
||||||
val haystackValues: PtArray?
|
|
||||||
get() = children[1] as? PtArray
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
|
|
||||||
const val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -235,15 +173,13 @@ class PtFunctionCall(val name: String,
|
|||||||
val void: Boolean,
|
val void: Boolean,
|
||||||
type: DataType,
|
type: DataType,
|
||||||
position: Position) : PtExpression(type, position) {
|
position: Position) : PtExpression(type, position) {
|
||||||
|
init {
|
||||||
|
if(!void)
|
||||||
|
require(type!=DataType.UNDEFINED)
|
||||||
|
}
|
||||||
|
|
||||||
val args: List<PtExpression>
|
val args: List<PtExpression>
|
||||||
get() = children.map { it as PtExpression }
|
get() = children.map { it as PtExpression }
|
||||||
|
|
||||||
init {
|
|
||||||
if(void) require(type.isUndefined) {
|
|
||||||
"void fcall should have undefined datatype"
|
|
||||||
}
|
|
||||||
// note: non-void calls can have UNDEFINED type: if they return more than 1 value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -262,55 +198,29 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
|
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||||
override fun hashCode(): Int = Objects.hash(type, value)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if(other==null || other !is PtBool)
|
|
||||||
return false
|
|
||||||
return value==other.value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = "PtBool:$value"
|
|
||||||
|
|
||||||
fun asInt(): Int = if(value) 1 else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtNumber(type: BaseDataType, val number: Double, position: Position) : PtExpression(DataType.forDt(type), position) {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
|
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
|
||||||
PtNumber(BaseDataType.UBYTE, if(bool) 1.0 else 0.0, position)
|
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(type==BaseDataType.BOOL)
|
if(type==DataType.BOOL)
|
||||||
throw IllegalArgumentException("use PtBool instead")
|
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||||
if(type!=BaseDataType.FLOAT) {
|
if(type!=DataType.FLOAT) {
|
||||||
val trunc = truncate(number)
|
val rounded = round(number)
|
||||||
if (trunc != number)
|
if (rounded != number)
|
||||||
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
|
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
|
||||||
}
|
|
||||||
when(type) {
|
|
||||||
BaseDataType.UBYTE -> require(number in 0.0..255.0)
|
|
||||||
BaseDataType.BYTE -> require(number in -128.0..127.0)
|
|
||||||
BaseDataType.UWORD -> require(number in 0.0..65535.0)
|
|
||||||
BaseDataType.WORD -> require(number in -32768.0..32767.0)
|
|
||||||
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
|
|
||||||
else -> require(type.isNumeric) { "numeric literal type should be numeric: $type" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(type, number)
|
override fun hashCode(): Int = Objects.hash(type, number)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
return if(other==null || other !is PtNumber)
|
if(other==null || other !is PtNumber)
|
||||||
false
|
return false
|
||||||
else if(!type.isBool && !other.type.isBool)
|
return number==other.number
|
||||||
number==other.number
|
|
||||||
else
|
|
||||||
type==other.type && number==other.number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||||
@ -324,7 +234,8 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
|
|||||||
get() = children.single() as PtExpression
|
get() = children.single() as PtExpression
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(operator in PrefixOperators) { "invalid prefix operator: $operator" }
|
// 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" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +251,6 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
|
|||||||
fun toConstantIntegerRange(): IntProgression? {
|
fun toConstantIntegerRange(): IntProgression? {
|
||||||
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||||
return when {
|
return when {
|
||||||
fromVal == toVal -> fromVal .. toVal
|
|
||||||
fromVal <= toVal -> when {
|
fromVal <= toVal -> when {
|
||||||
stepVal <= 0 -> IntRange.EMPTY
|
stepVal <= 0 -> IntRange.EMPTY
|
||||||
stepVal == 1 -> fromVal..toVal
|
stepVal == 1 -> fromVal..toVal
|
||||||
@ -356,11 +266,12 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
|
|||||||
|
|
||||||
val fromLv = from as? PtNumber
|
val fromLv = from as? PtNumber
|
||||||
val toLv = to as? PtNumber
|
val toLv = to as? PtNumber
|
||||||
if(fromLv==null || toLv==null)
|
val stepLv = step as? PtNumber
|
||||||
|
if(fromLv==null || toLv==null || stepLv==null)
|
||||||
return null
|
return null
|
||||||
val fromVal = fromLv.number.toInt()
|
val fromVal = fromLv.number.toInt()
|
||||||
val toVal = toLv.number.toInt()
|
val toVal = toLv.number.toInt()
|
||||||
val stepVal = step.number.toInt()
|
val stepVal = stepLv.number.toInt()
|
||||||
return makeRange(fromVal, toVal, stepVal)
|
return makeRange(fromVal, toVal, stepVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,21 +287,15 @@ class PtString(val value: String, val encoding: Encoding, position: Position) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtTypeCast(type: BaseDataType, position: Position) : PtExpression(DataType.forDt(type), position) {
|
class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
get() = children.single() as PtExpression
|
get() = children.single() as PtExpression
|
||||||
|
|
||||||
fun copy(): PtTypeCast {
|
|
||||||
val copy = PtTypeCast(type.base, position)
|
|
||||||
if(children[0] is PtIdentifier) {
|
|
||||||
copy.add((children[0] as PtIdentifier).copy())
|
|
||||||
} else {
|
|
||||||
TODO("copy node ${children[0]}")
|
|
||||||
}
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// special node that isn't created from compiling user code, but used internally in the Intermediate Code
|
// special node that isn't created from compiling user code, but used internally in the Intermediate Code
|
||||||
class PtIrRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
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,41 +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.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
|
DataType.FLOAT -> ReturnConvention(returnType, null, true)
|
||||||
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
|
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
|
||||||
null -> ReturnConvention(null, null)
|
null -> ReturnConvention(null, null, false)
|
||||||
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.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
|
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
||||||
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
|
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||||
else -> ReturnConvention(paramType, null)
|
else -> ReturnConvention(paramType, null, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,27 +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.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
|
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
in PassByReferenceDatatypes -> 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) }
|
||||||
@ -85,68 +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)),
|
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
|
"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),
|
||||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
||||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
// normal functions follow:
|
||||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
||||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
|
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
"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),
|
||||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
"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),
|
||||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
|
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), 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)),
|
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||||
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
|
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||||
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
|
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||||
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||||
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||||
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
|
||||||
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
|
||||||
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
|
||||||
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
|
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
|
||||||
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
|
"rsave" to FSignature(false, emptyList(), null),
|
||||||
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
|
"rsavex" to FSignature(false, emptyList(), null),
|
||||||
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
"rrestore" to FSignature(false, emptyList(), null),
|
||||||
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
"rrestorex" to FSignature(false, emptyList(), null),
|
||||||
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
|
||||||
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
|
||||||
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
|
||||||
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
|
||||||
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
|
||||||
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
|
||||||
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
|
||||||
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
|
||||||
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
|
|
||||||
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
|
||||||
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
|
|
||||||
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
|
|
||||||
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
|
|
||||||
"rsave" to FSignature(false, null),
|
|
||||||
"rrestore" to FSignature(false, null),
|
|
||||||
"memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
|
|
||||||
"callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
|
|
||||||
"callfar2" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
|
|
||||||
"call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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,
|
||||||
// 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 dontSplitWordArrays: 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,17 +1,11 @@
|
|||||||
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.toInt()
|
val integer = this.toInt()
|
||||||
if(integer<0)
|
if(integer<0)
|
||||||
@ -20,7 +14,7 @@ fun Number.toHex(): String {
|
|||||||
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,241 +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
|
||||||
|
ARRAY_UW, // pass by reference
|
||||||
|
ARRAY_W, // pass by reference
|
||||||
|
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
|
|
||||||
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
|
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.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
|
||||||
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
|
|
||||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
|
||||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
|
||||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
|
||||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
|
||||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
|
||||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
|
||||||
val BaseDataType.isPassByRef get() = this.isIterable
|
|
||||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
|
||||||
|
|
||||||
|
|
||||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
if(base.isArray) {
|
|
||||||
require(sub != null)
|
|
||||||
if(base.isSplitWordArray)
|
|
||||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
|
||||||
}
|
|
||||||
else if(base==BaseDataType.STR)
|
|
||||||
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
|
||||||
else
|
|
||||||
require(sub == null) { "only string and array base types can have a subtype"}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other !is DataType) return false
|
|
||||||
return base == other.base && sub == other.sub
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
|
||||||
val BYTE = DataType(BaseDataType.BYTE, null)
|
|
||||||
val UWORD = DataType(BaseDataType.UWORD, null)
|
|
||||||
val WORD = DataType(BaseDataType.WORD, null)
|
|
||||||
val LONG = DataType(BaseDataType.LONG, null)
|
|
||||||
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
|
||||||
val BOOL = DataType(BaseDataType.BOOL, null)
|
|
||||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
|
||||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
|
||||||
|
|
||||||
private val simpletypes = mapOf(
|
|
||||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
|
||||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
|
||||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
|
||||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
|
||||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
|
||||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
|
||||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
|
||||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
|
||||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
|
||||||
|
|
||||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
|
||||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
|
||||||
return if(splitwordarray && actualElementDt.isWord)
|
|
||||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
|
||||||
else {
|
|
||||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
|
||||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
|
||||||
else
|
|
||||||
throw NoSuchElementException("invalid element dt $elementDt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
|
||||||
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
|
|
||||||
else arrayFor(base, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun elementType(): DataType =
|
|
||||||
if(base.isArray || base==BaseDataType.STR)
|
|
||||||
forDt(sub!!)
|
|
||||||
else
|
|
||||||
throw IllegalArgumentException("not an array")
|
|
||||||
|
|
||||||
override fun toString(): String = when(base) {
|
|
||||||
BaseDataType.ARRAY -> {
|
|
||||||
when(sub) {
|
|
||||||
BaseDataType.BOOL -> "bool[]"
|
|
||||||
BaseDataType.FLOAT -> "float[]"
|
|
||||||
BaseDataType.BYTE -> "byte[]"
|
|
||||||
BaseDataType.WORD -> "word[]"
|
|
||||||
BaseDataType.UBYTE -> "ubyte[]"
|
|
||||||
BaseDataType.UWORD -> "uword[]"
|
|
||||||
else -> throw IllegalArgumentException("invalid sub type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BaseDataType.ARRAY_SPLITW -> {
|
|
||||||
when(sub) {
|
|
||||||
BaseDataType.WORD -> "word[] (split)"
|
|
||||||
BaseDataType.UWORD -> "uword[] (split)"
|
|
||||||
else -> throw IllegalArgumentException("invalid sub type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> base.name.lowercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sourceString(): String = when (base) {
|
|
||||||
BaseDataType.BOOL -> "bool"
|
|
||||||
BaseDataType.UBYTE -> "ubyte"
|
|
||||||
BaseDataType.BYTE -> "byte"
|
|
||||||
BaseDataType.UWORD -> "uword"
|
|
||||||
BaseDataType.WORD -> "word"
|
|
||||||
BaseDataType.LONG -> "long"
|
|
||||||
BaseDataType.FLOAT -> "float"
|
|
||||||
BaseDataType.STR -> "str"
|
|
||||||
BaseDataType.ARRAY -> {
|
|
||||||
when(sub) {
|
|
||||||
BaseDataType.UBYTE -> "ubyte["
|
|
||||||
BaseDataType.UWORD -> "@nosplit uword["
|
|
||||||
BaseDataType.BOOL -> "bool["
|
|
||||||
BaseDataType.BYTE -> "byte["
|
|
||||||
BaseDataType.WORD -> "@nosplit word["
|
|
||||||
BaseDataType.FLOAT -> "float["
|
|
||||||
else -> throw IllegalArgumentException("invalid sub type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BaseDataType.ARRAY_SPLITW -> {
|
|
||||||
when(sub) {
|
|
||||||
BaseDataType.UWORD -> "uword["
|
|
||||||
BaseDataType.WORD -> "word["
|
|
||||||
else -> throw IllegalArgumentException("invalid sub type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
|
|
||||||
}
|
|
||||||
|
|
||||||
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
|
||||||
infix fun isAssignableTo(targetType: DataType) =
|
|
||||||
when(base) {
|
|
||||||
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
|
|
||||||
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
|
||||||
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
|
||||||
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
|
||||||
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
|
||||||
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
|
|
||||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
|
||||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
|
||||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
|
||||||
BaseDataType.UNDEFINED -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
|
||||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
|
||||||
|
|
||||||
val isUndefined = base == BaseDataType.UNDEFINED
|
|
||||||
val isByte = base.isByte
|
|
||||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
|
||||||
val isSignedByte = base == BaseDataType.BYTE
|
|
||||||
val isByteOrBool = base.isByteOrBool
|
|
||||||
val isWord = base.isWord
|
|
||||||
val isUnsignedWord = base == BaseDataType.UWORD
|
|
||||||
val isSignedWord = base == BaseDataType.WORD
|
|
||||||
val isInteger = base.isInteger
|
|
||||||
val isIntegerOrBool = base.isIntegerOrBool
|
|
||||||
val isNumeric = base.isNumeric
|
|
||||||
val isNumericOrBool = base.isNumericOrBool
|
|
||||||
val isSigned = base.isSigned
|
|
||||||
val isUnsigned = !base.isSigned
|
|
||||||
val isArray = base.isArray
|
|
||||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
|
||||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
|
||||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
|
||||||
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
|
|
||||||
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
|
||||||
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
|
|
||||||
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
|
|
||||||
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
|
|
||||||
val isString = base == BaseDataType.STR
|
|
||||||
val isBool = base == BaseDataType.BOOL
|
|
||||||
val isFloat = base == BaseDataType.FLOAT
|
|
||||||
val isLong = base == BaseDataType.LONG
|
|
||||||
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
|
|
||||||
val isSplitWordArray = base.isSplitWordArray
|
|
||||||
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
|
|
||||||
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
|
|
||||||
val isIterable = base.isIterable
|
|
||||||
val isPassByRef = base.isPassByRef
|
|
||||||
val isPassByValue = base.isPassByValue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum class CpuRegister {
|
enum class CpuRegister {
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
@ -256,14 +73,7 @@ enum class RegisterOrPair {
|
|||||||
R8, R9, R10, R11, R12, R13, R14, R15;
|
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||||
|
|
||||||
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 asCpuRegister(): CpuRegister = when(this) {
|
fun asCpuRegister(): CpuRegister = when(this) {
|
||||||
@ -273,17 +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, null -> ""
|
|
||||||
else -> throw kotlin.IllegalArgumentException("invalid register param type")
|
|
||||||
}
|
|
||||||
return listOf("cx16", name.lowercase()+suffix)
|
|
||||||
}
|
|
||||||
} // 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 {
|
||||||
@ -293,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()} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,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,
|
||||||
@ -319,17 +153,12 @@ val Cx16VirtualRegisters = arrayOf(
|
|||||||
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
||||||
)
|
)
|
||||||
|
|
||||||
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 {
|
||||||
@ -351,9 +180,3 @@ enum class ZeropageWish {
|
|||||||
DONTCARE,
|
DONTCARE,
|
||||||
NOT_IN_ZEROPAGE
|
NOT_IN_ZEROPAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SplitWish {
|
|
||||||
DONTCARE,
|
|
||||||
SPLIT,
|
|
||||||
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: 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,7 +22,7 @@ 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 $fb+$fc
|
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
|
||||||
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
|
||||||
|
|
||||||
@ -38,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))
|
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 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,9 +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.isString || datatype.isArray -> {
|
DataType.STR, in ArrayDatatypes -> {
|
||||||
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)
|
||||||
@ -80,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
|
||||||
@ -94,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))
|
||||||
@ -118,10 +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)
|
||||||
else -> throw AssemblyError("invalid dt")
|
else -> throw AssemblyError("invalid dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -148,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,8 +135,7 @@ 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: List<String> = emptyList()
|
|
||||||
override val additionalAssemblerOptions = null
|
|
||||||
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
|
|
||||||
override val BSSHIGHRAM_END = 0u // TODO
|
|
||||||
override val BSSGOLDENRAM_START = 0u // TODO
|
|
||||||
override val BSSGOLDENRAM_END = 0u // TODO
|
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
|
||||||
override lateinit var golden: GoldenRam
|
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
|
||||||
|
|
||||||
override fun 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.Companion.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: List<String> = emptyList()
|
|
||||||
override val additionalAssemblerOptions = null
|
|
||||||
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: String?,
|
|
||||||
val ioAddresses: List<UIntRange>,
|
|
||||||
val zpScratchB1: UInt,
|
|
||||||
val zpScratchReg: UInt,
|
|
||||||
val zpScratchW1: UInt,
|
|
||||||
val zpScratchW2: 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 assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
|
||||||
|
|
||||||
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,
|
|
||||||
assemblerOptions,
|
|
||||||
ioAddresses,
|
|
||||||
props.getInteger("zp_scratch_b1"),
|
|
||||||
props.getInteger("zp_scratch_reg"),
|
|
||||||
props.getInteger("zp_scratch_w1"),
|
|
||||||
props.getInteger("zp_scratch_w2"),
|
|
||||||
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,
|
|
||||||
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.Companion.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: List<String> = emptyList()
|
|
||||||
override val additionalAssemblerOptions = null
|
|
||||||
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(
|
||||||
@ -44,4 +36,4 @@ class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
|
|||||||
success = { it }
|
success = { it }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,34 +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.isArray) {
|
|
||||||
if(numElements==null) return 2 // treat it as a pointer size
|
|
||||||
return when(dt.sub) {
|
|
||||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
|
||||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
|
||||||
BaseDataType.FLOAT-> numElements * 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.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.Companion.FLOAT_MEM_SIZE) {
|
|
||||||
|
|
||||||
override val name = NAME
|
|
||||||
override val defaultEncoding = Encoding.PETSCII
|
|
||||||
override val libraryPath = null
|
|
||||||
override val customLauncher: List<String> = emptyList()
|
|
||||||
override val additionalAssemblerOptions = null
|
|
||||||
override val 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,111 +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.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: List<String> = emptyList()
|
|
||||||
override val additionalAssemblerOptions = null
|
|
||||||
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 filename = programNameWithPath.name
|
|
||||||
if(programNameWithPath.isReadable()) {
|
|
||||||
vm.runProgram(programNameWithPath.readText(), quiet)
|
|
||||||
} else {
|
|
||||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
|
||||||
if(withExt.isReadable())
|
|
||||||
vm.runProgram(withExt.readText(), quiet)
|
|
||||||
else
|
|
||||||
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = false
|
override fun memorySize(arrayDt: DataType, numElements: Int) =
|
||||||
|
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
}
|
||||||
zeropage = VirtualZeropage(compilerOptions)
|
|
||||||
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface IVirtualMachineRunner {
|
|
||||||
fun runProgram(irSource: String, quiet: Boolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
|
||||||
override val SCRATCH_B1: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_REG: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_W1: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
override val SCRATCH_W2: UInt
|
|
||||||
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
|
||||||
}
|
|
@ -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
|
||||||
@ -6,16 +6,22 @@ import prog8.code.core.Zeropage
|
|||||||
import prog8.code.core.ZeropageType
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
|
||||||
// reference: "Mapping the C128" zeropage chapter.
|
// reference: "Mapping the C128" zero page chapter.
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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,
|
||||||
@ -27,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,
|
||||||
@ -47,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))
|
||||||
@ -63,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,7 +6,7 @@ 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
|
||||||
|
|
||||||
@ -21,11 +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(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
|
||||||
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
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,
|
||||||
@ -53,11 +53,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
).map{it.toUInt()})
|
).map{it.toUInt()})
|
||||||
}
|
}
|
||||||
|
|
||||||
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,13 +75,11 @@ 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 zeropage 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)
|
||||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
@ -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
|
||||||
@ -26,7 +26,7 @@ object AtasciiEncoding {
|
|||||||
'▖',
|
'▖',
|
||||||
|
|
||||||
// $10
|
// $10
|
||||||
'♣',
|
'♣',
|
||||||
'┌',
|
'┌',
|
||||||
'─',
|
'─',
|
||||||
'┼',
|
'┼',
|
||||||
@ -62,7 +62,7 @@ object AtasciiEncoding {
|
|||||||
'/',
|
'/',
|
||||||
|
|
||||||
// $30
|
// $30
|
||||||
'0',
|
'0',
|
||||||
'1',
|
'1',
|
||||||
'2',
|
'2',
|
||||||
'3',
|
'3',
|
||||||
@ -80,7 +80,7 @@ object AtasciiEncoding {
|
|||||||
'?',
|
'?',
|
||||||
|
|
||||||
// $40
|
// $40
|
||||||
'@',
|
'@',
|
||||||
'A',
|
'A',
|
||||||
'B',
|
'B',
|
||||||
'C',
|
'C',
|
||||||
@ -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 {
|
@ -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) {
|
70
codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt
Normal file
70
codeCore/src/prog8/code/target/cx16/CX16MachineDefinition.kt
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package prog8.code.target.cx16
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
|
import prog8.code.target.cbm.Mflpt5
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
class CX16MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU65c02
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
|
||||||
|
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||||
|
override var ESTACK_LO = 0x0700u // $0700-$077f inclusive
|
||||||
|
override var ESTACK_HI = 0x0780u // $0780-$07ff inclusive
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||||
|
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
|
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
listOf("syslib")
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
val emulator: String
|
||||||
|
val extraArgs: List<String>
|
||||||
|
|
||||||
|
when(selectedEmulator) {
|
||||||
|
1 -> {
|
||||||
|
emulator = "x16emu"
|
||||||
|
extraArgs = emptyList()
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
emulator = "box16"
|
||||||
|
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
|
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||||
|
val process: Process = processb.start()
|
||||||
|
process.waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
|
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.code.target.zp
|
package prog8.code.target.cx16
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||||||
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
|
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
|
||||||
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
|
||||||
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
|
||||||
|
|
||||||
@ -40,6 +40,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
ZeropageType.DONTUSE -> {
|
ZeropageType.DONTUSE -> {
|
||||||
free.clear() // don't use zeropage at all
|
free.clear() // don't use zeropage at all
|
||||||
}
|
}
|
||||||
|
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val distinctFree = free.distinct()
|
val distinctFree = free.distinct()
|
||||||
@ -47,14 +48,14 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
free.addAll(distinctFree)
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
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 zeropage 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)
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
@ -1,327 +0,0 @@
|
|||||||
package prog8.code.target.encodings
|
|
||||||
|
|
||||||
import com.github.michaelbull.result.Err
|
|
||||||
import com.github.michaelbull.result.Ok
|
|
||||||
import com.github.michaelbull.result.Result
|
|
||||||
import java.io.CharConversionException
|
|
||||||
|
|
||||||
object C64osEncoding {
|
|
||||||
|
|
||||||
// decoding: from C64 OS Screencodes (0-255) to unicode
|
|
||||||
// character table from:
|
|
||||||
// https://www.c64os.com/c64os/usersguide/appendices#charactersets
|
|
||||||
|
|
||||||
private val decodingC64os = charArrayOf(
|
|
||||||
'@' , // @ 0x00 -> COMMERCIAL AT
|
|
||||||
'a' , // a 0x01 -> LATIN SMALL LETTER A
|
|
||||||
'b' , // b 0x02 -> LATIN SMALL LETTER B
|
|
||||||
'c' , // c 0x03 -> LATIN SMALL LETTER C
|
|
||||||
'd' , // d 0x04 -> LATIN SMALL LETTER D
|
|
||||||
'e' , // e 0x05 -> LATIN SMALL LETTER E
|
|
||||||
'f' , // f 0x06 -> LATIN SMALL LETTER F
|
|
||||||
'g' , // g 0x07 -> LATIN SMALL LETTER G
|
|
||||||
'h' , // h 0x08 -> LATIN SMALL LETTER H
|
|
||||||
'i' , // i 0x09 -> LATIN SMALL LETTER I
|
|
||||||
'j' , // j 0x0A -> LATIN SMALL LETTER J
|
|
||||||
'k' , // k 0x0B -> LATIN SMALL LETTER K
|
|
||||||
'l' , // l 0x0C -> LATIN SMALL LETTER L
|
|
||||||
'm' , // m 0x0D -> LATIN SMALL LETTER M
|
|
||||||
'n' , // n 0x0E -> LATIN SMALL LETTER N
|
|
||||||
'o' , // o 0x0F -> LATIN SMALL LETTER O
|
|
||||||
'p' , // p 0x10 -> LATIN SMALL LETTER P
|
|
||||||
'q' , // q 0x11 -> LATIN SMALL LETTER Q
|
|
||||||
'r' , // r 0x12 -> LATIN SMALL LETTER R
|
|
||||||
's' , // s 0x13 -> LATIN SMALL LETTER S
|
|
||||||
't' , // t 0x14 -> LATIN SMALL LETTER T
|
|
||||||
'u' , // u 0x15 -> LATIN SMALL LETTER U
|
|
||||||
'v' , // v 0x16 -> LATIN SMALL LETTER V
|
|
||||||
'w' , // w 0x17 -> LATIN SMALL LETTER W
|
|
||||||
'x' , // x 0x18 -> LATIN SMALL LETTER X
|
|
||||||
'y' , // y 0x19 -> LATIN SMALL LETTER Y
|
|
||||||
'z' , // z 0x1A -> LATIN SMALL LETTER Z
|
|
||||||
'[' , // [ 0x1B -> LEFT SQUARE BRACKET
|
|
||||||
'\\' , // \ 0x1C -> REVERSE SOLIDUS
|
|
||||||
']' , // ] 0x1D -> RIGHT SQUARE BRACKET
|
|
||||||
'^' , // ^ 0x1E -> CIRCUMFLEX
|
|
||||||
'_' , // _ 0x1F -> UNDERSCORE
|
|
||||||
' ' , // 0x20 -> SPACE
|
|
||||||
'!' , // ! 0x21 -> EXCLAMATION MARK
|
|
||||||
'"' , // " 0x22 -> QUOTATION MARK
|
|
||||||
'#' , // # 0x23 -> NUMBER SIGN
|
|
||||||
'$' , // $ 0x24 -> DOLLAR SIGN
|
|
||||||
'%' , // % 0x25 -> PERCENT SIGN
|
|
||||||
'&' , // & 0x26 -> AMPERSAND
|
|
||||||
'\'' , // ' 0x27 -> APOSTROPHE
|
|
||||||
'(' , // ( 0x28 -> LEFT PARENTHESIS
|
|
||||||
')' , // ) 0x29 -> RIGHT PARENTHESIS
|
|
||||||
'*' , // * 0x2A -> ASTERISK
|
|
||||||
'+' , // + 0x2B -> PLUS SIGN
|
|
||||||
',' , // , 0x2C -> COMMA
|
|
||||||
'-' , // - 0x2D -> HYPHEN-MINUS
|
|
||||||
'.' , // . 0x2E -> FULL STOP
|
|
||||||
'/' , // / 0x2F -> SOLIDUS
|
|
||||||
'0' , // 0 0x30 -> DIGIT ZERO
|
|
||||||
'1' , // 1 0x31 -> DIGIT ONE
|
|
||||||
'2' , // 2 0x32 -> DIGIT TWO
|
|
||||||
'3' , // 3 0x33 -> DIGIT THREE
|
|
||||||
'4' , // 4 0x34 -> DIGIT FOUR
|
|
||||||
'5' , // 5 0x35 -> DIGIT FIVE
|
|
||||||
'6' , // 6 0x36 -> DIGIT SIX
|
|
||||||
'7' , // 7 0x37 -> DIGIT SEVEN
|
|
||||||
'8' , // 8 0x38 -> DIGIT EIGHT
|
|
||||||
'9' , // 9 0x39 -> DIGIT NINE
|
|
||||||
':' , // : 0x3A -> COLON
|
|
||||||
';' , // ; 0x3B -> SEMICOLON
|
|
||||||
'<' , // < 0x3C -> LESS-THAN SIGN
|
|
||||||
'=' , // = 0x3D -> EQUALS SIGN
|
|
||||||
'>' , // > 0x3E -> GREATER-THAN SIGN
|
|
||||||
'?' , // ? 0x3F -> QUESTION MARK
|
|
||||||
'`' , // ` 0x40 -> GRAVE ACCENT
|
|
||||||
'A' , // A 0x41 -> LATIN CAPITAL LETTER A
|
|
||||||
'B' , // B 0x42 -> LATIN CAPITAL LETTER B
|
|
||||||
'C' , // C 0x43 -> LATIN CAPITAL LETTER C
|
|
||||||
'D' , // D 0x44 -> LATIN CAPITAL LETTER D
|
|
||||||
'E' , // E 0x45 -> LATIN CAPITAL LETTER E
|
|
||||||
'F' , // F 0x46 -> LATIN CAPITAL LETTER F
|
|
||||||
'G' , // G 0x47 -> LATIN CAPITAL LETTER G
|
|
||||||
'H' , // H 0x48 -> LATIN CAPITAL LETTER H
|
|
||||||
'I' , // I 0x49 -> LATIN CAPITAL LETTER I
|
|
||||||
'J' , // J 0x4A -> LATIN CAPITAL LETTER J
|
|
||||||
'K' , // K 0x4B -> LATIN CAPITAL LETTER K
|
|
||||||
'L' , // L 0x4C -> LATIN CAPITAL LETTER L
|
|
||||||
'M' , // M 0x4D -> LATIN CAPITAL LETTER M
|
|
||||||
'N' , // N 0x4E -> LATIN CAPITAL LETTER N
|
|
||||||
'O' , // O 0x4F -> LATIN CAPITAL LETTER O
|
|
||||||
'P' , // P 0x50 -> LATIN CAPITAL LETTER P
|
|
||||||
'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
|
|
||||||
'R' , // R 0x52 -> LATIN CAPITAL LETTER R
|
|
||||||
'S' , // S 0x53 -> LATIN CAPITAL LETTER S
|
|
||||||
'T' , // T 0x54 -> LATIN CAPITAL LETTER T
|
|
||||||
'U' , // U 0x55 -> LATIN CAPITAL LETTER U
|
|
||||||
'V' , // V 0x56 -> LATIN CAPITAL LETTER V
|
|
||||||
'W' , // W 0x57 -> LATIN CAPITAL LETTER W
|
|
||||||
'X' , // X 0x58 -> LATIN CAPITAL LETTER X
|
|
||||||
'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
|
|
||||||
'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
|
|
||||||
'{' , // { 0x5B -> LEFT BRACE
|
|
||||||
'|' , // | 0x5C -> VERTICAL BAR
|
|
||||||
'}' , // } 0x5D -> RIGHT BRACE
|
|
||||||
'~' , // ~ 0x5E -> TILDE
|
|
||||||
'\ufffe', // 0x5F -> RESERVED
|
|
||||||
'\u00a0', // 0x60 -> NO-BREAK SPACE (TRANSPARENT)
|
|
||||||
'\ufffe', // 0x61 -> COMMODORE SYMBOL
|
|
||||||
'\u2191', // ↑ 0x62 -> UP ARROW
|
|
||||||
'\u2193', // ↓ 0x63 -> DOWN ARROW
|
|
||||||
'\u2190', // ← 0x64 -> LEFT ARROW
|
|
||||||
'\u2192', // → 0x65 -> RIGHT ARROW
|
|
||||||
'\u231A', // ⌚ 0x66 -> WATCH (ANALOG CLOCKFACE)
|
|
||||||
'\u21BB', // ↻ 0x67 -> CYCLE ARROWS
|
|
||||||
'\u2026', // … 0x68 -> ELLIPSIS
|
|
||||||
'\u25a7', // ▧ 0x69 -> DIAGNONAL STRIPES
|
|
||||||
'\u2610', // ☐ 0x6A -> CHECKBOX UNCHECKED
|
|
||||||
'\u2611', // ☑ 0x6B -> CHECKBOX CHECKED
|
|
||||||
'\ufffe', // 0x6C -> RADIO BUTTON UNSELECTED
|
|
||||||
'\ufffe', // 0x6D -> RADIO BUTTON SELECTED
|
|
||||||
'\ufffe', // 0x6E -> UTILITY CLOSE BUTTON
|
|
||||||
'\ufffe', // 0x6F -> UTILITY TITLE BAR
|
|
||||||
'\u00a9', // © 0x70 -> COPYRIGHT
|
|
||||||
'\u2713', // ✓ 0x71 -> CHECKMARK
|
|
||||||
'\u2261', // ≡ 0x72 -> THREE HORIZONTAL STRIPES
|
|
||||||
'\ufffe', // 0x73 -> TICK TRACK
|
|
||||||
'\ufffe', // 0x74 -> TICK TRACK NUB
|
|
||||||
'\ufffe', // 0x75 -> TAB CORNER
|
|
||||||
'\u2980', // ⦀ 0x76 -> THREE VERTICAL STRIPES
|
|
||||||
'\ufffe', // 0x77 -> CUSTOM 1
|
|
||||||
'\ufffe', // 0x78 -> CUSTOM 2
|
|
||||||
'\ufffe', // 0x79 -> CUSTOM 3
|
|
||||||
'\ufffe', // 0x7A -> CUSTOM 4
|
|
||||||
'\ufffe', // 0x7B -> CUSTOM 5
|
|
||||||
'\ufffe', // 0x7C -> CUSTOM 6
|
|
||||||
'\ufffe', // 0x7D -> CUSTOM 7
|
|
||||||
'\ufffe', // 0x7E -> CUSTOM 8
|
|
||||||
'\ufffe', // 0x7F -> CUSTOM 9
|
|
||||||
'\ufffe', // 0x80 -> REVERSED COMMERCIAL AT
|
|
||||||
'\ufffe', // 0x81 -> REVERSED LATIN SMALL LETTER A
|
|
||||||
'\ufffe', // 0x82 -> REVERSED LATIN SMALL LETTER B
|
|
||||||
'\ufffe', // 0x83 -> REVERSED LATIN SMALL LETTER C
|
|
||||||
'\ufffe', // 0x84 -> REVERSED LATIN SMALL LETTER D
|
|
||||||
'\ufffe', // 0x85 -> REVERSED LATIN SMALL LETTER E
|
|
||||||
'\ufffe', // 0x86 -> REVERSED LATIN SMALL LETTER F
|
|
||||||
'\ufffe', // 0x87 -> REVERSED LATIN SMALL LETTER G
|
|
||||||
'\ufffe', // 0x88 -> REVERSED LATIN SMALL LETTER H
|
|
||||||
'\ufffe', // 0x89 -> REVERSED LATIN SMALL LETTER I
|
|
||||||
'\ufffe', // 0x8A -> REVERSED LATIN SMALL LETTER J
|
|
||||||
'\ufffe', // 0x8B -> REVERSED LATIN SMALL LETTER K
|
|
||||||
'\ufffe', // 0x8C -> REVERSED LATIN SMALL LETTER L
|
|
||||||
'\ufffe', // 0x8D -> REVERSED LATIN SMALL LETTER M
|
|
||||||
'\ufffe', // 0x8E -> REVERSED LATIN SMALL LETTER N
|
|
||||||
'\ufffe', // 0x8F -> REVERSED LATIN SMALL LETTER O
|
|
||||||
'\ufffe', // 0x90 -> REVERSED LATIN SMALL LETTER P
|
|
||||||
'\ufffe', // 0x91 -> REVERSED LATIN SMALL LETTER Q
|
|
||||||
'\ufffe', // 0x92 -> REVERSED LATIN SMALL LETTER R
|
|
||||||
'\ufffe', // 0x93 -> REVERSED LATIN SMALL LETTER S
|
|
||||||
'\ufffe', // 0x94 -> REVERSED LATIN SMALL LETTER T
|
|
||||||
'\ufffe', // 0x95 -> REVERSED LATIN SMALL LETTER U
|
|
||||||
'\ufffe', // 0x96 -> REVERSED LATIN SMALL LETTER V
|
|
||||||
'\ufffe', // 0x97 -> REVERSED LATIN SMALL LETTER W
|
|
||||||
'\ufffe', // 0x98 -> REVERSED LATIN SMALL LETTER X
|
|
||||||
'\ufffe', // 0x99 -> REVERSED LATIN SMALL LETTER Y
|
|
||||||
'\ufffe', // 0x9A -> REVERSED LATIN SMALL LETTER Z
|
|
||||||
'\ufffe', // 0x9B -> REVERSED LEFT SQUARE BRACKET
|
|
||||||
'\ufffe', // 0x9C -> REVERSED REVERSE SOLIDUS
|
|
||||||
'\ufffe', // 0x9D -> REVERSED RIGHT SQUARE BRACKET
|
|
||||||
'\ufffe', // 0x9E -> REVERSED CIRCUMFLEX
|
|
||||||
'\ufffe', // 0x9F -> REVERSED UNDERSCORE
|
|
||||||
'\ufffe', // 0xA0 -> REVERSED SPACE
|
|
||||||
'\ufffe', // 0xA1 -> REVERSED EXCLAMATION MARK
|
|
||||||
'\ufffe', // 0xA2 -> REVERSED QUOTATION MARK
|
|
||||||
'\ufffe', // 0xA3 -> REVERSED NUMBER SIGN
|
|
||||||
'\ufffe', // 0xA4 -> REVERSED DOLLAR SIGN
|
|
||||||
'\ufffe', // 0xA5 -> REVERSED PERCENT SIGN
|
|
||||||
'\ufffe', // 0xA6 -> REVERSED AMPERSAND
|
|
||||||
'\ufffe', // 0xA7 -> REVERSED APOSTROPHE
|
|
||||||
'\ufffe', // 0xA8 -> REVERSED LEFT PARENTHESIS
|
|
||||||
'\ufffe', // 0xA9 -> REVERSED RIGHT PARENTHESIS
|
|
||||||
'\ufffe', // 0xAA -> REVERSED ASTERISK
|
|
||||||
'\ufffe', // 0xAB -> REVERSED PLUS SIGN
|
|
||||||
'\ufffe', // 0xAC -> REVERSED COMMA
|
|
||||||
'\ufffe', // 0xAD -> REVERSED HYPHEN-MINUS
|
|
||||||
'\ufffe', // 0xAE -> REVERSED FULL STOP
|
|
||||||
'\ufffe', // 0xAF -> REVERSED SOLIDUS
|
|
||||||
'\ufffe', // 0xB0 -> REVERSED DIGIT ZERO
|
|
||||||
'\ufffe', // 0xB1 -> REVERSED DIGIT ONE
|
|
||||||
'\ufffe', // 0xB2 -> REVERSED DIGIT TWO
|
|
||||||
'\ufffe', // 0xB3 -> REVERSED DIGIT THREE
|
|
||||||
'\ufffe', // 0xB4 -> REVERSED DIGIT FOUR
|
|
||||||
'\ufffe', // 0xB5 -> REVERSED DIGIT FIVE
|
|
||||||
'\ufffe', // 0xB6 -> REVERSED DIGIT SIX
|
|
||||||
'\ufffe', // 0xB7 -> REVERSED DIGIT SEVEN
|
|
||||||
'\ufffe', // 0xB8 -> REVERSED DIGIT EIGHT
|
|
||||||
'\ufffe', // 0xB9 -> REVERSED DIGIT NINE
|
|
||||||
'\ufffe', // 0xBA -> REVERSED COLON
|
|
||||||
'\ufffe', // 0xBB -> REVERSED SEMICOLON
|
|
||||||
'\ufffe', // 0xBC -> REVERSED LESS-THAN SIGN
|
|
||||||
'\ufffe', // 0xBD -> REVERSED EQUALS SIGN
|
|
||||||
'\ufffe', // 0xBE -> REVERSED GREATER-THAN SIGN
|
|
||||||
'\ufffe', // 0xBF -> REVERSED QUESTION MARK
|
|
||||||
'\ufffe', // 0xC0 -> REVERSED GRAVE ACCENT
|
|
||||||
'\ufffe', // 0xC1 -> REVERSED LATIN CAPITAL LETTER A
|
|
||||||
'\ufffe', // 0xC2 -> REVERSED LATIN CAPITAL LETTER B
|
|
||||||
'\ufffe', // 0xC3 -> REVERSED LATIN CAPITAL LETTER C
|
|
||||||
'\ufffe', // 0xC4 -> REVERSED LATIN CAPITAL LETTER D
|
|
||||||
'\ufffe', // 0xC5 -> REVERSED LATIN CAPITAL LETTER E
|
|
||||||
'\ufffe', // 0xC6 -> REVERSED LATIN CAPITAL LETTER F
|
|
||||||
'\ufffe', // 0xC7 -> REVERSED LATIN CAPITAL LETTER G
|
|
||||||
'\ufffe', // 0xC8 -> REVERSED LATIN CAPITAL LETTER H
|
|
||||||
'\ufffe', // 0xC9 -> REVERSED LATIN CAPITAL LETTER I
|
|
||||||
'\ufffe', // 0xCA -> REVERSED LATIN CAPITAL LETTER J
|
|
||||||
'\ufffe', // 0xCB -> REVERSED LATIN CAPITAL LETTER K
|
|
||||||
'\ufffe', // 0xCC -> REVERSED LATIN CAPITAL LETTER L
|
|
||||||
'\ufffe', // 0xCD -> REVERSED LATIN CAPITAL LETTER M
|
|
||||||
'\ufffe', // 0xCE -> REVERSED LATIN CAPITAL LETTER N
|
|
||||||
'\ufffe', // 0xCF -> REVERSED LATIN CAPITAL LETTER O
|
|
||||||
'\ufffe', // 0xD0 -> REVERSED LATIN CAPITAL LETTER P
|
|
||||||
'\ufffe', // 0xD1 -> REVERSED LATIN CAPITAL LETTER Q
|
|
||||||
'\ufffe', // 0xD2 -> REVERSED LATIN CAPITAL LETTER R
|
|
||||||
'\ufffe', // 0xD3 -> REVERSED LATIN CAPITAL LETTER S
|
|
||||||
'\ufffe', // 0xD4 -> REVERSED LATIN CAPITAL LETTER T
|
|
||||||
'\ufffe', // 0xD5 -> REVERSED LATIN CAPITAL LETTER U
|
|
||||||
'\ufffe', // 0xD6 -> REVERSED LATIN CAPITAL LETTER V
|
|
||||||
'\ufffe', // 0xD7 -> REVERSED LATIN CAPITAL LETTER W
|
|
||||||
'\ufffe', // 0xD8 -> REVERSED LATIN CAPITAL LETTER X
|
|
||||||
'\ufffe', // 0xD9 -> REVERSED LATIN CAPITAL LETTER Y
|
|
||||||
'\ufffe', // 0xDA -> REVERSED LATIN CAPITAL LETTER Z
|
|
||||||
'\ufffe', // 0xDB -> REVERSED LEFT BRACE
|
|
||||||
'\ufffe', // 0xDC -> REVERSED VERTICAL BAR
|
|
||||||
'\ufffe', // 0xDD -> REVERSED RIGHT BRACE
|
|
||||||
'\ufffe', // 0xDE -> REVERSED TILDE
|
|
||||||
'\ufffe', // 0xDF -> RESERVED
|
|
||||||
'\ufffe', // 0xE0 -> RESERVED
|
|
||||||
'\ufffe', // 0xE1 -> REVERSED COMMODORE SYMBOL
|
|
||||||
'\ufffe', // 0xE2 -> REVERSED UP ARROW
|
|
||||||
'\ufffe', // 0xE3 -> REVERSED DOWN ARROW
|
|
||||||
'\ufffe', // 0xE4 -> REVERSED LEFT ARROW
|
|
||||||
'\ufffe', // 0xE5 -> REVERSED RIGHT ARROW
|
|
||||||
'\ufffe', // 0xE6 -> REVERSED ANALOG CLOCKFACE
|
|
||||||
'\ufffe', // 0xE7 -> REVERSED CYCLE ARROWS
|
|
||||||
'\ufffe', // 0xE8 -> REVERSED ELLIPSIS
|
|
||||||
'\ufffe', // 0xE9 -> REVERSED DIAGONAL STRIPES
|
|
||||||
'\ufffe', // 0xEA -> REVERSED CHECKBOX UNCHECKED
|
|
||||||
'\ufffe', // 0xEB -> REVERSED CHECKBOX CHECKED
|
|
||||||
'\ufffe', // 0xEC -> REVERSED RADIO BUTTON UNSELECTED
|
|
||||||
'\ufffe', // 0xED -> REVERSED RADIO BUTTON SELECTED
|
|
||||||
'\ufffe', // 0xEE -> MEMORY CHIP ICON
|
|
||||||
'\u21e7', // ⇧ 0xEF -> SHIFT SYMBOL
|
|
||||||
'\ufffe', // 0xF0 -> REVERSED COPYRIGHT SYMBOL
|
|
||||||
'\ufffe', // 0xF1 -> REVERSED CHECKMARK
|
|
||||||
'\ufffe', // 0xF2 -> REVERSED THREE HORIZONTAL STRIPES
|
|
||||||
'\ufffe', // 0xF3 -> REVERSED TICK TRACK
|
|
||||||
'\ufffe', // 0xF4 -> REVERSED TICK TRACK NUB
|
|
||||||
'\ufffe', // 0xF5 -> REVERSED TAB CORNER
|
|
||||||
'\ufffe', // 0xF6 -> REVERSED THREE VERTICAL STRIPES
|
|
||||||
'\ufffe', // 0xF7 -> CUSTOM 10
|
|
||||||
'\ufffe', // 0xF8 -> CUSTOM 11
|
|
||||||
'\ufffe', // 0xF9 -> CUSTOM 12
|
|
||||||
'\ufffe', // 0xFA -> CUSTOM 13
|
|
||||||
'\ufffe', // 0xFB -> CUSTOM 14
|
|
||||||
'\ufffe', // 0xFC -> CUSTOM 15
|
|
||||||
'\ufffe', // 0xFD -> CUSTOM 16
|
|
||||||
'\ufffe', // 0xFE -> CUSTOM 17
|
|
||||||
'\ufffe' // 0xFF -> CUSTOM 18
|
|
||||||
)
|
|
||||||
|
|
||||||
// encoding: from unicode to C64 OS Screencodes (0-255)
|
|
||||||
private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index}
|
|
||||||
|
|
||||||
private fun replaceSpecial(chr: Char): Char =
|
|
||||||
when(chr) {
|
|
||||||
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
|
||||||
else -> chr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
|
|
||||||
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
|
|
||||||
val chr = replaceSpecial(chr3)
|
|
||||||
val screencode = encodingC64os[chr]
|
|
||||||
return screencode?.toUByte() ?: when (chr) {
|
|
||||||
'\u0000' -> 0u
|
|
||||||
'\n' -> 13u
|
|
||||||
in '\u8000'..'\u80ff' -> {
|
|
||||||
// special case: take the lower 8 bit hex value directly
|
|
||||||
(chr.code - 0x8000).toUByte()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
if(chr.isISOControl())
|
|
||||||
throw CharConversionException("no c64os character for char #${chr.code}")
|
|
||||||
else
|
|
||||||
throw CharConversionException("no c64os character for char #${chr.code} '${chr}'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
|
||||||
Ok(text.map {
|
|
||||||
try {
|
|
||||||
encodeChar(it, lowercase)
|
|
||||||
} catch (x: CharConversionException) {
|
|
||||||
encodeChar(it, !lowercase)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch(cx: CharConversionException) {
|
|
||||||
Err(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
|
|
||||||
return try {
|
|
||||||
Ok(screencode.map {
|
|
||||||
val code = it.toInt()
|
|
||||||
if(code<0 || code>= decodingC64os.size)
|
|
||||||
throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}")
|
|
||||||
decodingC64os[code]
|
|
||||||
}.joinToString(""))
|
|
||||||
} catch(ce: CharConversionException) {
|
|
||||||
Err(ce)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package prog8.code.target.encodings
|
|
||||||
|
|
||||||
import com.github.michaelbull.result.Err
|
|
||||||
import com.github.michaelbull.result.Ok
|
|
||||||
import com.github.michaelbull.result.Result
|
|
||||||
import java.io.CharConversionException
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
|
|
||||||
object Cp437Encoding {
|
|
||||||
val charset: Charset = Charset.forName("IBM437")
|
|
||||||
|
|
||||||
fun encode(str: String): Result<List<UByte>, CharConversionException> {
|
|
||||||
return try {
|
|
||||||
val mapped = str.map { chr ->
|
|
||||||
when (chr) {
|
|
||||||
'\u0000' -> 0u
|
|
||||||
'\u00a0' -> 255u
|
|
||||||
'☺' -> 1u
|
|
||||||
'☻' -> 2u
|
|
||||||
'♥' -> 3u
|
|
||||||
'♦' -> 4u
|
|
||||||
'♣' -> 5u
|
|
||||||
'♠' -> 6u
|
|
||||||
'•' -> 7u
|
|
||||||
'◘' -> 8u
|
|
||||||
'○' -> 9u
|
|
||||||
'◙' -> 10u
|
|
||||||
'♂' -> 11u
|
|
||||||
'♀' -> 12u
|
|
||||||
'♪' -> 13u
|
|
||||||
'♫' -> 14u
|
|
||||||
'☼' -> 15u
|
|
||||||
'►' -> 16u
|
|
||||||
'◄' -> 17u
|
|
||||||
'↕' -> 18u
|
|
||||||
'‼' -> 19u
|
|
||||||
'¶' -> 20u
|
|
||||||
'§' -> 21u
|
|
||||||
'▬' -> 22u
|
|
||||||
'↨' -> 23u
|
|
||||||
'↑' -> 24u
|
|
||||||
'↓' -> 25u
|
|
||||||
'→' -> 26u
|
|
||||||
'←' -> 27u
|
|
||||||
'∟' -> 28u
|
|
||||||
'↔' -> 29u
|
|
||||||
'▲' -> 30u
|
|
||||||
'▼' -> 31u
|
|
||||||
in '\u8000'..'\u80ff' -> {
|
|
||||||
// special case: take the lower 8 bit hex value directly
|
|
||||||
(chr.code - 0x8000).toUByte()
|
|
||||||
}
|
|
||||||
else -> charset.encode(chr.toString())[0].toUByte()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(mapped)
|
|
||||||
} catch (ce: CharConversionException) {
|
|
||||||
Err(ce)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
|
||||||
return try {
|
|
||||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
|
||||||
} catch (ce: CharConversionException) {
|
|
||||||
Err(ce)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
package prog8.code.target.encodings
|
|
||||||
|
|
||||||
import com.github.michaelbull.result.Err
|
|
||||||
import com.github.michaelbull.result.Ok
|
|
||||||
import com.github.michaelbull.result.Result
|
|
||||||
import java.io.CharConversionException
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
|
|
||||||
|
|
||||||
object JapaneseCharacterConverter {
|
|
||||||
// adapted from https://github.com/raminduw/Japanese-Character-Converter
|
|
||||||
|
|
||||||
private val ZENKAKU_KATAKANA = charArrayOf(
|
|
||||||
'ァ', 'ア', 'ィ', 'イ', 'ゥ',
|
|
||||||
'ウ', 'ェ', 'エ', 'ォ', 'オ', 'カ', 'ガ', 'キ', 'ギ', 'ク', 'グ', 'ケ', 'ゲ',
|
|
||||||
'コ', 'ゴ', 'サ', 'ザ', 'シ', 'ジ', 'ス', 'ズ', 'セ', 'ゼ', 'ソ', 'ゾ', 'タ',
|
|
||||||
'ダ', 'チ', 'ヂ', 'ッ', 'ツ', 'ヅ', 'テ', 'デ', 'ト', 'ド', 'ナ', 'ニ', 'ヌ',
|
|
||||||
'ネ', 'ノ', 'ハ', 'バ', 'パ', 'ヒ', 'ビ', 'ピ', 'フ', 'ブ', 'プ', 'ヘ', 'ベ',
|
|
||||||
'ペ', 'ホ', 'ボ', 'ポ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ャ', 'ヤ', 'ュ', 'ユ',
|
|
||||||
'ョ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ヮ', 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン',
|
|
||||||
'ヴ', 'ヵ', 'ヶ'
|
|
||||||
)
|
|
||||||
|
|
||||||
private val HANKAKU_HIRAGANA = charArrayOf(
|
|
||||||
'ぁ', 'あ', 'ぃ', 'い', 'ぅ', 'う', 'ぇ', 'え',
|
|
||||||
'ぉ', 'お', 'か', 'が', 'き', 'ぎ', 'く', 'ぐ',
|
|
||||||
'け', 'げ', 'こ', 'ご', 'さ', 'ざ', 'し', 'じ',
|
|
||||||
'す', 'ず', 'せ', 'ぜ', 'そ', 'ぞ', 'た', 'だ',
|
|
||||||
'ち', 'ぢ', 'っ', 'つ', 'づ', 'て', 'で', 'と',
|
|
||||||
'ど', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ば',
|
|
||||||
'ぱ', 'ひ', 'び', 'ぴ', 'ふ', 'ぶ', 'ぷ', 'へ',
|
|
||||||
'べ', 'ぺ', 'ほ', 'ぼ', 'ぽ', 'ま', 'み', 'む',
|
|
||||||
'め', 'も', 'ゃ', 'や', 'ゅ', 'ゆ', 'ょ', 'よ',
|
|
||||||
'ら', 'り', 'る', 'れ', 'ろ', 'ゎ', 'わ', 'ゐ',
|
|
||||||
'ゑ', 'を', 'ん', 'ゔ', 'ゕ', 'ゖ'
|
|
||||||
)
|
|
||||||
|
|
||||||
private val HANKAKU_KATAKANA = arrayOf(
|
|
||||||
"ァ", "ア", "ィ", "イ", "ゥ",
|
|
||||||
"ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ",
|
|
||||||
"ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ",
|
|
||||||
"ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド",
|
|
||||||
"ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ",
|
|
||||||
"ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ", "ム", "メ",
|
|
||||||
"モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ",
|
|
||||||
"ワ", "イ", "エ", "ヲ", "ン", "ヴ", "カ", "ケ"
|
|
||||||
)
|
|
||||||
|
|
||||||
private val ZENKAKU_KATAKANA_FIRST_CHAR_CODE = ZENKAKU_KATAKANA.first().code
|
|
||||||
private val HANKAKU_HIRAGANA_FIRST_CHAR_CODE = HANKAKU_HIRAGANA.first().code
|
|
||||||
|
|
||||||
private fun zenkakuKatakanaToHankakuKatakana(c: Char): String = if (c in ZENKAKU_KATAKANA) HANKAKU_KATAKANA[c.code - ZENKAKU_KATAKANA_FIRST_CHAR_CODE] else c.toString()
|
|
||||||
private fun hankakuKatakanaToZenkakuKatakana(c: Char): Char = if (c in HANKAKU_HIRAGANA) ZENKAKU_KATAKANA[c.code - HANKAKU_HIRAGANA_FIRST_CHAR_CODE] else c
|
|
||||||
|
|
||||||
fun zenkakuKatakanaToHankakuKatakana(s: String): String = buildString {
|
|
||||||
for (element in s) {
|
|
||||||
val converted = hankakuKatakanaToZenkakuKatakana(element)
|
|
||||||
val convertedChar = zenkakuKatakanaToHankakuKatakana(converted)
|
|
||||||
append(convertedChar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object KatakanaEncoding {
|
|
||||||
val charset: Charset = Charset.forName("JIS_X0201")
|
|
||||||
|
|
||||||
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
|
|
||||||
return try {
|
|
||||||
val mapped = str.map { chr ->
|
|
||||||
when (chr) {
|
|
||||||
'\n' -> if(newlineToCarriageReturn) 13u else 10u
|
|
||||||
|
|
||||||
'\u0000' -> 0u
|
|
||||||
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
|
|
||||||
|
|
||||||
'♥' -> 0xe3u
|
|
||||||
'♦' -> 0xe4u
|
|
||||||
'♣' -> 0xe5u
|
|
||||||
'♠' -> 0xe6u
|
|
||||||
|
|
||||||
'大' -> 0xeau
|
|
||||||
'中' -> 0xebu
|
|
||||||
'小' -> 0xecu
|
|
||||||
'百' -> 0xedu
|
|
||||||
'千' -> 0xeeu
|
|
||||||
'万' -> 0xefu
|
|
||||||
'♪' -> 0xf0u
|
|
||||||
'土' -> 0xf1u
|
|
||||||
'金' -> 0xf2u
|
|
||||||
'木' -> 0xf3u
|
|
||||||
'水' -> 0xf4u
|
|
||||||
'火' -> 0xf5u
|
|
||||||
'月' -> 0xf6u
|
|
||||||
'日' -> 0xf7u
|
|
||||||
'時' -> 0xf8u
|
|
||||||
'分' -> 0xf9u
|
|
||||||
'秒' -> 0xfau
|
|
||||||
'年' -> 0xfbu
|
|
||||||
'円' -> 0xfcu
|
|
||||||
'人' -> 0xfdu
|
|
||||||
'生' -> 0xfeu
|
|
||||||
'〒' -> 0xffu
|
|
||||||
in '\u8000'..'\u80ff' -> {
|
|
||||||
// special case: take the lower 8 bit hex value directly
|
|
||||||
(chr.code - 0x8000).toUByte()
|
|
||||||
}
|
|
||||||
else -> charset.encode(chr.toString())[0].toUByte()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(mapped)
|
|
||||||
} catch (ce: CharConversionException) {
|
|
||||||
Err(ce)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
|
|
||||||
return try {
|
|
||||||
Ok(String(bytes.map {
|
|
||||||
when(it) {
|
|
||||||
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
|
|
||||||
else -> it.toByte()
|
|
||||||
}
|
|
||||||
}.toByteArray(), charset))
|
|
||||||
} catch (ce: CharConversionException) {
|
|
||||||
Err(ce)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,75 @@
|
|||||||
|
package prog8.code.target.virtual
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.isReadable
|
||||||
|
import kotlin.io.path.name
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
class VirtualMachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.VIRTUAL
|
||||||
|
|
||||||
|
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||||
|
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||||
|
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
||||||
|
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||||
|
|
||||||
|
override var ESTACK_LO = 0u // not actually used
|
||||||
|
override var ESTACK_HI = 0u // not actually used
|
||||||
|
override val BSSHIGHRAM_START = 0u // not actually used
|
||||||
|
override val BSSHIGHRAM_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.toFloat().toBits().toUInt()
|
||||||
|
val hexStr = bits.toString(16).padStart(8, '0')
|
||||||
|
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||||
|
return parts.joinToString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
|
return listOf("syslib")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||||
|
println("\nStarting Virtual Machine...")
|
||||||
|
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||||
|
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||||
|
val filename = programNameWithPath.name
|
||||||
|
if(programNameWithPath.isReadable()) {
|
||||||
|
vm.runProgram(programNameWithPath.readText())
|
||||||
|
} else {
|
||||||
|
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||||
|
if(withExt.isReadable())
|
||||||
|
vm.runProgram(withExt.readText())
|
||||||
|
else
|
||||||
|
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isIOAddress(address: UInt): Boolean = false
|
||||||
|
|
||||||
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = VirtualZeropage(compilerOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IVirtualMachineRunner {
|
||||||
|
fun runProgram(irSource: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||||
|
override val SCRATCH_B1: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_REG: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_W1: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_W2: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
|
||||||
|
override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
package prog8.code.target.zp
|
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.Zeropage
|
|
||||||
import prog8.code.core.ZeropageType
|
|
||||||
|
|
||||||
class ConfigurableZeropage(
|
|
||||||
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
|
||||||
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
|
|
||||||
override val SCRATCH_W1: UInt, // temp storage 1 for a word
|
|
||||||
override val SCRATCH_W2: UInt, // temp storage 2 for a word
|
|
||||||
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
|
|
||||||
basicsafe: List<UIntRange>,
|
|
||||||
kernalsafe: List<UIntRange>,
|
|
||||||
fullsafe: List<UIntRange>,
|
|
||||||
options: CompilationOptions
|
|
||||||
) : Zeropage(options) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (options.floats) {
|
|
||||||
TODO("floats in configurable target zp")
|
|
||||||
}
|
|
||||||
|
|
||||||
if(SCRATCH_REG!=SCRATCH_B1+1u)
|
|
||||||
throw IllegalArgumentException("Zero page scratch variable REG should be B1+1")
|
|
||||||
|
|
||||||
when (options.zeropage) {
|
|
||||||
ZeropageType.DONTUSE -> { /* don't use any zeropage at all */ }
|
|
||||||
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
|
|
||||||
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
|
|
||||||
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
|
|
||||||
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
|
|
||||||
}
|
|
||||||
|
|
||||||
val distinctFree = free.distinct()
|
|
||||||
free.clear()
|
|
||||||
free.addAll(distinctFree)
|
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
|
||||||
allocateCx16VirtualRegisters()
|
|
||||||
retainAllowed()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun allocateCx16VirtualRegisters() {
|
|
||||||
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
|
|
||||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
|
||||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
|
||||||
for(reg in 0..15) {
|
|
||||||
val address = virtualRegistersStart + (2*reg).toUInt()
|
|
||||||
if(address<=0xffu) {
|
|
||||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
|
||||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
|
||||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
|
||||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
|
||||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
|
||||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package prog8.code.target.zp
|
|
||||||
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.InternalCompilerException
|
|
||||||
import prog8.code.core.Zeropage
|
|
||||||
import prog8.code.core.ZeropageType
|
|
||||||
|
|
||||||
|
|
||||||
// reference: http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
|
||||||
|
|
||||||
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
|
||||||
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
|
|
||||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
|
||||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
|
||||||
|
|
||||||
init {
|
|
||||||
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 -> {
|
|
||||||
free.addAll(0x00u..0xffu)
|
|
||||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
|
||||||
}
|
|
||||||
ZeropageType.KERNALSAFE -> {
|
|
||||||
free.addAll(0x00u..0xffu)
|
|
||||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
|
||||||
}
|
|
||||||
ZeropageType.FLOATSAFE,
|
|
||||||
ZeropageType.BASICSAFE -> {
|
|
||||||
free.addAll(0xb3u..0xbau) // TODO more?
|
|
||||||
}
|
|
||||||
ZeropageType.DONTUSE -> {
|
|
||||||
free.clear() // don't use zeropage at all
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val distinctFree = free.distinct()
|
|
||||||
free.clear()
|
|
||||||
free.addAll(distinctFree)
|
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
|
||||||
retainAllowed()
|
|
||||||
}
|
|
||||||
}
|
|
63
codeGenCpu6502/build.gradle
Normal file
63
codeGenCpu6502/build.gradle
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
id "io.kotest" version "0.3.9"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = javaVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':codeCore')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||||
|
|
||||||
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDir "${project.projectDir}/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
kotlin("jvm")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":codeCore"))
|
|
||||||
implementation(project(":simpleAst"))
|
|
||||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
|
||||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
|
||||||
|
|
||||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
|
||||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
java {
|
|
||||||
setSrcDirs(listOf("$projectDir/src"))
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
setSrcDirs(listOf("$projectDir/res"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
test {
|
|
||||||
java {
|
|
||||||
setSrcDirs(listOf("$projectDir/test"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.test {
|
|
||||||
// Enable JUnit 5 (Gradle 4.6+).
|
|
||||||
useJUnitPlatform()
|
|
||||||
|
|
||||||
// Always run tests, even when nothing changed.
|
|
||||||
dependsOn("cleanTest")
|
|
||||||
|
|
||||||
// Show test results.
|
|
||||||
testLogging {
|
|
||||||
events("skipped", "failed")
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,10 +11,8 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="module" module-name="codeCore" />
|
<orderEntry type="module" module-name="codeCore" />
|
||||||
<orderEntry type="module" module-name="simpleAst" />
|
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +1,35 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.code.GENERATED_LABEL_PREFIX
|
|
||||||
import prog8.code.StConstant
|
import prog8.code.StConstant
|
||||||
import prog8.code.StMemVar
|
import prog8.code.StMemVar
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.core.IMachineDefinition
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
|
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
|
||||||
|
|
||||||
var numberOfOptimizations = 0
|
var numberOfOptimizations = 0
|
||||||
|
|
||||||
var linesByFour = getLinesBy(lines, 4)
|
var linesByFour = getLinesBy(lines, 4)
|
||||||
|
|
||||||
var mods = optimizeIncDec(linesByFour)
|
var mods = optimizeUselessStackByteWrites(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeIncDec(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = optimizeCmpSequence(linesByFour)
|
||||||
if(mods.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
apply(mods, lines)
|
apply(mods, lines)
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
@ -30,28 +43,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationT
|
|||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
|
mods= optimizeJsrRtsAndOtherCombinations(linesByFour)
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
mods = optimizeUselessPushPopStack(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
mods = optimizeUnneededTempvarInAdd(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
|
||||||
apply(mods, lines)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
|
||||||
numberOfOptimizations++
|
|
||||||
}
|
|
||||||
|
|
||||||
mods = optimizeTSBtoRegularOr(linesByFour)
|
|
||||||
if(mods.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
apply(mods, lines)
|
apply(mods, lines)
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
@ -66,13 +58,15 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationT
|
|||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
|
mods = optimizeSamePointerIndexing(linesByFourteen)
|
||||||
if(mods.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
apply(mods, lines)
|
apply(mods, lines)
|
||||||
linesByFourteen = getLinesBy(lines, 14)
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO more assembly peephole optimizations
|
||||||
|
|
||||||
return numberOfOptimizations
|
return numberOfOptimizations
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,49 +75,62 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
|
|||||||
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
|
||||||
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
|
||||||
|
|
||||||
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false)
|
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||||
|
|
||||||
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||||
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||||
if(modification.remove) {
|
if(modification.remove)
|
||||||
if(modification.removeLabel)
|
lines.removeAt(modification.lineIndex)
|
||||||
lines.removeAt(modification.lineIndex)
|
|
||||||
else {
|
|
||||||
val line = lines[modification.lineIndex]
|
|
||||||
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
|
|
||||||
lines.removeAt(modification.lineIndex)
|
|
||||||
else if (haslabel(line)) {
|
|
||||||
val label = keeplabel(line)
|
|
||||||
if (label.isNotEmpty())
|
|
||||||
lines[modification.lineIndex] = label
|
|
||||||
else
|
|
||||||
lines.removeAt(modification.lineIndex)
|
|
||||||
} else lines.removeAt(modification.lineIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
lines[modification.lineIndex] = modification.replacement!!
|
lines[modification.lineIndex] = modification.replacement!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun haslabel(line: String): Boolean {
|
|
||||||
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun keeplabel(line: String): String {
|
|
||||||
if(':' in line)
|
|
||||||
return line.substringBefore(':') + ':'
|
|
||||||
val splits = line.split('\t', ' ', limit=2)
|
|
||||||
return if(splits.size>1) splits[0] + ':' else ""
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||||
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||||
|
|
||||||
|
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// when statement (on bytes) generates a sequence of:
|
||||||
|
// lda $ce01,x
|
||||||
|
// cmp #$20
|
||||||
|
// beq check_prog8_s72choice_32
|
||||||
|
// lda $ce01,x
|
||||||
|
// cmp #$21
|
||||||
|
// beq check_prog8_s73choice_33
|
||||||
|
// the repeated lda can be removed
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for(lines in linesByFour) {
|
||||||
|
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||||
|
lines[1].value.trim().startsWith("cmp ") &&
|
||||||
|
lines[2].value.trim().startsWith("beq ") &&
|
||||||
|
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||||
|
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||||
|
// this is a lot harder for word values because the instruction sequence varies.
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for(lines in linesByFour) {
|
||||||
|
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||||
|
lines[1].value.trim()=="dex" &&
|
||||||
|
lines[2].value.trim()=="inx" &&
|
||||||
|
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
private fun optimizeSameAssignments(
|
private fun optimizeSameAssignments(
|
||||||
linesByFourteen: Sequence<List<IndexedValue<String>>>,
|
linesByFourteen: List<List<IndexedValue<String>>>,
|
||||||
machine: ICompilationTarget,
|
machine: IMachineDefinition,
|
||||||
symbolTable: SymbolTable
|
symbolTable: SymbolTable
|
||||||
): List<Modification> {
|
): List<Modification> {
|
||||||
|
|
||||||
@ -312,7 +319,7 @@ private fun optimizeSameAssignments(
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
|
||||||
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||||
// if Y isn't modified in between we can omit the second LDY:
|
// if Y isn't modified in between we can omit the second LDY:
|
||||||
@ -349,60 +356,33 @@ private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<L
|
|||||||
mods.add(Modification(lines[4].index, true, null))
|
mods.add(Modification(lines[4].index, true, null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
beq +
|
|
||||||
lda #1
|
|
||||||
+
|
|
||||||
[ optional: label_xxxx_shortcut line here]
|
|
||||||
beq label_xxxx_shortcut / bne label_xxxx_shortcut
|
|
||||||
or *_afterif labels.
|
|
||||||
|
|
||||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
|
||||||
*/
|
|
||||||
|
|
||||||
val autoLabelPrefix = GENERATED_LABEL_PREFIX
|
|
||||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
|
||||||
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
|
||||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
|
||||||
}
|
|
||||||
else if(fourth.startsWith(autoLabelPrefix) && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
|
|
||||||
if((fifth.startsWith("beq $autoLabelPrefix") || fifth.startsWith("bne $autoLabelPrefix")) &&
|
|
||||||
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeStoreLoadSame(
|
private fun optimizeStoreLoadSame(
|
||||||
linesByFour: Sequence<List<IndexedValue<String>>>,
|
linesByFour: List<List<IndexedValue<String>>>,
|
||||||
machine: ICompilationTarget,
|
machine: IMachineDefinition,
|
||||||
symbolTable: SymbolTable
|
symbolTable: SymbolTable
|
||||||
): List<Modification> {
|
): List<Modification> {
|
||||||
|
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (lines in linesByFour) {
|
for (lines in linesByFour) {
|
||||||
val first = lines[1].value.trimStart()
|
val first = lines[1].value.trimStart()
|
||||||
val second = lines[2].value.trimStart()
|
val second = lines[2].value.trimStart()
|
||||||
val third = lines[3].value.trimStart()
|
|
||||||
|
|
||||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
|
||||||
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
|
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
|
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
|
||||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
|
(first.startsWith("lda ") && second.startsWith("lda ")) ||
|
||||||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
|
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
|
||||||
(first.startsWith("ldx ") && second.startsWith("ldx "))
|
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
|
||||||
|
(first.startsWith("sta ") && second.startsWith("lda ")) ||
|
||||||
|
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||||
|
(first.startsWith("stx ") && second.startsWith("ldx "))
|
||||||
) {
|
) {
|
||||||
|
val third = lines[3].value.trimStart()
|
||||||
val attemptRemove =
|
val attemptRemove =
|
||||||
if(third.isBranch()) {
|
if(third.isBranch()) {
|
||||||
// a branch instruction follows, we can only remove the load instruction if
|
// a branch instruction follows, we can only remove the load instruction if
|
||||||
@ -439,27 +419,21 @@ private fun optimizeStoreLoadSame(
|
|||||||
} else if(first=="phx" && second=="pla") {
|
} else if(first=="phx" && second=="pla") {
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
mods.add(Modification(lines[2].index, false, " txa"))
|
mods.add(Modification(lines[2].index, false, " txa"))
|
||||||
|
} else if(first=="phx" && second=="ply") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " txy"))
|
||||||
} else if(first=="phy" && second=="pla") {
|
} else if(first=="phy" && second=="pla") {
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
mods.add(Modification(lines[2].index, false, " tya"))
|
mods.add(Modification(lines[2].index, false, " tya"))
|
||||||
}
|
} else if(first=="phy" && second=="plx") {
|
||||||
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
|
mods.add(Modification(lines[2].index, false, " tyx"))
|
||||||
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
|
|
||||||
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
|
|
||||||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
|
|
||||||
(first.startsWith("ldy ") && second.startsWith("sty "))
|
|
||||||
) {
|
|
||||||
val firstLoc = first.substring(4).trimStart()
|
|
||||||
val secondLoc = second.substring(4).trimStart()
|
|
||||||
if (firstLoc == secondLoc)
|
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
|
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||||
|
|
||||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||||
// try to get the constant value address, could return null if it's a symbol instead
|
// try to get the constant value address, could return null if it's a symbol instead
|
||||||
@ -484,7 +458,7 @@ private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (lines in linesByFour) {
|
for (lines in linesByFour) {
|
||||||
@ -501,269 +475,45 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
|||||||
mods.add(Modification(lines[0].index, true, null))
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// jsr Sub + rts -> jmp Sub
|
// jsr Sub + rts -> jmp Sub
|
||||||
// jmp Sub + rts -> jmp Sub
|
|
||||||
// rts + jmp -> remove jmp
|
// rts + jmp -> remove jmp
|
||||||
// rts + bxx -> remove bxx
|
// rts + bxx -> remove bxx
|
||||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
|
||||||
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
|
|
||||||
// and some other optimizations.
|
|
||||||
|
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (lines in linesByFour) {
|
for (lines in linesByFour) {
|
||||||
val first = lines[0].value
|
val first = lines[0].value
|
||||||
val second = lines[1].value
|
val second = lines[1].value
|
||||||
val third = lines[2].value
|
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||||
|
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||||
if(!haslabel(second)) {
|
mods += Modification(lines[1].index, true, null)
|
||||||
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
}
|
||||||
|
else if (" rts" in first || "\trts" in first) {
|
||||||
|
if (" jmp" in second || "\tjmp" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bra" in second || "\tbra" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bcc" in second || "\tbcc" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bcs" in second || "\tbcs" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" beq" in second || "\tbeq" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bne" in second || "\tbne" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bmi" in second || "\tbmi" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bpl" in second || "\tbpl" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bvs" in second || "\tbvs" in second)
|
||||||
|
mods += Modification(lines[1].index, true, null)
|
||||||
|
else if (" bvc" in second || "\tbvc" in second)
|
||||||
mods += Modification(lines[1].index, true, null)
|
mods += Modification(lines[1].index, true, null)
|
||||||
}
|
|
||||||
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
|
||||||
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
|
|
||||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (" rts" in first || "\trts" in first) {
|
|
||||||
if (" jmp" in second || "\tjmp" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bra" in second || "\tbra" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bcc" in second || "\tbcc" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bcs" in second || "\tbcs" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" beq" in second || "\tbeq" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bne" in second || "\tbne" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bmi" in second || "\tbmi" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bpl" in second || "\tbpl" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bvs" in second || "\tbvs" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
else if (" bvc" in second || "\tbvc" in second)
|
|
||||||
mods += Modification(lines[1].index, true, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
|
|
||||||
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
|
|
||||||
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
|
|
||||||
) {
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
}
|
|
||||||
else if(" cmp #0" in second || "\tcmp #0" in second) {
|
|
||||||
// there are many instructions that modify A and set the bits...
|
|
||||||
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
|
|
||||||
if(" $instr" in first || "\t$instr" in first) {
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// only remove bra followed by jmp or jmp followed by bra
|
|
||||||
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
|
|
||||||
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
}
|
|
||||||
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
LDA NUM1
|
|
||||||
CMP NUM2
|
|
||||||
BCC LABEL
|
|
||||||
BEQ LABEL
|
|
||||||
|
|
||||||
(or something similar) which branches to LABEL when NUM1 <= NUM2. (In this case NUM1 and NUM2 are unsigned numbers.) However, consider the following sequence:
|
|
||||||
|
|
||||||
LDA NUM2
|
|
||||||
CMP NUM1
|
|
||||||
BCS LABEL
|
|
||||||
*/
|
|
||||||
val tfirst = first.trimStart()
|
|
||||||
val tsecond = second.trimStart()
|
|
||||||
val tthird = lines[2].value.trimStart()
|
|
||||||
val tfourth = lines[3].value.trimStart()
|
|
||||||
if(tfirst.startsWith("lda") && tsecond.startsWith("cmp") && tthird.startsWith("bcc") && tfourth.startsWith("beq")) {
|
|
||||||
val label = tthird.substring(4)
|
|
||||||
if(label==tfourth.substring(4)) {
|
|
||||||
mods += Modification(lines[0].index, false, " lda ${tsecond.substring(4)}")
|
|
||||||
mods += Modification(lines[1].index, false, " cmp ${tfirst.substring(4)}")
|
|
||||||
mods += Modification(lines[2].index, false, " bcs $label")
|
|
||||||
mods += Modification(lines[3].index, true, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun sameLabel(branchInstr: String, jumpInstr: String, labelInstr: String): Boolean {
|
|
||||||
if('(' in jumpInstr) return false // indirect jump cannot be replaced
|
|
||||||
val label = labelInstr.trimEnd().substringBefore(':').substringBefore(' ').substringBefore('\t')
|
|
||||||
val branchLabel = branchInstr.trimStart().substring(3).trim()
|
|
||||||
return label==branchLabel
|
|
||||||
}
|
|
||||||
|
|
||||||
// beq Label + jmp Addr + Label -> bne Addr
|
|
||||||
if((" jmp" in second || "\tjmp " in second) && haslabel(third)) {
|
|
||||||
if((" beq " in first || "\tbeq " in first) && sameLabel(first, second, third)) {
|
|
||||||
val branch = second.replace("jmp", "bne")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
else if((" bne " in first || "\tbne " in first) && sameLabel(first, second, third)) {
|
|
||||||
val branch = second.replace("jmp", "beq")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
else if((" bcc " in first || "\tbcc " in first) && sameLabel(first, second, third)){
|
|
||||||
val branch = second.replace("jmp", "bcs")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
else if((" bcs " in first || "\tbcs " in first) && sameLabel(first, second, third)) {
|
|
||||||
val branch = second.replace("jmp", "bcc")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
else if((" bpl " in first || "\tbpl " in first) && sameLabel(first, second, third)) {
|
|
||||||
val branch = second.replace("jmp", "bmi")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
else if((" bmi " in first || "\tbmi " in first) && sameLabel(first, second, third)) {
|
|
||||||
val branch = second.replace("jmp", "bpl")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
else if((" bvc " in first || "\tbvc " in first) && sameLabel(first, second, third)) {
|
|
||||||
val branch = second.replace("jmp", "bvs")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
else if((" bvs " in first || "\tbvs " in first) && sameLabel(first, second, third)) {
|
|
||||||
val branch = second.replace("jmp", "bvc")
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, branch))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
|
|
||||||
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
|
|
||||||
if(lines[0].value.trimStart().startsWith("ph$register")) {
|
|
||||||
if(lines[2].value.trimStart().startsWith("pl$register")) {
|
|
||||||
val second = lines[1].value.trimStart().take(6).lowercase()
|
|
||||||
if(register!in second
|
|
||||||
&& !second.startsWith("jsr")
|
|
||||||
&& !second.startsWith("pl")
|
|
||||||
&& !second.startsWith("ph")) {
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (lines[3].value.trimStart().startsWith("pl$register")) {
|
|
||||||
val second = lines[1].value.trimStart().take(6).lowercase()
|
|
||||||
val third = lines[2].value.trimStart().take(6).lowercase()
|
|
||||||
if(register !in second && register !in third
|
|
||||||
&& !second.startsWith("jsr") && !third.startsWith("jsr")
|
|
||||||
&& !second.startsWith("pl") && !third.startsWith("pl")
|
|
||||||
&& !second.startsWith("ph") && !third.startsWith("ph")) {
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (lines in linesByFour) {
|
|
||||||
optimize('a', lines)
|
|
||||||
optimize('x', lines)
|
|
||||||
optimize('y', lines)
|
|
||||||
|
|
||||||
val first = lines[1].value.trimStart()
|
|
||||||
val second = lines[2].value.trimStart()
|
|
||||||
val third = lines[3].value.trimStart()
|
|
||||||
|
|
||||||
// phy + ldy + pla -> tya + ldy
|
|
||||||
// phx + ldx + pla -> txa + ldx
|
|
||||||
// pha + lda + pla -> nop
|
|
||||||
when (first) {
|
|
||||||
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, " tya"))
|
|
||||||
}
|
|
||||||
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
|
||||||
mods.add(Modification(lines[1].index, false, " txa"))
|
|
||||||
}
|
|
||||||
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
|
|
||||||
for(lines in linesByFour) {
|
|
||||||
val first = lines[0].value.trimStart()
|
|
||||||
val second = lines[1].value.trimStart()
|
|
||||||
val third = lines[2].value.trimStart()
|
|
||||||
if(first.startsWith("lda") && second.startsWith("tsb") && third.startsWith("lda")) {
|
|
||||||
val operand1 = first.substring(3)
|
|
||||||
val operand2 = second.substring(3)
|
|
||||||
val operand3 = third.substring(3)
|
|
||||||
if(operand1!=operand2 && operand2==operand3) {
|
|
||||||
mods.add(Modification(lines[0].index, false, " lda $operand2"))
|
|
||||||
mods.add(Modification(lines[1].index, false, " ora $operand1"))
|
|
||||||
mods.add(Modification(lines[2].index, false, " sta $operand2"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
|
||||||
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
|
|
||||||
// this can be performed without the scratch variable: clc / adc something
|
|
||||||
val mods = mutableListOf<Modification>()
|
|
||||||
|
|
||||||
for(lines in linesByFour) {
|
|
||||||
val first = lines[0].value.trimStart()
|
|
||||||
val second = lines[1].value.trimStart()
|
|
||||||
val third = lines[2].value.trimStart()
|
|
||||||
val fourth = lines[3].value.trimStart()
|
|
||||||
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
|
|
||||||
if(fourth.substring(4)==first.substring(4)) {
|
|
||||||
mods.add(Modification(lines[0].index, false, " clc"))
|
|
||||||
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
|
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.code.ast.PtAsmSub
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.Cx16VirtualRegisters
|
import prog8.code.core.Cx16VirtualRegisters
|
||||||
import prog8.code.core.RegisterOrPair
|
import prog8.code.core.RegisterOrPair
|
||||||
|
import prog8.code.core.RegisterOrStatusflag
|
||||||
|
|
||||||
|
|
||||||
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
||||||
@ -10,23 +11,50 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
|||||||
// order is:
|
// order is:
|
||||||
// 1) cx16 virtual word registers,
|
// 1) cx16 virtual word registers,
|
||||||
// 2) paired CPU registers,
|
// 2) paired CPU registers,
|
||||||
// 3) single CPU registers (order Y,X,A),
|
// 3) single CPU registers (X last), except A,
|
||||||
// 4) floating point registers (FAC1, FAC2),
|
// 4) CPU Carry status flag
|
||||||
// 5) CPU Carry status flag
|
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
|
||||||
val args = sub.parameters.withIndex()
|
val args = sub.parameters.withIndex()
|
||||||
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
||||||
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
||||||
val (singleRegsMixed, rest) = args3.partition { it.value.first.registerOrPair != null }
|
val (regsWithoutA, args4) = args3.partition { it.value.first.registerOrPair != RegisterOrPair.A }
|
||||||
val (singleCpuRegs, floatRegs) = singleRegsMixed.partition {it.value.first.registerOrPair != RegisterOrPair.FAC1 && it.value.first.registerOrPair != RegisterOrPair.FAC2 }
|
val (regA, rest) = args4.partition { it.value.first.registerOrPair != null }
|
||||||
|
|
||||||
cx16regs.forEach { order += it.index }
|
cx16regs.forEach { order += it.index }
|
||||||
pairedRegs.forEach { order += it.index }
|
pairedRegs.forEach { order += it.index }
|
||||||
singleCpuRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
|
regsWithoutA.forEach {
|
||||||
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null})
|
if(it.value.first.registerOrPair != RegisterOrPair.X)
|
||||||
floatRegs.forEach { order += it.index }
|
order += it.index
|
||||||
|
}
|
||||||
|
regsWithoutA.firstOrNull { it.value.first.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
|
||||||
rest.forEach { order += it.index }
|
rest.forEach { order += it.index }
|
||||||
|
regA.forEach { order += it.index }
|
||||||
require(order.size==sub.parameters.size)
|
require(order.size==sub.parameters.size)
|
||||||
|
|
||||||
return order
|
return order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun asmsub6502ArgsHaveRegisterClobberRisk(
|
||||||
|
args: List<PtExpression>,
|
||||||
|
params: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>
|
||||||
|
): Boolean {
|
||||||
|
fun isClobberRisk(expr: PtExpression): Boolean {
|
||||||
|
when (expr) {
|
||||||
|
is PtArrayIndexer -> {
|
||||||
|
return params.any {
|
||||||
|
it.first.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtBuiltinFunctionCall -> {
|
||||||
|
if (expr.name == "lsb" || expr.name == "msb")
|
||||||
|
return isClobberRisk(expr.args[0])
|
||||||
|
if (expr.name == "mkword")
|
||||||
|
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
|
||||||
|
return !expr.isSimple()
|
||||||
|
}
|
||||||
|
else -> return !expr.isSimple()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.size>1 && args.any { isClobberRisk(it) }
|
||||||
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.code.GENERATED_LABEL_PREFIX
|
import com.github.michaelbull.result.Ok
|
||||||
import prog8.code.IAssemblyProgram
|
import com.github.michaelbull.result.Result
|
||||||
import prog8.code.core.CompilationOptions
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.core.*
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.code.core.OutputType
|
|
||||||
import prog8.code.target.C128Target
|
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.PETTarget
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.isRegularFile
|
||||||
|
|
||||||
|
|
||||||
internal class AssemblyProgram(
|
internal class AssemblyProgram(
|
||||||
@ -28,83 +27,47 @@ internal class AssemblyProgram(
|
|||||||
|
|
||||||
val assemblerCommand: List<String>
|
val assemblerCommand: List<String>
|
||||||
|
|
||||||
when(options.output) {
|
when (compTarget.name) {
|
||||||
OutputType.PRG -> {
|
in setOf("c64", "c128", "cx16") -> {
|
||||||
// CBM machines .prg generation.
|
// CBM machines .prg generation.
|
||||||
|
|
||||||
val command = mutableListOf("64tass", "--cbm-prg", "--ascii", "--case-sensitive", "--long-branch",
|
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
if(options.warnSymbolShadowing)
|
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
|
||||||
command.add("-Wshadow")
|
)
|
||||||
else
|
|
||||||
command.add("-Wno-shadow")
|
|
||||||
|
|
||||||
if(options.asmQuiet)
|
if(options.asmQuiet)
|
||||||
command.add("--quiet")
|
command.add("--quiet")
|
||||||
|
|
||||||
if(options.asmListfile) {
|
if(options.asmListfile)
|
||||||
command.add("--list=$listFile")
|
command.add("--list=$listFile")
|
||||||
}
|
|
||||||
|
|
||||||
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
val outFile = when (options.output) {
|
||||||
|
OutputType.PRG -> {
|
||||||
|
command.add("--cbm-prg")
|
||||||
|
println("\nCreating prg for target ${compTarget.name}.")
|
||||||
|
prgFile
|
||||||
|
}
|
||||||
|
OutputType.RAW -> {
|
||||||
|
command.add("--nostart")
|
||||||
|
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||||
|
binFile
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid output type")
|
||||||
|
}
|
||||||
|
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
if(!options.quiet)
|
|
||||||
println("\nCreating prg for target ${compTarget.name}.")
|
|
||||||
}
|
}
|
||||||
OutputType.XEX -> {
|
"atari" -> {
|
||||||
// Atari800XL .xex generation.
|
// Atari800XL .xex generation.
|
||||||
|
|
||||||
val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch",
|
// TODO are these options okay for atari?
|
||||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
|
||||||
|
|
||||||
if(options.warnSymbolShadowing)
|
|
||||||
command.add("-Wshadow")
|
|
||||||
else
|
|
||||||
command.add("-Wno-shadow")
|
|
||||||
|
|
||||||
if(options.asmQuiet)
|
|
||||||
command.add("--quiet")
|
|
||||||
|
|
||||||
if(options.asmListfile)
|
|
||||||
command.add("--list=$listFile")
|
|
||||||
|
|
||||||
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
|
||||||
assemblerCommand = command
|
|
||||||
if(!options.quiet)
|
|
||||||
println("\nCreating xex for target ${compTarget.name}.")
|
|
||||||
}
|
|
||||||
OutputType.RAW -> {
|
|
||||||
// Neo6502/headerless raw program generation.
|
|
||||||
val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch",
|
|
||||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
|
||||||
|
|
||||||
if(options.warnSymbolShadowing)
|
|
||||||
command.add("-Wshadow")
|
|
||||||
else
|
|
||||||
command.add("-Wno-shadow")
|
|
||||||
|
|
||||||
if(options.asmQuiet)
|
|
||||||
command.add("--quiet")
|
|
||||||
|
|
||||||
if(options.asmListfile)
|
|
||||||
command.add("--list=$listFile")
|
|
||||||
|
|
||||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
|
||||||
assemblerCommand = command
|
|
||||||
if(!options.quiet)
|
|
||||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
|
||||||
}
|
|
||||||
OutputType.LIBRARY -> {
|
|
||||||
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
|
|
||||||
|
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
|
"--no-monitor"
|
||||||
if(options.warnSymbolShadowing)
|
)
|
||||||
command.add("-Wshadow")
|
|
||||||
else
|
|
||||||
command.add("-Wno-shadow")
|
|
||||||
|
|
||||||
if(options.asmQuiet)
|
if(options.asmQuiet)
|
||||||
command.add("--quiet")
|
command.add("--quiet")
|
||||||
@ -112,30 +75,28 @@ internal class AssemblyProgram(
|
|||||||
if(options.asmListfile)
|
if(options.asmListfile)
|
||||||
command.add("--list=$listFile")
|
command.add("--list=$listFile")
|
||||||
|
|
||||||
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
val outFile = when (options.output) {
|
||||||
if(!options.quiet)
|
OutputType.XEX -> {
|
||||||
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
command.add("--atari-xex")
|
||||||
command.add("--cbm-prg")
|
println("\nCreating xex for target ${compTarget.name}.")
|
||||||
} else {
|
xexFile
|
||||||
if(!options.quiet)
|
}
|
||||||
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
OutputType.RAW -> {
|
||||||
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
command.add("--nostart")
|
||||||
|
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||||
|
binFile
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid output type")
|
||||||
}
|
}
|
||||||
|
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
|
||||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
|
||||||
assemblerCommand = command
|
assemblerCommand = command
|
||||||
}
|
}
|
||||||
|
else -> throw AssemblyError("invalid compilation target")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.compTarget.additionalAssemblerOptions!=null)
|
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
||||||
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
val result = proc.waitFor()
|
||||||
|
if (result == 0 && compTarget.name!="atari") {
|
||||||
val proc = ProcessBuilder(assemblerCommand)
|
|
||||||
if(!options.quiet)
|
|
||||||
proc.inheritIO()
|
|
||||||
val process = proc.start()
|
|
||||||
val result = process.waitFor()
|
|
||||||
if (result == 0) {
|
|
||||||
removeGeneratedLabelsFromMonlist()
|
removeGeneratedLabelsFromMonlist()
|
||||||
generateBreakpointList()
|
generateBreakpointList()
|
||||||
}
|
}
|
||||||
@ -143,7 +104,7 @@ internal class AssemblyProgram(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeGeneratedLabelsFromMonlist() {
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
|
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
|
||||||
val lines = viceMonListFile.toFile().readLines()
|
val lines = viceMonListFile.toFile().readLines()
|
||||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
@ -160,7 +121,7 @@ internal class AssemblyProgram(
|
|||||||
for (line in viceMonListFile.toFile().readLines()) {
|
for (line in viceMonListFile.toFile().readLines()) {
|
||||||
val match = pattern.matchEntire(line)
|
val match = pattern.matchEntire(line)
|
||||||
if (match != null)
|
if (match != null)
|
||||||
breakpoints.add("break $" + match.groupValues[1])
|
breakpoints.add("break \$" + match.groupValues[1])
|
||||||
}
|
}
|
||||||
val num = breakpoints.size
|
val num = breakpoints.size
|
||||||
breakpoints.add(0, "; breakpoint list now follows")
|
breakpoints.add(0, "; breakpoint list now follows")
|
||||||
@ -169,3 +130,18 @@ internal class AssemblyProgram(
|
|||||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||||
|
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
|
||||||
|
return com.github.michaelbull.result.runCatching {
|
||||||
|
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").text
|
||||||
|
}.mapError { NoSuchFileException(File(filename)) }
|
||||||
|
} else {
|
||||||
|
val sib = Path(source.origin).resolveSibling(filename)
|
||||||
|
if (sib.isRegularFile())
|
||||||
|
Ok(SourceCode.File(sib).text)
|
||||||
|
else
|
||||||
|
Ok(SourceCode.File(Path(filename)).text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user