Compare commits

..

1 Commits
v9.2 ... v8.14

Author SHA1 Message Date
e55a675d2e release 8.14 2023-05-29 23:00:43 +02:00
246 changed files with 3815 additions and 8079 deletions

View File

@ -10,18 +10,13 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 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@v3 uses: actions/setup-java@v2
with: with:
java-version: 11 java-version: 11
distribution: adopt distribution: adopt

3
.gitignore vendored
View File

@ -15,7 +15,6 @@ 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
@ -30,8 +29,6 @@ parsetab.py
compiler/lib/ compiler/lib/
.gradle .gradle
**/BuildVersion.kt
/prog8compiler.jar /prog8compiler.jar
sd*.img sd*.img
*.d64 *.d64

2
.idea/kotlinc.xml generated
View File

@ -4,6 +4,6 @@
<option name="jvmTarget" value="11" /> <option name="jvmTarget" value="11" />
</component> </component>
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0-release-358" /> <option name="version" value="1.8.21-release-380" />
</component> </component>
</project> </project>

View File

@ -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.0"> <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.0/antlr4-4.13.0.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.0/antlr4-runtime-4.13.0.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!/" />

View File

@ -1,21 +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.6.2" /> <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.6.2/kotest-assertions-core-jvm-5.6.2.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/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.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.8.10/kotlin-stdlib-1.8.10.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/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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.7.0/kotlinx-coroutines-jdk8-1.7.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$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.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.6.2/kotest-common-jvm-5.6.2.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.6.2/kotest-assertions-api-jvm-5.6.2.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.7.0/kotlinx-coroutines-core-jvm-1.7.0.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/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -1,22 +1,22 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="io.kotest.property.jvm" type="repository"> <library name="io.kotest.property.jvm" type="repository">
<properties maven-id="io.kotest:kotest-property-jvm:5.6.2" /> <properties maven-id="io.kotest:kotest-property-jvm:5.5.5" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.6.2/kotest-property-jvm-5.6.2.jar!/" /> <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$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.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$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.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$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.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/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.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-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.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/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.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!/" /> <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-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -1,42 +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.6.2" /> <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.6.2/kotest-runner-junit5-jvm-5.6.2.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.6.2/kotest-framework-api-jvm-5.6.2.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.6.2/kotest-assertions-shared-jvm-5.6.2.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.7.0/kotlinx-coroutines-test-jvm-1.7.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.6.2/kotest-common-jvm-5.6.2.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.6.2/kotest-framework-engine-jvm-5.6.2.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.157/classgraph-4.8.157.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.2.0/opentest4j-1.2.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.7.0/kotlinx-coroutines-debug-1.7.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.6.2/kotest-framework-discovery-jvm-5.6.2.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.6.2/kotest-assertions-core-jvm-5.6.2.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.7.0/kotlinx-coroutines-jdk8-1.7.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.6.2/kotest-assertions-api-jvm-5.6.2.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.6.2/kotest-extensions-jvm-5.6.2.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.6.2/kotest-framework-concurrency-jvm-5.6.2.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.7.0/kotlinx-coroutines-core-jvm-1.7.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-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.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.8.10/kotlin-stdlib-1.8.10.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/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.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-common/1.8.10/kotlin-stdlib-common-1.8.10.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.8.10/kotlin-reflect-1.8.10.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 />

View File

@ -1,13 +1,13 @@
<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:1.1.18" /> <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/1.1.18/kotlin-result-jvm-1.1.18.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-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.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/1.8.10/kotlin-stdlib-1.8.10.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.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -14,21 +14,6 @@ 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).
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:

View File

@ -26,7 +26,7 @@ compileTestKotlin {
dependencies { dependencies {
// should have no dependencies to other modules // should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
} }
sourceSets { sourceSets {

View File

@ -80,16 +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? {
val node = flat[name]
return when(node) {
is StMemVar -> node.length
is StMemorySlab -> node.size.toInt()
is StStaticVariable -> node.length
else -> null
}
}
} }

View File

@ -94,9 +94,6 @@ 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) {
// ... split array also add _lsb and _msb to symboltable?
// }
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node) StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
} }
is PtBuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {

View File

@ -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,9 +63,7 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>() children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? = fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" || it.name=="p8_main" } allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub?
?.children
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8_start" || it.name=="p8_main.p8_start") } as PtSub?
} }
@ -74,7 +71,6 @@ class PtBlock(name: String,
val address: UInt?, val address: UInt?,
val library: Boolean, val library: Boolean,
val forceOutput: Boolean, val forceOutput: Boolean,
val noSymbolPrefixing: Boolean,
val alignment: BlockAlignment, 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.
position: Position position: Position

View File

@ -1,6 +1,9 @@
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.round import kotlin.math.round
@ -25,7 +28,7 @@ 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 -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
@ -48,7 +51,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
this.name == target.identifier!!.name this.name == target.identifier!!.name
} }
target.array != null && this is PtArrayIndexer -> { target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index
} }
else -> false else -> false
} }
@ -115,9 +118,6 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
val index: PtExpression val index: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
init { init {
require(elementType in NumericDatatypes) require(elementType in NumericDatatypes)
} }

View File

@ -17,7 +17,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> "&" is PtAddressOf -> "&"
is PtArray -> "array len=${node.children.size} ${type(node.type)}" is PtArray -> "array len=${node.children.size} ${type(node.type)}"
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}" is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}" is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
is PtBuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {
val str = if(node.void) "void " else "" val str = if(node.void) "void " else ""
@ -96,14 +96,13 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
str str
} }
is PtVariable -> { is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val str = if(node.arraySize!=null) { val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type) val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}" "${eltType.name.lowercase()}[${node.arraySize}] ${node.name}"
} }
else if(node.type in ArrayDatatypes) { else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type) val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] $split ${node.name}" "${eltType.name.lowercase()}[] ${node.name}"
} }
else else
"${node.type.name.lowercase()} ${node.name}" "${node.type.name.lowercase()} ${node.name}"

View File

@ -68,6 +68,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
} }
} }
val BuiltinFunctions: Map<String, FSignature> = mapOf( val BuiltinFunctions: Map<String, FSignature> = mapOf(
// this set of function have no return value and operate in-place: // this set of function have no return value and operate in-place:
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
@ -79,41 +80,19 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
// cmp returns a status in the carry flag, but not a proper return value // cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), "cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), "prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null), "abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow: // normal functions follow:
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE), "sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE), "sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null), "sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), "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__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE), "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__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), "any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), "all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), "lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), "msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD), "mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), "peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), "peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), "poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),

View File

@ -19,8 +19,8 @@ class CompilationOptions(val output: OutputType,
var asmQuiet: Boolean = false, var asmQuiet: Boolean = false,
var asmListfile: Boolean = false, var asmListfile: Boolean = false,
var experimentalCodegen: Boolean = false, var experimentalCodegen: Boolean = false,
var varsHighBank: Int? = null, var varsHigh: Boolean = false,
var splitWordArrays: Boolean = false, var useNewExprCode: Boolean = false,
var evalStackBaseAddress: UInt? = null, var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""), var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap() var symbolDefs: Map<String, String> = emptyMap()

View File

@ -11,9 +11,7 @@ enum class DataType {
ARRAY_UB, // pass by reference ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference ARRAY_UW, // pass by reference
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
ARRAY_W, // pass by reference ARRAY_W, // pass by reference
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
ARRAY_F, // pass by reference ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference ARRAY_BOOL, // pass by reference
UNDEFINED; UNDEFINED;
@ -121,14 +119,12 @@ val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWO
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL) 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 NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val SignedDatatypes = arrayOf(DataType.BYTE, 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_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL) 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 StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf( val IterableDatatypes = arrayOf(
DataType.STR, DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL DataType.ARRAY_F, DataType.ARRAY_BOOL
) )
val PassByValueDatatypes = NumericDatatypes val PassByValueDatatypes = NumericDatatypes
@ -139,8 +135,6 @@ val ArrayToElementTypes = mapOf(
DataType.ARRAY_UB to DataType.UBYTE, DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD, DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD, DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_W_SPLIT to DataType.WORD,
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT, DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL DataType.ARRAY_BOOL to DataType.BOOL
) )

View File

@ -3,7 +3,6 @@ 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 undefined(symbol: List<String>, position: Position)
fun noErrors(): Boolean fun noErrors(): Boolean
fun report() fun report()
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) { fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {

View File

@ -28,9 +28,9 @@ dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
} }
sourceSets { sourceSets {

View File

@ -3,7 +3,6 @@ package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import prog8.code.StNodeType import prog8.code.StNodeType
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
@ -15,180 +14,18 @@ import kotlin.io.path.writeLines
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1" internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend { class AsmGen6502: ICodeGeneratorBackend {
override fun generate( override fun generate(
program: PtProgram, program: PtProgram,
symbolTable: SymbolTable, symbolTable: SymbolTable,
options: CompilationOptions, options: CompilationOptions,
errors: IErrorReporter errors: IErrorReporter
): IAssemblyProgram? { ): IAssemblyProgram? {
val st = if(prefixSymbols) prefixSymbols(program, options, symbolTable) else symbolTable val asmgen = AsmGen6502Internal(program, symbolTable, options, errors)
val asmgen = AsmGen6502Internal(program, st, options, errors)
return asmgen.compileToAssembly() return asmgen.compileToAssembly()
} }
private fun prefixSymbols(program: PtProgram, options: CompilationOptions, st: SymbolTable): SymbolTable {
val nodesToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
fun prefixNamedNode(node: PtNamedNode) {
node.name = "p8_${node.name}"
}
fun prefixSymbols(node: PtNode) {
when(node) {
is PtAsmSub -> {
prefixNamedNode(node)
node.parameters.forEach { (_, param) -> prefixNamedNode(param) }
}
is PtSub -> {
prefixNamedNode(node)
node.parameters.forEach { prefixNamedNode(it) }
}
is PtFunctionCall -> {
val stNode = st.lookup(node.name)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
functionCallsToPrefix += node.parent to index
}
}
is PtIdentifier -> {
var lookupName = node.name
if(node.type in SplitWordArrayTypes && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
lookupName = lookupName.dropLast(4)
}
val stNode = st.lookup(lookupName)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
is PtJump -> {
if(node.identifier!=null) {
val stNode = st.lookup(node.identifier!!.name)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
else if(node.generatedLabel!=null) {
val stNode = st.lookup(node.generatedLabel!!)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
}
is PtBlock -> prefixNamedNode(node)
is PtConstant -> prefixNamedNode(node)
is PtLabel -> prefixNamedNode(node)
is PtMemMapped -> prefixNamedNode(node)
is PtSubroutineParameter -> prefixNamedNode(node)
is PtVariable -> {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
else -> { }
}
node.children.forEach { prefixSymbols(it) }
}
program.allBlocks().forEach { block ->
if (!block.noSymbolPrefixing) {
prefixSymbols(block)
}
}
nodesToPrefix.forEach { (parent, index) ->
val node = parent.children[index]
when(node) {
is PtIdentifier -> parent.children[index] = node.prefix(parent, st)
is PtFunctionCall -> throw AssemblyError("PtFunctionCall should be processed in their own list, last")
is PtJump -> parent.children[index] = node.prefix(parent, st)
is PtVariable -> parent.children[index] = node.prefix(st)
else -> throw AssemblyError("weird node to prefix $node")
}
}
// reversed so inner calls (such as arguments to a function call) get processed before the actual function call itself
functionCallsToPrefix.reversed().forEach { (parent, index) ->
val node = parent.children[index]
if(node is PtFunctionCall) {
parent.children[index] = node.prefix(parent)
} else {
throw AssemblyError("expected PtFunctionCall")
}
}
return SymbolTableMaker(program, options).make()
}
} }
private fun PtVariable.prefix(st: SymbolTable): PtVariable {
name = name.split('.').map {"p8_$it" }.joinToString(".")
if(value==null)
return this
val arrayValue = value as? PtArray
return if(arrayValue!=null && arrayValue.children.any { it !is PtNumber} ) {
val newValue = PtArray(arrayValue.type, arrayValue.position)
arrayValue.children.forEach { elt ->
when(elt) {
is PtIdentifier -> newValue.add(elt.prefix(arrayValue, st))
is PtNumber -> newValue.add(elt)
is PtAddressOf -> {
if(elt.definingBlock()?.noSymbolPrefixing==true)
newValue.add(elt)
else {
val newAddr = PtAddressOf(elt.position)
newAddr.children.add(elt.identifier.prefix(newAddr, st))
newAddr.parent = arrayValue
newValue.add(newAddr)
}
}
else -> throw AssemblyError("weird array value element $elt")
}
}
PtVariable(name, type, zeropage, newValue, arraySize, position)
}
else this
}
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
val jump = if(identifier!=null) {
val prefixedIdent = identifier!!.prefix(this, st)
PtJump(prefixedIdent, address, generatedLabel, position)
} else {
val prefixedLabel = generatedLabel!!.split('.').map {"p8_$it" }.joinToString(".")
PtJump(null, address, prefixedLabel, position)
}
jump.parent = parent
return jump
}
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
val newName = name.split('.').map {"p8_$it" }.joinToString(".")
val call = PtFunctionCall(newName, void, type, position)
call.children.addAll(children)
call.children.forEach { it.parent = call }
call.parent = parent
if(name.endsWith("concat_string"))
println("CONCAT ${this.position}")
return call
}
private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
val target = st.lookup(name)
if(target?.astNode?.definingBlock()?.noSymbolPrefixing==true)
return this
val newName = name.split('.').map { "p8_$it" }.joinToString(".")
val node = PtIdentifier(newName, type, position)
node.parent = parent
return node
}
class AsmGen6502Internal ( class AsmGen6502Internal (
val program: PtProgram, val program: PtProgram,
internal val symbolTable: SymbolTable, internal val symbolTable: SymbolTable,
@ -525,15 +362,13 @@ class AsmGen6502Internal (
internal fun loadScaledArrayIndexIntoRegister( internal fun loadScaledArrayIndexIntoRegister(
expr: PtArrayIndexer, expr: PtArrayIndexer,
elementDt: DataType, elementDt: DataType,
register: CpuRegister register: CpuRegister,
addOneExtra: Boolean = false
) { ) {
val reg = register.toString().lowercase() val reg = register.toString().lowercase()
val indexnum = expr.index.asConstInteger() val indexnum = expr.index.asConstInteger()
if (indexnum != null) { if (indexnum != null) {
val indexValue = if(expr.splitWords) val indexValue = indexnum * options.compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0
indexnum
else
indexnum * options.compTarget.memorySize(elementDt)
out(" ld$reg #$indexValue") out(" ld$reg #$indexValue")
return return
} }
@ -542,42 +377,77 @@ class AsmGen6502Internal (
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}") ?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}")
val indexName = asmVariableName(indexVar) val indexName = asmVariableName(indexVar)
if (addOneExtra) {
if(expr.splitWords) { // add 1 to the result
when (register) { when (elementDt) {
CpuRegister.A -> out(" lda $indexName") in ByteDatatypes -> {
CpuRegister.X -> out(" ldx $indexName") out(" ldy $indexName | iny")
CpuRegister.Y -> out(" ldy $indexName") when (register) {
} CpuRegister.A -> out(" tya")
return CpuRegister.X -> out(" tyx")
} CpuRegister.Y -> {
}
when (elementDt) { }
in ByteDatatypes -> out(" ld$reg $indexName")
in WordDatatypes -> {
out(" lda $indexName | asl a")
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
} }
} in WordDatatypes -> {
DataType.FLOAT -> { out(" lda $indexName | sec | rol a")
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"} when (register) {
out(""" CpuRegister.A -> {
lda $indexName }
asl a CpuRegister.X -> out(" tax")
asl a CpuRegister.Y -> out(" tay")
clc }
adc $indexName"""
)
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
} }
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out(
"""
lda $indexName
asl a
asl a
sec
adc $indexName"""
)
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
}
} else {
when (elementDt) {
in ByteDatatypes -> out(" ld$reg $indexName")
in WordDatatypes -> {
out(" lda $indexName | asl a")
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out(
"""
lda $indexName
asl a
asl a
clc
adc $indexName"""
)
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
} }
else -> throw AssemblyError("weird dt")
} }
} }
@ -676,6 +546,7 @@ class AsmGen6502Internal (
private fun translate(stmt: PtIfElse) { private fun translate(stmt: PtIfElse) {
val condition = stmt.condition as? PtBinaryExpression val condition = stmt.condition as? PtBinaryExpression
if(condition!=null) { if(condition!=null) {
require(!options.useNewExprCode)
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>' requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) { if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull() val jump = stmt.ifScope.children.singleOrNull()
@ -803,48 +674,65 @@ class AsmGen6502Internal (
private fun repeatWordCount(count: Int, stmt: PtRepeatLoop) { private fun repeatWordCount(count: Int, stmt: PtRepeatLoop) {
require(count in 257..65535) { "invalid repeat count ${stmt.position}" } require(count in 257..65535) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt) if(isTargetCpu(CpuType.CPU65c02)) {
// the iny + double dec is microoptimization of the 16 bit loop val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt)
out(""" out("""
ldy #>$count lda #<$count
lda #<$count ldy #>$count
beq + sta $counterVar
iny sty $counterVar+1
+ sta $counterVar
sty $counterVar+1
$repeatLabel""") $repeatLabel""")
translate(stmt.statements) translate(stmt.statements)
out(""" out("""
dec $counterVar lda $counterVar
bne $repeatLabel bne +
dec $counterVar+1 dec $counterVar+1
bne $repeatLabel""") + dec $counterVar
lda $counterVar
ora $counterVar+1
bne $repeatLabel""")
} else {
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
out("""
lda #<$count
ldy #>$count
sta $counterVar
sty $counterVar+1
$repeatLabel""")
translate(stmt.statements)
out("""
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
lda $counterVar
ora $counterVar+1
bne $repeatLabel""")
}
} }
private fun repeatWordCountInAY(endLabel: String, stmt: PtRepeatLoop) { private fun repeatWordCountInAY(endLabel: String, stmt: PtRepeatLoop) {
// note: A/Y must have been loaded with the number of iterations! // note: A/Y must have been loaded with the number of iterations!
// the iny + double dec is microoptimization of the 16 bit loop // no need to explicitly test for 0 iterations as this is done in the countdown logic below
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt) val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
out(""" out("""
cmp #0 sta $counterVar
beq + sty $counterVar+1
iny $repeatLabel lda $counterVar
+ sta $counterVar bne +
sty $counterVar+1 lda $counterVar+1
ora $counterVar+1 beq $endLabel
beq $endLabel lda $counterVar
$repeatLabel""") bne +
dec $counterVar+1
+ dec $counterVar
""")
translate(stmt.statements) translate(stmt.statements)
out(""" jmp(repeatLabel)
dec $counterVar
bne $repeatLabel
dec $counterVar+1
bne $repeatLabel""")
out(endLabel) out(endLabel)
} }
private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) { private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) {
require(count in 2..256) { "invalid repeat count ${stmt.position}" } require(count in 2..256) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
@ -1135,10 +1023,18 @@ $repeatLabel""")
} }
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? { internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
if (pointerOffsetExpr !is PtBinaryExpression) return null val left: PtExpression
val operator = pointerOffsetExpr.operator val right: PtExpression
val left = pointerOffsetExpr.left val operator: String
val right = pointerOffsetExpr.right
if (pointerOffsetExpr is PtBinaryExpression) {
require(!options.useNewExprCode)
operator = pointerOffsetExpr.operator
left = pointerOffsetExpr.left
right = pointerOffsetExpr.right
}
else return null
if (operator != "+") return null if (operator != "+") return null
val leftDt = left.type val leftDt = left.type
val rightDt = right.type val rightDt = right.type
@ -1250,7 +1146,10 @@ $repeatLabel""")
} }
internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? { internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? {
val stScope = asmgen.symbolTable.lookup(name) ?: return null val stScope = asmgen.symbolTable.lookup(name)
require(stScope!=null) {
"invalid name lookup $name"
}
val node = stScope.astNode val node = stScope.astNode
if(node is PtSubroutineParameter) if(node is PtSubroutineParameter)
return node return node
@ -2898,6 +2797,7 @@ $repeatLabel""")
out(" sta P8ESTACK_LO,x | dex") out(" sta P8ESTACK_LO,x | dex")
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!options.useNewExprCode)
val addrExpr = expr.address as PtBinaryExpression val addrExpr = expr.address as PtBinaryExpression
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(pushResultOnEstack) if(pushResultOnEstack)

View File

@ -43,14 +43,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
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()) { if(mods.isNotEmpty()) {
apply(mods, lines) apply(mods, lines)
linesByFour = getLinesBy(lines, 4) linesByFour = getLinesBy(lines, 4)
@ -524,40 +517,3 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
} }
return mods return mods
} }
private fun optimizeUselessPushPopStack(linesByFour: List<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)
}
return mods
}

View File

@ -31,15 +31,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"msb" -> funcMsb(fcall, resultToStack, resultRegister) "msb" -> funcMsb(fcall, resultToStack, resultRegister)
"lsb" -> funcLsb(fcall, resultToStack, resultRegister) "lsb" -> funcLsb(fcall, resultToStack, resultRegister)
"mkword" -> funcMkword(fcall, resultToStack, resultRegister) "mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultToStack, resultRegister) "abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultToStack, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultToStack, resultRegister)
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope) "any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope) "sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall) "divmod" -> funcDivmod(fcall)
"divmod__uword" -> funcDivmodW(fcall) "divmodw" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall) "rol2" -> funcRol2(fcall)
"ror" -> funcRor(fcall) "ror" -> funcRor(fcall)
@ -272,34 +269,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assign, fcall.definingISub()) asmgen.translateNormalAssignment(assign, fcall.definingISub())
} }
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope) translateArguments(fcall, scope)
when(fcall.args[0].type) { if(resultToStack)
DataType.UBYTE -> { asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
if(resultToStack) else {
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_stack") asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
else { assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
}
}
DataType.UWORD -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
}
}
DataType.FLOAT -> {
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
if(resultToStack)
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
else {
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
}
else -> throw AssemblyError("weird dt")
} }
} }
@ -352,7 +328,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda #$numElements lda #$numElements
jsr floats.func_reverse_f""") jsr floats.func_reverse_f""")
} }
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
@ -399,7 +374,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
jsr prog8_lib.func_sort_ub""") jsr prog8_lib.func_sort_ub""")
} }
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported") DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} else } else
@ -434,8 +408,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("ror2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror2", 'w') translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw") asmgen.out(" jsr prog8_lib.ror2_array_uw")
} }
@ -495,8 +467,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("ror split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror", 'w') translateRolRorArrayArgs(what.variable, what, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw") asmgen.out(" jsr prog8_lib.ror_array_uw")
} }
@ -539,8 +509,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("rol2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol2", 'w') translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw") asmgen.out(" jsr prog8_lib.rol2_array_uw")
} }
@ -600,8 +568,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("rol split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol", 'w') translateRolRorArrayArgs(what.variable, what, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw") asmgen.out(" jsr prog8_lib.rol_array_uw")
} }
@ -617,8 +583,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) { private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
if(indexer.splitWords)
TODO("rol/ror split words access ${indexer.position}")
if(arrayvar.type==DataType.UWORD) { if(arrayvar.type==DataType.UWORD) {
if(dt!='b') if(dt!='b')
throw AssemblyError("non-array var indexing requires bytes dt") throw AssemblyError("non-array var indexing requires bytes dt")
@ -666,7 +630,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack") DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack") DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack") DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
} else { } else {
@ -674,7 +637,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0") DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0") DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0") DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
@ -686,35 +648,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val dt = fcall.args.single().type val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt) { when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack") DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
DataType.UWORD -> {}
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack") DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
else -> { else -> throw AssemblyError("weird type")
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
}
} }
} else { } else {
when (dt) { when (dt) {
DataType.BYTE -> { DataType.UBYTE -> asmgen.out(" ldy #0")
asmgen.out(" jsr prog8_lib.abs_b_into_A") DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false) DataType.UWORD -> {}
} DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
DataType.WORD -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
}
DataType.FLOAT -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
DataType.UBYTE -> {
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
}
DataType.UWORD -> {
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
}
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
} }
} }
@ -751,6 +699,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr) val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) { if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
@ -812,6 +761,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else fallback() } else fallback()
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr) val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) { if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
@ -846,161 +796,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
when(fcall.type) {
in ByteDatatypes -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed)
}
}
in WordDatatypes -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
}
}
else -> throw AssemblyError("invalid dt")
}
}
private fun funcMin(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left
asmgen.out(" cmp P8ZP_SCRATCH_B1")
if(signed) asmgen.out(" bmi +") else asmgen.out(" bcc +")
asmgen.out("""
lda P8ZP_SCRATCH_B1
+""")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
}
} else if(fcall.type in WordDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) {
asmgen.out("""
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2
tya
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bpl +
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jmp ++
+ lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
+""")
} else {
asmgen.out("""
lda P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2+1
bcc ++
bne +
lda P8ZP_SCRATCH_W1
cmp P8ZP_SCRATCH_W2
bcc ++
+ lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
jmp ++
+ lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
+""")
}
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
}
} else {
throw AssemblyError("min float not supported")
}
}
private fun funcMax(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
asmgen.out(" cmp P8ZP_SCRATCH_B1")
if(signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
asmgen.out("""
lda P8ZP_SCRATCH_B1
+""")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
}
} else if(fcall.type in WordDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) {
asmgen.out("""
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2
tya
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bmi +
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jmp ++
+ lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
+""")
} else {
asmgen.out("""
lda P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2+1
bcc ++
bne +
lda P8ZP_SCRATCH_W1
cmp P8ZP_SCRATCH_W2
bcc ++
+ lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jmp ++
+ lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
+""")
}
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
}
} else {
throw AssemblyError("max float not supported")
}
}
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(resultToStack) { if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
@ -1160,27 +955,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// and will not generate another cmp when lsb() is directly used inside a comparison expression. // and will not generate another cmp when lsb() is directly used inside a comparison expression.
} }
RegisterOrPair.Y -> { RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tay | cpy #0") asmgen.out(" tay | pla | cpy #0")
}
RegisterOrPair.AY -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" ldy #0 | cmp #0")
}
RegisterOrPair.AX -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
asmgen.out(" ldx #0 | cmp #0")
}
RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
asmgen.out(" ldy #0 | cpx #0")
}
in Cx16VirtualRegisters -> {
asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister)
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
zero.parent=fcall
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
asmgen.out(" lda cx16.r0L")
} }
else -> throw AssemblyError("invalid reg") else -> throw AssemblyError("invalid reg")
} }

View File

@ -237,9 +237,6 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float") asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
} }
in SplitWordArrayTypes -> {
throw AssemblyError("can't push address of split-word array ${expr.position}")
}
in IterableDatatypes -> { in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex") asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
} }
@ -248,6 +245,8 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
} }
private fun translateExpression(expr: PtBinaryExpression) { private fun translateExpression(expr: PtBinaryExpression) {
require(!asmgen.options.useNewExprCode)
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED! // Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right)) if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
return return
@ -762,9 +761,6 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
return return
} }
if(arrayExpr.splitWords)
TODO("split words expression ${arrayExpr.position}")
val constIndexNum = arrayExpr.index.asConstInteger() val constIndexNum = arrayExpr.index.asConstInteger()
if(constIndexNum!=null) { if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt) val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)

View File

@ -51,35 +51,7 @@ internal class ForLoopsAsmGen(private val program: PtProgram,
// loop over byte range via loopvar // loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt)) asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false) asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
@ -97,35 +69,7 @@ $modifiedLabel cmp #0 ; modified
// loop over byte range via loopvar // loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt)) asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false) asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(stepsize>0) { if(stepsize>0) {
@ -156,9 +100,8 @@ $modifiedLabel cmp #0 ; modified
stepsize == 1 || stepsize == -1 -> { stepsize == 1 || stepsize == -1 -> {
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range) assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
@ -191,9 +134,8 @@ $modifiedLabel2 cmp #0 ; modified
// (u)words, step >= 2 // (u)words, step >= 2
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range) assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
@ -240,15 +182,31 @@ $endLabel""")
// (u)words, step <= -2 // (u)words, step <= -2
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range) assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(iterableDt==DataType.ARRAY_UW) {
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
sta $varname
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
$modifiedLabel cmp #0 ; modified
bcc $endLabel
bne $loopLabel
lda $varname
$modifiedLabel2 cmp #0 ; modified
bcs $loopLabel
$endLabel""")
} else {
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
sec sec
@ -266,6 +224,7 @@ $modifiedLabel sbc #0 ; modified
eor #$80 eor #$80
+ bpl $loopLabel + bpl $loopLabel
$endLabel""") $endLabel""")
}
} }
} }
} }
@ -275,56 +234,6 @@ $endLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
// pre-check for end already reached.
// 'to' is in AY, do NOT clobber this!
if(iterableDt==DataType.ARRAY_W) {
if(stepsize<0)
asmgen.out("""
sta P8ZP_SCRATCH_W2 ; to
sty P8ZP_SCRATCH_W2+1 ; to
lda $fromVar
cmp P8ZP_SCRATCH_W2
lda $fromVar+1
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #${'$'}80
+ bmi $endLabel
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1""")
else
asmgen.out("""
sta P8ZP_SCRATCH_REG
cmp $fromVar
tya
sbc $fromVar+1
bvc +
eor #${'$'}80
+ bmi $endLabel
lda P8ZP_SCRATCH_REG""")
} else {
if(stepsize<0)
asmgen.out("""
cpy $fromVar+1
beq +
bcc ++
bcs $endLabel
+ cmp $fromVar
bcc +
beq +
bne $endLabel
+""")
else
asmgen.out("""
cpy $fromVar+1
bcc $endLabel
bne +
cmp $fromVar
bcc $endLabel
+""")
}
}
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) { private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
@ -423,46 +332,7 @@ $loopLabel sty $indexVar
// allocate index var on ZP if possible // allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
asmgen.out("$indexVar .byte 0")
}
asmgen.out(endLabel)
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
numElements!!
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda ${iterableName}_lsb,y
sta $loopvarName
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255u) {
asmgen.out("""
ldy $indexVar
iny
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16u) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") } failure = { asmgen.out("$indexVar .byte 0") }
) )
} else { } else {
@ -720,7 +590,7 @@ $loopLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) = private fun assignLoopvar(stmt: PtForLoop, range: PtRange) =
asmgen.assignExpressionToVariable( asmgen.assignExpressionToVariable(
range.from, range.from,
asmgen.asmVariableName(stmt.variable), asmgen.asmVariableName(stmt.variable),

View File

@ -164,7 +164,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(requiredDt!=value.type) if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte value is required") throw AssemblyError("for statusflag, byte value is required")
if (statusflag == Statusflag.Pc) { if (statusflag == Statusflag.Pc) {
// this boolean param needs to be set last, right before the jsr // this param needs to be set last, right before the jsr
// for now, this is already enforced on the subroutine definition by the Ast Checker // for now, this is already enforced on the subroutine definition by the Ast Checker
when(value) { when(value) {
is PtNumber -> { is PtNumber -> {
@ -183,7 +183,12 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
else -> { else -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ror a") asmgen.out("""
beq +
sec
bcs ++
+ clc
+""")
} }
} }
} else throw AssemblyError("can only use Carry as status flag parameter") } else throw AssemblyError("can only use Carry as status flag parameter")

View File

@ -65,31 +65,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable) val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
val elementDt = targetArrayIdx.type val elementDt = targetArrayIdx.type
val constIndex = targetArrayIdx.index.asConstInteger() val constIndex = targetArrayIdx.index.asConstInteger()
if(targetArrayIdx.splitWords) {
if(constIndex!=null) {
if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb+$constIndex | bne + | inc ${asmArrayvarname}_msb+$constIndex |+")
else
asmgen.out("""
lda ${asmArrayvarname}_lsb+$constIndex
bne +
dec ${asmArrayvarname}_msb+$constIndex
+ dec ${asmArrayvarname}_lsb+$constIndex""")
} else {
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
else
asmgen.out("""
lda ${asmArrayvarname}_lsb,x
bne +
dec ${asmArrayvarname}_msb,x
+ dec ${asmArrayvarname}_lsb,x""")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
return
}
if(constIndex!=null) { if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt) val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) { when(elementDt) {
@ -99,10 +74,11 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+") asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
else else
asmgen.out(""" asmgen.out("""
lda $asmArrayvarname+$indexValue lda $asmArrayvarname+$indexValue
bne + bne +
dec $asmArrayvarname+$indexValue+1 dec $asmArrayvarname+$indexValue+1
+ dec $asmArrayvarname+$indexValue""") + dec $asmArrayvarname+$indexValue
""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)") asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
@ -113,8 +89,9 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
} }
else else
{ {
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.saveRegisterLocal(CpuRegister.X, scope!!) asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X) asmgen.out(" tax")
when(elementDt) { when(elementDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x") asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
@ -124,20 +101,20 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+") asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
else else
asmgen.out(""" asmgen.out("""
lda $asmArrayvarname,x lda $asmArrayvarname,x
bne + bne +
dec $asmArrayvarname+1,x dec $asmArrayvarname+1,x
+ dec $asmArrayvarname,x + dec $asmArrayvarname,x
""") """)
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
ldy #>$asmArrayvarname ldy #>$asmArrayvarname
clc clc
adc #<$asmArrayvarname adc #<$asmArrayvarname
bcc + bcc +
iny iny
+ jsr floats.inc_var_f""") + jsr floats.inc_var_f""")
} }
else -> throw AssemblyError("weird array elt dt") else -> throw AssemblyError("weird array elt dt")
} }

View File

@ -33,9 +33,8 @@ internal class ProgramAndVarsGen(
internal fun generate() { internal fun generate() {
header() header()
val allBlocks = program.allBlocks() val allBlocks = program.allBlocks()
if(allBlocks.first().name != "main")
if(allBlocks.first().name != "p8_main" && allBlocks.first().name != "main") throw AssemblyError("first block should be 'main'")
throw AssemblyError("first block should be 'main' or 'p8_main'")
if(errors.noErrors()) { if(errors.noErrors()) {
program.allBlocks().forEach { block2asm(it) } program.allBlocks().forEach { block2asm(it) }
@ -105,15 +104,15 @@ internal class ProgramAndVarsGen(
asmgen.out("+\t.word 0") asmgen.out("+\t.word 0")
asmgen.out("prog8_entrypoint\t; assembly code starts here") asmgen.out("prog8_entrypoint\t; assembly code starts here")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jsr sys.init_system") asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr sys.init_system_phase2") asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
} }
CbmPrgLauncherType.NONE -> { CbmPrgLauncherType.NONE -> {
asmgen.out("; ---- program without basic sys call ----") asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}") asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jsr sys.init_system") asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr sys.init_system_phase2") asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
} }
} }
} }
@ -121,8 +120,8 @@ internal class ProgramAndVarsGen(
asmgen.out("; ---- atari xex program ----") asmgen.out("; ---- atari xex program ----")
asmgen.out("* = ${options.loadAddress.toHex()}") asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jsr sys.init_system") asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr sys.init_system_phase2") asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
} }
} }
@ -140,24 +139,24 @@ internal class ProgramAndVarsGen(
"cx16" -> { "cx16" -> {
if(options.floats) if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr p8_main.p8_start") asmgen.out(" jsr main.start")
asmgen.out(" jmp sys.cleanup_at_exit") asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
} }
"c64" -> { "c64" -> {
asmgen.out(" jsr p8_main.p8_start | lda #31 | sta $01") asmgen.out(" jsr main.start | lda #31 | sta $01")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jmp sys.cleanup_at_exit") asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else else
asmgen.out(" rts") asmgen.out(" rts")
} }
"c128" -> { "c128" -> {
asmgen.out(" jsr p8_main.p8_start | lda #0 | sta ${"$"}ff00") asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jmp sys.cleanup_at_exit") asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else else
asmgen.out(" rts") asmgen.out(" rts")
} }
else -> asmgen.jmp("p8_main.p8_start") else -> asmgen.jmp("main.start")
} }
} }
@ -176,8 +175,7 @@ internal class ProgramAndVarsGen(
private fun footer() { private fun footer() {
asmgen.out("; bss sections") asmgen.out("; bss sections")
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}") if(options.varsHigh) {
if(options.varsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) { if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined") throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
} }
@ -313,9 +311,7 @@ internal class ProgramAndVarsGen(
asmgen.out("${sub.name}\t$asmStartScope") asmgen.out("${sub.name}\t$asmStartScope")
val scope = symboltable.lookupOrElse(sub.scopedName) { val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
throw AssemblyError("lookup ${sub.scopedName}")
}
require(scope.type==StNodeType.SUBROUTINE) require(scope.type==StNodeType.SUBROUTINE)
val varsInSubroutine = getVars(scope) val varsInSubroutine = getVars(scope)
@ -335,7 +331,7 @@ internal class ProgramAndVarsGen(
asmsubs2asm(sub.children) asmsubs2asm(sub.children)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic // the main.start subroutine is the program's entrypoint and should perform some initialization logic
if((sub.name=="start" || sub.name=="p8_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8_main")) if(sub.name=="start" && sub.definingBlock()!!.name=="main")
entrypointInitialization() entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) { if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
@ -484,8 +480,8 @@ internal class ProgramAndVarsGen(
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR } val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
if(svar?.onetimeInitializationStringValue!=null) if(svar.onetimeInitializationStringValue!=null)
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!)) result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
} }
return result return result
@ -496,8 +492,8 @@ internal class ProgramAndVarsGen(
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes } val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
if(svar?.onetimeInitializationArrayValue!=null) if(svar.onetimeInitializationArrayValue!=null)
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!)) result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
} }
return result return result
@ -547,11 +543,6 @@ internal class ProgramAndVarsGen(
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?") DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?") DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}") DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
in SplitWordArrayTypes -> {
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
in ArrayDatatypes -> { in ArrayDatatypes -> {
val numbytes = compTarget.memorySize(variable.dt, variable.length!!) val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes") asmgen.out("${variable.name}\t.fill $numbytes")
@ -636,18 +627,6 @@ internal class ProgramAndVarsGen(
asmgen.out(" .sint " + chunk.joinToString()) asmgen.out(" .sint " + chunk.joinToString())
} }
} }
DataType.ARRAY_UW_SPLIT -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_W_SPLIT -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map { val floatFills = array.map {
@ -707,7 +686,7 @@ internal class ProgramAndVarsGen(
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0') "$"+number.toString(16).padStart(2, '0')
} }
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map { DataType.ARRAY_UW -> array.map {
if(it.number!=null) { if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0') "$" + it.number!!.toInt().toString(16).padStart(4, '0')
} }
@ -739,11 +718,11 @@ internal class ProgramAndVarsGen(
else else
"-$$hexnum" "-$$hexnum"
} }
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map { DataType.ARRAY_UW -> array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0') "$" + number.toString(16).padStart(4, '0')
} }
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map { DataType.ARRAY_W -> array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0') val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0) if(number>=0)

View File

@ -115,7 +115,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
left is PtIdentifier && left.name==scopedName left is PtIdentifier && left.name==scopedName
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords left is PtArrayIndexer && left isSameAs array!!
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
left isSameAs memory!! left isSameAs memory!!
@ -178,8 +178,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
} }
is PtFunctionCall -> { is PtFunctionCall -> {
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}") val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol.astNode as IPtSubroutine val sub = symbol!!.astNode as IPtSubroutine
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
?: throw AssemblyError("can't translate zero return values in assignment") ?: throw AssemblyError("can't translate zero return values in assignment")

View File

@ -45,12 +45,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val variable = assign.source.asmVarname val variable = assign.source.asmVarname
when (assign.target.datatype) { when (assign.target.datatype) {
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable) DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype) DataType.WORD -> assignVariableWord(assign.target, variable)
DataType.UWORD -> { DataType.UWORD -> {
if(assign.source.datatype in PassByReferenceDatatypes) if(assign.source.datatype in PassByReferenceDatatypes)
assignAddressOf(assign.target, variable) assignAddressOf(assign.target, variable)
else else
assignVariableWord(assign.target, variable, assign.source.datatype) assignVariableWord(assign.target, variable)
} }
DataType.FLOAT -> assignVariableFloat(assign.target, variable) DataType.FLOAT -> assignVariableFloat(assign.target, variable)
DataType.STR -> assignVariableString(assign.target, variable) DataType.STR -> assignVariableString(assign.target, variable)
@ -80,20 +80,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
val constIndex = value.index.asConstInteger() val constIndex = value.index.asConstInteger()
if(value.splitWords) {
require(elementDt in WordDatatypes)
if(constIndex!=null) {
asmgen.out(" lda ${arrayVarName}_lsb+$constIndex | ldy ${arrayVarName}_msb+$constIndex")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
} else {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.out(" lda ${arrayVarName}_lsb,y | pha | lda ${arrayVarName}_msb,y | tay | pla")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return
}
if (constIndex!=null) { if (constIndex!=null) {
// constant array index value // constant array index value
val indexValue = constIndex * program.memsizer.memorySize(elementDt) val indexValue = constIndex * program.memsizer.memorySize(elementDt)
@ -158,6 +144,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByte(assign.target, null, value.address as PtIdentifier) assignMemoryByte(assign.target, null, value.address as PtIdentifier)
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val addrExpr = value.address as PtBinaryExpression val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
assignRegisterByte(assign.target, CpuRegister.A, false) assignRegisterByte(assign.target, CpuRegister.A, false)
@ -288,86 +275,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
is PtPrefix -> { is PtPrefix -> {
if(assign.target.array==null) { if(assign.target.array==null) {
if(assign.source.datatype==assign.target.datatype) { // First assign the value to the target then apply the operator in place on the target.
if(assign.source.datatype in IntegerDatatypes) { // This saves a temporary variable
val signed = assign.source.datatype in SignedDatatypes translateNormalAssignment(
if(assign.source.datatype in ByteDatatypes) { AsmAssignment(
assignExpressionToRegister(value.value, RegisterOrPair.A, signed) AsmAssignSource.fromAstSource(value.value, program, asmgen),
when(value.operator) { assign.target, program.memsizer, assign.position
"+" -> {} ), scope
"-" -> { )
if(asmgen.isTargetCpu(CpuType.CPU65c02)) when (value.operator) {
asmgen.out(" eor #255 | ina") "+" -> {}
else "-" -> inplaceNegate(assign, true, scope)
asmgen.out(" eor #255 | clc | adc #1") "~" -> inplaceInvert(assign, scope)
} "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
"~" -> asmgen.out(" eor #255") else -> throw AssemblyError("invalid prefix operator")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
assignRegisterByte(assign.target, CpuRegister.A, signed)
} else {
assignExpressionToRegister(value.value, RegisterOrPair.AY, signed)
when(value.operator) {
"+" -> {}
"-" -> {
asmgen.out("""
sec
eor #255
adc #0
pha
tya
eor #255
adc #0
tay
pla""")
}
"~" -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
} else {
// First assign the value to the target then apply the operator in place on the target.
// This saves a temporary variable
translateNormalAssignment(
AsmAssignment(
AsmAssignSource.fromAstSource(value.value, program, asmgen),
assign.target, program.memsizer, assign.position
), scope
)
when (value.operator) {
"+" -> {}
"-" -> inplaceNegate(assign, true, scope)
"~" -> inplaceInvert(assign, scope)
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
}
} else {
// use a temporary variable
val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
assignExpressionToVariable(value.value, tempvar, value.type)
when (value.operator) {
"+" -> {}
"-", "~" -> {
val assignTempvar = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar),
program.memsizer, assign.position)
if(value.operator=="-")
inplaceNegate(assignTempvar, true, scope)
else
inplaceInvert(assignTempvar, scope)
}
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
if(value.type in ByteDatatypes)
assignVariableByte(assign.target, tempvar)
else
assignVariableWord(assign.target, tempvar, value.type)
} }
} else { } else {
assignPrefixedExpressionToArrayElt(assign, scope) assignPrefixedExpressionToArrayElt(assign, scope)
@ -378,6 +299,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignRegisterByte(assign.target, CpuRegister.A, false) assignRegisterByte(assign.target, CpuRegister.A, false)
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
if(!attemptAssignOptimizedBinexpr(value, assign)) { if(!attemptAssignOptimizedBinexpr(value, assign)) {
// All remaining binary expressions just evaluate via the stack for now. // All remaining binary expressions just evaluate via the stack for now.
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here, // (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
@ -404,7 +326,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assignToTempvar, scope) asmgen.translateNormalAssignment(assignToTempvar, scope)
when(assign.target.datatype) { when(assign.target.datatype) {
in ByteDatatypes -> assignVariableByte(assign.target, tempvar) in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype) in WordDatatypes -> assignVariableWord(assign.target, tempvar)
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar) DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
else -> throw AssemblyError("weird dt") else -> throw AssemblyError("weird dt")
} }
@ -423,6 +345,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
require(!asmgen.options.useNewExprCode)
if(expr.operator in ComparisonOperators) { if(expr.operator in ComparisonOperators) {
if(expr.right.asConstInteger() == 0) { if(expr.right.asConstInteger() == 0) {
if(expr.operator == "==" || expr.operator=="!=") { if(expr.operator == "==" || expr.operator=="!=") {
@ -811,7 +734,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
in WordDatatypes -> { in WordDatatypes -> {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
asmgen.out(" jsr math.multiply_words") asmgen.out("""
jsr math.multiply_words
lda math.multiply_words.result
ldy math.multiply_words.result+1""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true return true
} }
@ -829,14 +755,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true return true
} }
in WordDatatypes -> { in WordDatatypes -> {
if (value in asmgen.optimizedWordMultiplications) { assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type in SignedDatatypes)
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type in SignedDatatypes) if (value in asmgen.optimizedWordMultiplications)
asmgen.out(" jsr math.mul_word_${value}") asmgen.out(" jsr math.mul_word_${value}")
} else
else { asmgen.out("""
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") sta P8ZP_SCRATCH_W1
asmgen.out(" jsr math.multiply_words") sty P8ZP_SCRATCH_W1+1
} lda #<$value
ldy #>$value
jsr math.multiply_words
lda math.multiply_words.result
ldy math.multiply_words.result+1""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true return true
} }
@ -893,7 +823,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
asmgen.out(" jsr math.divmod_uw_asm") asmgen.out(" jsr math.divmod_uw_asm")
assignVariableWord(assign.target, "P8ZP_SCRATCH_W2", DataType.UWORD) assignVariableWord(assign.target, "P8ZP_SCRATCH_W2")
return true return true
} }
else -> return false else -> return false
@ -1211,6 +1141,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
require(!asmgen.options.useNewExprCode)
when (expr.operator) { when (expr.operator) {
"==" -> { "==" -> {
when(val dt = expr.left.type) { when(val dt = expr.left.type) {
@ -1390,6 +1321,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier) assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val addrExpr = value.address as PtBinaryExpression val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
asmgen.out(" ldy #0") asmgen.out(" ldy #0")
@ -1919,26 +1851,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
} }
in WordDatatypes -> { in WordDatatypes -> {
if(target.array!!.splitWords) asmgen.out("""
asmgen.out(""" inx
inx lda P8ESTACK_LO,x
lda P8ESTACK_LO,x sta ${target.asmVarname}+$scaledIdx
sta ${target.asmVarname}_lsb+$scaledIdx lda P8ESTACK_HI,x
lda P8ESTACK_HI,x sta ${target.asmVarname}+$scaledIdx+1
sta ${target.asmVarname}_msb+$scaledIdx""") """)
else
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta ${target.asmVarname}+$scaledIdx
lda P8ESTACK_HI,x
sta ${target.asmVarname}+$scaledIdx+1""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
lda #<(${target.asmVarname}+$scaledIdx) lda #<(${target.asmVarname}+$scaledIdx)
ldy #>(${target.asmVarname}+$scaledIdx) ldy #>(${target.asmVarname}+$scaledIdx)
jsr floats.pop_float""") jsr floats.pop_float
""")
} }
else -> throw AssemblyError("weird target variable type ${target.datatype}") else -> throw AssemblyError("weird target variable type ${target.datatype}")
} }
@ -1952,8 +1878,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y") asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y")
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
if(target.array.splitWords)
TODO("assign into split words ${target.position}")
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
asmgen.out(""" asmgen.out("""
inx inx
@ -2115,29 +2039,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String, sourceDt: DataType) { private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) {
require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE)
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
if(sourceDt==DataType.UBYTE) { asmgen.out("""
asmgen.out(" lda $sourceName | sta ${target.asmVarname}") lda $sourceName
if(asmgen.isTargetCpu(CpuType.CPU65c02)) ldy $sourceName+1
asmgen.out(" stz ${target.asmVarname}") sta ${target.asmVarname}
else sty ${target.asmVarname}+1
asmgen.out(" lda #0 | sta ${target.asmVarname}") """)
}
else
asmgen.out("""
lda $sourceName
ldy $sourceName+1
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast") throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if(sourceDt==DataType.UBYTE) TODO("assign byte to word array")
target.array!! target.array!!
if(target.constArrayIndexValue!=null) { if(target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt() val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
@ -2146,18 +2061,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
} }
in WordDatatypes -> { in WordDatatypes -> {
if(target.array.splitWords) asmgen.out("""
asmgen.out(""" lda $sourceName
lda $sourceName sta ${target.asmVarname}+$scaledIdx
sta ${target.asmVarname}_lsb+${target.constArrayIndexValue} lda $sourceName+1
lda $sourceName+1 sta ${target.asmVarname}+$scaledIdx+1
sta ${target.asmVarname}_msb+${target.constArrayIndexValue}""") """)
else
asmgen.out("""
lda $sourceName
sta ${target.asmVarname}+$scaledIdx
lda $sourceName+1
sta ${target.asmVarname}+$scaledIdx+1""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
@ -2167,7 +2076,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
lda #<(${target.asmVarname}+$scaledIdx) lda #<(${target.asmVarname}+$scaledIdx)
ldy #>(${target.asmVarname}+$scaledIdx) ldy #>(${target.asmVarname}+$scaledIdx)
jsr floats.copy_float""") jsr floats.copy_float
""")
} }
else -> throw AssemblyError("weird target variable type ${target.datatype}") else -> throw AssemblyError("weird target variable type ${target.datatype}")
} }
@ -2181,18 +2091,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
if(target.array.splitWords) asmgen.out("""
asmgen.out(""" lda $sourceName
lda $sourceName sta ${target.asmVarname},y
sta ${target.asmVarname}_lsb,y lda $sourceName+1
lda $sourceName+1 sta ${target.asmVarname}+1,y
sta ${target.asmVarname}_msb,y""") """)
else
asmgen.out("""
lda $sourceName
sta ${target.asmVarname},y
lda $sourceName+1
sta ${target.asmVarname}+1,y""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A) asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
@ -2213,53 +2117,29 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
if(sourceDt==DataType.UBYTE) { when(target.register!!) {
when(target.register!!) { RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName")
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName") RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName") RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName") in Cx16VirtualRegisters -> {
in Cx16VirtualRegisters -> { asmgen.out(
asmgen.out(" lda $sourceName | sta cx16.${target.register.toString().lowercase()}") """
if(asmgen.isTargetCpu(CpuType.CPU65c02)) lda $sourceName
asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1") sta cx16.${target.register.toString().lowercase()}
else lda $sourceName+1
asmgen.out(" lda #0 | sta cx16.${target.register.toString().lowercase()}+1") sta cx16.${target.register.toString().lowercase()}+1
} """)
else -> throw AssemblyError("can't load word in a single 8-bit register")
}
} else {
when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName")
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
in Cx16VirtualRegisters -> {
asmgen.out(
"""
lda $sourceName
sta cx16.${target.register.toString().lowercase()}
lda $sourceName+1
sta cx16.${target.register.toString().lowercase()}+1
""")
}
else -> throw AssemblyError("can't load word in a single 8-bit register")
} }
else -> throw AssemblyError("can't load word in a single 8-bit register")
} }
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
if(sourceDt==DataType.UBYTE) { asmgen.out("""
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x") lda $sourceName
if(asmgen.isTargetCpu(CpuType.CPU65c02)) sta P8ESTACK_LO,x
asmgen.out(" stz P8ESTACK_HI,x") lda $sourceName+1
else sta P8ESTACK_HI,x
asmgen.out(" lda #0 | sta P8ESTACK_HI,x") dex""")
}
else
asmgen.out("""
lda $sourceName
sta P8ESTACK_LO,x
lda $sourceName+1
sta P8ESTACK_HI,x
dex""")
} }
} }
} }
@ -2415,14 +2295,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
return return
} }
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) { if (target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt() val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
} }
else { else {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y)
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y") asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
} }
} }
@ -2470,8 +2348,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
""") """)
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if(wordtarget.array!!.splitWords)
TODO("assign byte into split words ${wordtarget.position}")
if (wordtarget.constArrayIndexValue!=null) { if (wordtarget.constArrayIndexValue!=null) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
asmgen.out(" lda $sourceName") asmgen.out(" lda $sourceName")
@ -2480,7 +2356,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
else { else {
asmgen.saveRegisterLocal(CpuRegister.X, wordtarget.scope!!) asmgen.saveRegisterLocal(CpuRegister.X, wordtarget.scope!!)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.X)
asmgen.out(" lda $sourceName") asmgen.out(" lda $sourceName")
asmgen.signExtendAYlsb(DataType.BYTE) asmgen.signExtendAYlsb(DataType.BYTE)
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x") asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
@ -2550,41 +2426,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1") asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if(wordtarget.array!!.splitWords) { if (wordtarget.constArrayIndexValue!=null) {
if (wordtarget.constArrayIndexValue!=null) { val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
val scaledIdx = wordtarget.constArrayIndexValue!! asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}_lsb+$scaledIdx") if(asmgen.isTargetCpu(CpuType.CPU65c02))
if(asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
asmgen.out(" stz ${wordtarget.asmVarname}_msb+$scaledIdx") else
else asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}_msb+$scaledIdx") }
} else {
else { asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y) asmgen.out("""
asmgen.out(""" lda $sourceName
lda $sourceName sta ${wordtarget.asmVarname},y
sta ${wordtarget.asmVarname}_lsb,y iny
lda #0 lda #0
sta ${wordtarget.asmVarname}_msb,y""") sta ${wordtarget.asmVarname},y""")
}
} else {
if (wordtarget.constArrayIndexValue!=null) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
asmgen.out("""
lda $sourceName
sta ${wordtarget.asmVarname},y
iny
lda #0
sta ${wordtarget.asmVarname},y""")
}
} }
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
@ -2841,95 +2698,47 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if(target.array!!.splitWords) { if (target.constArrayIndexValue!=null) {
// assign to split lsb/msb word array val idx = target.constArrayIndexValue!! * 2u
if (target.constArrayIndexValue!=null) { when (regs) {
val idx = target.constArrayIndexValue!! RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
when (regs) { RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}_lsb+$idx | stx ${target.asmVarname}_msb+$idx") RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}_lsb+$idx | sty ${target.asmVarname}_msb+$idx") in Cx16VirtualRegisters -> {
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}_lsb+$idx | sty ${target.asmVarname}_msb+$idx")
in Cx16VirtualRegisters -> {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.out("""
lda $srcReg
sta ${target.asmVarname}_lsb+$idx
lda $srcReg+1
sta ${target.asmVarname}_msb+$idx""")
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
}
}
else {
if (regs !in Cx16VirtualRegisters) {
when (regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out("""
pla
sta ${target.asmVarname}_msb,y
pla
sta ${target.asmVarname}_lsb,y""")
} else {
val srcReg = asmgen.asmSymbolName(regs) val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out(""" asmgen.out("""
lda $srcReg lda $srcReg
sta ${target.asmVarname}_lsb,y sta ${target.asmVarname}+$idx
lda $srcReg+1 lda $srcReg+1
sta ${target.asmVarname}_msb,y""") sta ${target.asmVarname}+$idx+1""")
} }
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
} else { }
// assign to normal word array else {
if (target.constArrayIndexValue!=null) { if (regs !in Cx16VirtualRegisters) {
val idx = target.constArrayIndexValue!! * 2u
when (regs) { when (regs) {
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1") RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1") RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1") RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
in Cx16VirtualRegisters -> { else -> throw AssemblyError("expected reg pair")
val srcReg = asmgen.asmSymbolName(regs)
asmgen.out("""
lda $srcReg
sta ${target.asmVarname}+$idx
lda $srcReg+1
sta ${target.asmVarname}+$idx+1""")
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
}
}
else {
if (regs !in Cx16VirtualRegisters) {
when (regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out("""
iny
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
} else {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out("""
iny
lda $srcReg+1
sta ${target.asmVarname},y
dey
lda $srcReg
sta ${target.asmVarname},y""")
} }
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
} else {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
lda $srcReg+1
sta ${target.asmVarname},y
dey
lda $srcReg
sta ${target.asmVarname},y""")
} }
} }
} }
@ -3024,16 +2833,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
if(target.array.splitWords) asmgen.out("""
asmgen.out(""" lda #0
lda #0 sta ${target.asmVarname},y
sta ${target.asmVarname}_lsb,y sta ${target.asmVarname}+1,y
sta ${target.asmVarname}_msb,y""") """)
else
asmgen.out("""
lda #0
sta ${target.asmVarname},y
sta ${target.asmVarname}+1,y""")
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { when(target.register!!) {
@ -3081,18 +2885,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
if(target.array.splitWords) asmgen.out("""
asmgen.out(""" lda #<${word.toHex()}
lda #<${word.toHex()} sta ${target.asmVarname},y
sta ${target.asmVarname}_lsb,y lda #>${word.toHex()}
lda #>${word.toHex()} sta ${target.asmVarname}+1,y
sta ${target.asmVarname}_msb,y""") """)
else
asmgen.out("""
lda #<${word.toHex()}
sta ${target.asmVarname},y
lda #>${word.toHex()}
sta ${target.asmVarname}+1,y""")
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { when(target.register!!) {
@ -3150,14 +2948,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
return return
} }
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) { if (target.constArrayIndexValue!=null) {
val indexValue = target.constArrayIndexValue!! val indexValue = target.constArrayIndexValue!!
asmgen.out(" stz ${target.asmVarname}+$indexValue") asmgen.out(" stz ${target.asmVarname}+$indexValue")
} }
else { else {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda #0 | sta ${target.asmVarname},y") asmgen.out(" lda #0 | sta ${target.asmVarname},y")
} }
} }
@ -3208,14 +3004,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
return return
} }
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) { if (target.constArrayIndexValue!=null) {
val indexValue = target.constArrayIndexValue!! val indexValue = target.constArrayIndexValue!!
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue") asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
} }
else { else {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y") asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y")
} }
} }
@ -3485,13 +3279,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda ${address.toHex()}") RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda ${address.toHex()}")
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda ${address.toHex()}") RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda ${address.toHex()}")
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}") RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}")
in Cx16VirtualRegisters -> {
asmgen.out(" lda ${address.toHex()} | sta cx16.${wordtarget.register.toString().lowercase()}")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.${wordtarget.register.toString().lowercase()}+1")
else
asmgen.out(" lda #0 | sta cx16.${wordtarget.register.toString().lowercase()}+1")
}
else -> throw AssemblyError("word regs can only be pair") else -> throw AssemblyError("word regs can only be pair")
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
@ -3524,13 +3311,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.AX -> asmgen.out(" ldx #0") RegisterOrPair.AX -> asmgen.out(" ldx #0")
RegisterOrPair.AY -> asmgen.out(" ldy #0") RegisterOrPair.AY -> asmgen.out(" ldy #0")
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0") RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
in Cx16VirtualRegisters -> {
asmgen.out(" sta cx16.${wordtarget.register.toString().lowercase()}")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.${wordtarget.register.toString().lowercase()}+1")
else
asmgen.out(" lda #0 | sta cx16.${wordtarget.register.toString().lowercase()}+1")
}
else -> throw AssemblyError("word regs can only be pair") else -> throw AssemblyError("word regs can only be pair")
} }
} }
@ -3575,6 +3355,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.storeAIntoPointerVar(addressExpr) asmgen.storeAIntoPointerVar(addressExpr)
} }
addressExpr is PtBinaryExpression -> { addressExpr is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true)) if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
storeViaExprEval() storeViaExprEval()
} }
@ -3698,18 +3479,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.BYTE -> { DataType.BYTE -> {
when (target.kind) { when (target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out("""
asmgen.out(""" lda #0
lda ${target.asmVarname} sec
eor #255 sbc ${target.asmVarname}
ina sta ${target.asmVarname}""")
sta ${target.asmVarname}""")
else
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}""")
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { when(target.register!!) {
@ -3718,6 +3492,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" eor #255 | ina") asmgen.out(" eor #255 | ina")
else else
asmgen.out(" eor #255 | clc | adc #1") asmgen.out(" eor #255 | clc | adc #1")
} }
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx") RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx")
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny") RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny")

View File

@ -93,7 +93,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.expression) inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.expression)
} }
else { else {
@ -177,6 +178,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression)
} else { } else {
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!)
@ -193,10 +195,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
val indexVar = target.array.index as? PtIdentifier val indexVar = target.array.index as? PtIdentifier
when { when {
indexNum!=null -> { indexNum!=null -> {
val targetVarName = if(target.array.splitWords) val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
"${target.asmVarname} + ${indexNum.number.toInt()}"
else
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
when (target.datatype) { when (target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
when(value.kind) { when(value.kind) {
@ -258,10 +257,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
indexVar!=null -> { indexVar!=null -> {
when (target.datatype) { when (target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
if(value.kind==SourceStorageKind.EXPRESSION
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_B1") asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_B1")
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!) asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!)
@ -273,6 +268,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression)
} else { } else {
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!)
@ -284,19 +281,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda P8ZP_SCRATCH_B1 | sta ${target.array.variable.name},y") asmgen.out(" lda P8ZP_SCRATCH_B1 | sta ${target.array.variable.name},y")
} }
in WordDatatypes -> { in WordDatatypes -> {
if(value.kind==SourceStorageKind.EXPRESSION
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
if(target.array.splitWords) { asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
asmgen.out(" lda ${target.array.variable.name}_lsb,y | sta P8ZP_SCRATCH_W1") asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda ${target.array.variable.name}_msb,y | sta P8ZP_SCRATCH_W1+1") asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!)
} else {
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
asmgen.out(" lda ${target.array.variable.name}+1,y | sta P8ZP_SCRATCH_W1+1")
}
asmgen.saveRegisterStack(CpuRegister.Y, false)
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_word_litval_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt()) SourceStorageKind.LITERALNUMBER -> inplaceModification_word_litval_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplaceModification_word_variable_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype) SourceStorageKind.VARIABLE -> inplaceModification_word_variable_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype)
@ -305,6 +293,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression) inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression)
} else { } else {
inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!) inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!)
@ -312,14 +302,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
else -> throw AssemblyError("weird source type ${value.kind}") else -> throw AssemblyError("weird source type ${value.kind}")
} }
asmgen.restoreRegisterStack(CpuRegister.Y, false) asmgen.restoreRegisterLocal(CpuRegister.Y)
if(target.array.splitWords) { asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name}_lsb,y") asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}_msb,y")
} else {
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}+1,y")
}
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
@ -600,9 +585,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" clc | adc $name | sta $name") asmgen.out(" clc | adc $name | sta $name")
} }
"-" -> { "-" -> {
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG" asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", dt)
asmgen.assignExpressionToVariable(value, tmpByte, dt) asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_B1 | sta $name")
asmgen.out(" lda $name | sec | sbc $tmpByte | sta $name")
} }
"*" -> { "*" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
@ -1079,12 +1063,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
"-" -> { "-" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.out(""" asmgen.out("""
sta $tmpByte sta P8ZP_SCRATCH_B1
lda $name lda $name
sec sec
sbc $tmpByte sbc P8ZP_SCRATCH_B1
sta $name""") sta $name""")
} }
"|" -> { "|" -> {
@ -1119,12 +1102,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
"-" -> { "-" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.out(""" asmgen.out("""
sta $tmpByte sta P8ZP_SCRATCH_B1
lda $name lda $name
sec sec
sbc $tmpByte sbc P8ZP_SCRATCH_B1
sta $name sta $name
bcc + bcc +
dec $name+1 dec $name+1
@ -1221,8 +1203,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda #<$value lda #<$value
ldy #>$value ldy #>$value
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
sty $name+1""") lda math.multiply_words.result+1
sta $name+1""")
} }
} }
"/" -> { "/" -> {
@ -1680,8 +1664,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $name lda $name
ldy $name+1 ldy $name+1
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
sty $name+1""") lda math.multiply_words.result+1
sta $name+1""")
} }
"/" -> { "/" -> {
if(dt==DataType.UWORD) { if(dt==DataType.UWORD) {
@ -1818,8 +1804,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $name lda $name
ldy $name+1 ldy $name+1
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
sty $name+1""") lda math.multiply_words.result+1
sta $name+1
""")
} }
"/" -> { "/" -> {
if(dt==DataType.WORD) { if(dt==DataType.WORD) {
@ -1832,7 +1821,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy $otherName+1 ldy $otherName+1
jsr math.divmod_w_asm jsr math.divmod_w_asm
sta $name sta $name
sty $name+1""") sty $name+1
""")
} }
else { else {
asmgen.out(""" asmgen.out("""
@ -1844,7 +1834,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy $otherName+1 ldy $otherName+1
jsr math.divmod_uw_asm jsr math.divmod_uw_asm
sta $name sta $name
sty $name+1""") sty $name+1
""")
} }
} }
"%" -> { "%" -> {
@ -2000,6 +1991,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) { private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) {
// this should be the last resort for code generation for this, // this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow). // because the value is evaluated onto the eval stack (=slow).
fun multiplyVarByWordInAY() { fun multiplyVarByWordInAY() {
asmgen.out(""" asmgen.out("""
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -2007,8 +2000,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $name lda $name
ldy $name+1 ldy $name+1
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
sty $name+1 lda math.multiply_words.result+1
sta $name+1
""") """)
} }
@ -2209,10 +2204,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1") asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1")
} }
"-" -> { "-" -> {
val tmpWord = if(name!="P8ZP_SCRATCH_W1") "P8ZP_SCRATCH_W1" else "P8ZP_SCRATCH_W2" asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt)
asmgen.assignExpressionToVariable(value, tmpWord, valueDt) asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_W1 | sta $name | lda $name+1 | sbc P8ZP_SCRATCH_W1+1 | sta $name+1")
asmgen.out(" lda $name | sec | sbc $tmpWord | sta $name | lda $name+1 | sbc $tmpWord+1 | sta $name+1")
// TODO wrong signed word subtraction code??? ^^^
} }
"*" -> { "*" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)

View File

@ -45,10 +45,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text) warnings.add(text)
} }
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
override fun noErrors(): Boolean = errors.isEmpty() override fun noErrors(): Boolean = errors.isEmpty()
override fun report() { override fun report() {

View File

@ -1,9 +1,7 @@
package prog8tests.codegencpu6502 package prog8tests.codegencpu6502
import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
@ -42,9 +40,9 @@ class TestCodegen: FunSpec({
// xx += cx16.r0 // xx += cx16.r0
// } // }
//} //}
val codegen = AsmGen6502(prefixSymbols = false) val codegen = AsmGen6502()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY)) sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@ -94,7 +92,7 @@ class TestCodegen: FunSpec({
program.add(block) program.add(block)
// define the "cx16.r0" virtual register // define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY)) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block) program.add(cx16block)
@ -109,19 +107,9 @@ class TestCodegen: FunSpec({
test("64tass assembler available? - if this fails you need to install 64tass in the path") { test("64tass assembler available? - if this fails you need to install 64tass in the path") {
val command = mutableListOf("64tass", "--version") val command = mutableListOf("64tass", "--version")
shouldNotThrowAny { shouldNotThrowAny {
val proc = ProcessBuilder(command).start() val proc = ProcessBuilder(command).inheritIO().start()
val output = String(proc.inputStream.readBytes())
val result = proc.waitFor() val result = proc.waitFor()
result.shouldBe(0) result.shouldBe(0)
val (_, version) = output.split('V')
val (major, minor, _) = version.split('.')
val majorNum = major.toInt()
val minorNum = minor.toInt()
withClue("64tass version should be 1.58 or newer") {
majorNum shouldBeGreaterThanOrEqual 1
if (majorNum == 1)
minorNum shouldBeGreaterThanOrEqual 58
}
} }
} }
}) })

View File

@ -29,7 +29,7 @@ dependencies {
implementation project(':codeGenIntermediate') implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
} }

View File

@ -26,15 +26,6 @@ class ExperiCodeGen: ICodeGeneratorBackend {
IRFileWriter(irProgram, null).write() IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **") println("** experimental codegen stub: no assembly generated **")
return EmptyProgram return null
} }
} }
private object EmptyProgram : IAssemblyProgram {
override val name = "<Empty Program>"
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
println("** nothing assembled **")
return true
}
}

View File

@ -28,9 +28,9 @@ dependencies {
implementation project(':intermediate') implementation project(':intermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
} }
sourceSets { sourceSets {

View File

@ -104,10 +104,27 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
value.add(origAssign.value) value.add(origAssign.value)
} else { } else {
require(origAssign.operator.endsWith('=')) require(origAssign.operator.endsWith('='))
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position) if(codeGen.options.useNewExprCode) {
val left: PtExpression = origAssign.target.children.single() as PtExpression // X += Y -> temp = X, temp += Y, X = temp
value.add(left) val tempvar = codeGen.getReusableTempvar(origAssign.definingSub()!!, origAssign.target.type)
value.add(origAssign.value) val assign = PtAssignment(origAssign.position)
val target = PtAssignTarget(origAssign.position)
target.add(tempvar)
assign.add(target)
assign.add(origAssign.target.children.single())
val augAssign = PtAugmentedAssign(origAssign.operator, origAssign.position)
augAssign.add(target)
augAssign.add(origAssign.value)
val assignBack = PtAssignment(origAssign.position)
assignBack.add(origAssign.target)
assignBack.add(tempvar)
return translateRegularAssign(assign) + translate(augAssign) + translateRegularAssign(assignBack)
} else {
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
value.add(left)
value.add(origAssign.value)
}
} }
val normalAssign = PtAssignment(origAssign.position) val normalAssign = PtAssignment(origAssign.position)
normalAssign.add(origAssign.target) normalAssign.add(origAssign.target)
@ -167,18 +184,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else false } else false
if (assignment.value is PtMachineRegister) { if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord) { if(extendByteToWord)
valueRegister = codeGen.registers.nextFree() addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister, reg2=(assignment.value as PtMachineRegister).register), null)
}
} else { } else {
val tr = expressionEval.translateExpression(assignment.value) val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1) addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) { if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null) addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1 = valueRegister), null)
} }
} }
} }
@ -221,71 +235,36 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} }
val fixedIndex = constIntValue(targetArray.index) val fixedIndex = constIntValue(targetArray.index)
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
if(zero) { if(zero) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also { val offset = fixedIndex*itemsize
if(targetArray.splitWords) { val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+$offset") }
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code result += code
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable) }
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
} }
} else { } else {
if(targetDt== IRDataType.FLOAT) { if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
}
result += chunk result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code result += code
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
} }
} else { } else {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also { val offset = fixedIndex*itemsize
if(targetArray.splitWords) { val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code result += code
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
} }
} }
} }
@ -322,21 +301,28 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} }
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> { private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also returned. // returns the code to load the Index into the register, which is also return\ed.
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(itemsize==1 || array.splitWords) { if(itemsize==1) {
val tr = expressionEval.translateExpression(array.index) val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg) return Pair(result, tr.resultReg)
} }
val mult: PtExpression if(codeGen.options.useNewExprCode) {
mult = PtBinaryExpression("*", DataType.UBYTE, array.position) val tr = expressionEval.translateExpression(array.index)
mult.children += array.index result += tr.chunks
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position) addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, immediate = itemsize), null)
val tr = expressionEval.translateExpression(mult) return Pair(result, tr.resultReg)
addToResult(result, tr, tr.resultReg, -1) } else {
return Pair(result, tr.resultReg) val mult: PtExpression
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
}
} }
} }

View File

@ -1,10 +1,9 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.SignedDatatypes
import prog8.code.core.SplitWordArrayTypes
import prog8.intermediate.* import prog8.intermediate.*
@ -14,12 +13,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return when(call.name) { return when(call.name) {
"any" -> funcAny(call) "any" -> funcAny(call)
"all" -> funcAll(call) "all" -> funcAll(call)
"abs__byte", "abs__word", "abs__float" -> funcAbs(call) "abs" -> funcAbs(call)
"cmp" -> funcCmp(call) "cmp" -> funcCmp(call)
"sgn" -> funcSgn(call) "sgn" -> funcSgn(call)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call) "sqrt16" -> funcSqrt16(call)
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE) "divmod" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD) "divmodw" -> funcDivmod(call, IRDataType.WORD)
"pop" -> funcPop(call) "pop" -> funcPop(call)
"popw" -> funcPopw(call) "popw" -> funcPopw(call)
"push" -> funcPush(call) "push" -> funcPush(call)
@ -28,7 +27,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"rsavex", "rsavex",
"rrestore", "rrestore",
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore "rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> funcCallfar(call) "callfar" -> throw AssemblyError("callfar() is for cx16 target only")
"msb" -> funcMsb(call) "msb" -> funcMsb(call)
"lsb" -> funcLsb(call) "lsb" -> funcLsb(call)
"memory" -> funcMemory(call) "memory" -> funcMemory(call)
@ -38,9 +37,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"pokew" -> funcPokeW(call) "pokew" -> funcPokeW(call)
"pokemon" -> ExpressionCodeResult.EMPTY // easter egg function "pokemon" -> ExpressionCodeResult.EMPTY // easter egg function
"mkword" -> funcMkword(call) "mkword" -> funcMkword(call)
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
"sort" -> funcSort(call) "sort" -> funcSort(call)
"reverse" -> funcReverse(call) "reverse" -> funcReverse(call)
"rol" -> funcRolRor(Opcode.ROXL, call) "rol" -> funcRolRor(Opcode.ROXL, call)
@ -52,18 +48,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
} }
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1])
val argumentwordTr = exprGen.translateExpression(call.args[2])
addToResult(result, bankTr, bankTr.resultReg, -1)
addToResult(result, addressTr, addressTr.resultReg, -1)
addToResult(result, argumentwordTr, argumentwordTr.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.CALLFAR, listOf(IRDataType.BYTE to bankTr.resultReg, IRDataType.WORD to addressTr.resultReg, IRDataType.WORD to argumentwordTr.resultReg), IRDataType.WORD to argumentwordTr.resultReg)
return ExpressionCodeResult(result, IRDataType.WORD, argumentwordTr.resultReg, -1)
}
private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult { private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val number = call.args[0] val number = call.args[0]
@ -118,9 +102,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name) val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val syscall = val syscall =
when (arrayName.type) { when (array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ANY_BYTE DataType.ARRAY_B -> IMSyscall.ANY_BYTE
DataType.ARRAY_UW, DataType.ARRAY_UW,
@ -132,16 +116,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name) val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val syscall = val syscall =
when(arrayName.type) { when(array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ALL_BYTE DataType.ARRAY_B -> IMSyscall.ALL_BYTE
DataType.ARRAY_UW, DataType.ARRAY_UW,
@ -153,7 +137,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
@ -167,13 +151,21 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
when (sourceDt) { when (sourceDt) {
DataType.UBYTE -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
DataType.BYTE -> { DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, immediate = 0x80)
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=tr.resultReg)
} }
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
@ -183,18 +175,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, immediate = 0x8000)
it += IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg) it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
} }
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
} }
DataType.FLOAT -> { else -> throw AssemblyError("weird type")
val resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
else -> throw AssemblyError("weird dt")
} }
} }
@ -210,37 +198,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, vmDt, resultReg, -1) return ExpressionCodeResult(result, vmDt, resultReg, -1)
} }
private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcSqrt16(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
val dt = call.args[0].type addToResult(result, tr, tr.resultReg, -1)
when(dt) { val resultReg = codeGen.registers.nextFree()
DataType.UBYTE -> { result += IRCodeChunk(null, null).also {
addToResult(result, tr, tr.resultReg, -1) it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
DataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
DataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg)
val resultFpReg = codeGen.registers.nextFreeFloat()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
}
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
else -> throw AssemblyError("invalid dt for sqrt")
} }
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
} }
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -283,43 +249,41 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name) val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val syscall = val syscall =
when(arrayName.type) { when(array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name) val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val syscall = val syscall =
when(arrayName.type) { when(array.dt) {
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
DataType.ARRAY_B -> IMSyscall.SORT_BYTE DataType.ARRAY_B -> IMSyscall.SORT_BYTE
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
DataType.ARRAY_W -> IMSyscall.SORT_WORD DataType.ARRAY_W -> IMSyscall.SORT_WORD
DataType.STR -> IMSyscall.SORT_UBYTE DataType.STR -> IMSyscall.SORT_UBYTE
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported") DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw IllegalArgumentException("weird type to sort") else -> throw IllegalArgumentException("weird type to sort")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
@ -330,85 +294,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, msbTr, msbTr.resultReg, -1) addToResult(result, msbTr, msbTr.resultReg, -1)
val lsbTr = exprGen.translateExpression(call.args[1]) val lsbTr = exprGen.translateExpression(call.args[1])
addToResult(result, lsbTr, lsbTr.resultReg, -1) addToResult(result, lsbTr, lsbTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultReg, reg2 = lsbTr.resultReg, reg3 = msbTr.resultReg) it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = lsbTr.resultReg, reg2 = msbTr.resultReg)
} }
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1) return ExpressionCodeResult(result, IRDataType.WORD, lsbTr.resultReg, -1)
}
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val type = irType(call.type)
val valueTr = exprGen.translateExpression(call.args[0])
val minimumTr = exprGen.translateExpression(call.args[1])
val maximumTr = exprGen.translateExpression(call.args[2])
result += valueTr.chunks
result += minimumTr.chunks
result += maximumTr.chunks
if(type==IRDataType.FLOAT) {
result += codeGen.makeSyscall(
IMSyscall.CLAMP_FLOAT, listOf(
valueTr.dt to valueTr.resultFpReg,
minimumTr.dt to minimumTr.resultFpReg,
maximumTr.dt to maximumTr.resultFpReg,
), type to valueTr.resultFpReg
)
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
} else {
val syscall = when(call.type) {
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE
DataType.BYTE -> IMSyscall.CLAMP_BYTE
DataType.UWORD -> IMSyscall.CLAMP_UWORD
DataType.WORD -> IMSyscall.CLAMP_WORD
else -> throw AssemblyError("invalid dt")
}
result += codeGen.makeSyscall(syscall, listOf(
valueTr.dt to valueTr.resultReg,
minimumTr.dt to minimumTr.resultReg,
maximumTr.dt to maximumTr.resultReg,
), type to valueTr.resultReg
)
return ExpressionCodeResult(result, type, valueTr.resultReg, -1)
}
}
private fun funcMin(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val type = irType(call.type)
val result = mutableListOf<IRCodeChunkBase>()
val leftTr = exprGen.translateExpression(call.args[0])
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
// right <= left, take right
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
}
result += IRCodeChunk(after, null)
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
}
private fun funcMax(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val type = irType(call.type)
val result = mutableListOf<IRCodeChunkBase>()
val leftTr = exprGen.translateExpression(call.args[0])
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
// right >= left, take right
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
}
result += IRCodeChunk(after, null)
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
} }
private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult {

View File

@ -1,6 +1,7 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.StRomSub import prog8.code.StRomSub
import prog8.code.StStaticVariable
import prog8.code.StSub import prog8.code.StSub
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
@ -44,9 +45,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
is PtIdentifier -> { is PtIdentifier -> {
val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) { if (expr.type in PassByValueDatatypes) {
val vmDt = irType(expr.type)
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat() val resultFpRegister = codeGen.registers.nextFreeFloat()
code += IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = expr.name) code += IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = expr.name)
@ -59,7 +60,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} else { } else {
// for strings and arrays etc., load the *address* of the value instead // for strings and arrays etc., load the *address* of the value instead
val vmDt = if(expr.type==DataType.UNDEFINED) IRDataType.WORD else irType(expr.type)
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = expr.name) code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = expr.name)
ExpressionCodeResult(code, vmDt, resultRegister, -1) ExpressionCodeResult(code, vmDt, resultRegister, -1)
@ -105,7 +105,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(check: PtContainmentCheck): ExpressionCodeResult { private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
when(check.iterable.type) { val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable
when(iterable.dt) {
DataType.STR -> { DataType.STR -> {
val elementTr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
@ -120,8 +121,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val iterableTr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1) addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
val iterableLength = codeGen.symbolTable.getLength(check.iterable.name) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg) result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
} }
@ -131,13 +131,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val iterableTr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1) addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
val iterableLength = codeGen.symbolTable.getLength(check.iterable.name) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg) result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
} }
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported") DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
else -> throw AssemblyError("weird iterable dt ${check.iterable.type} for ${check.iterable.name}") else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}")
} }
} }
@ -161,33 +160,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
var resultRegister = -1 var resultRegister = -1
if(arrayIx.splitWords) {
require(vmDt==IRDataType.WORD)
val arrayLength = codeGen.symbolTable.getLength(arrayIx.variable.name)
resultRegister = codeGen.registers.nextFree()
val finalResultReg = codeGen.registers.nextFree()
if(arrayIx.index is PtNumber) {
val memOffset = (arrayIx.index as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
val tmpRegMsb = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb+$memOffset")
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb+$memOffset")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb)
}
} else {
val tr = translateExpression(arrayIx.index)
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
val tmpRegMsb = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb)
}
}
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
}
var resultFpRegister = -1 var resultFpRegister = -1
if(arrayIx.index is PtNumber) { if(arrayIx.index is PtNumber) {
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString() val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()
@ -275,13 +247,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) { when(cast.value.type) {
DataType.BYTE -> { DataType.BYTE -> {
// byte -> uword: sign extend // byte -> uword: sign extend
actualResultReg2 = codeGen.registers.nextFree() actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null) addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
} }
DataType.UBYTE -> { DataType.UBYTE -> {
// ubyte -> uword: sign extend // ubyte -> uword: sign extend
actualResultReg2 = codeGen.registers.nextFree() actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null) addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
} }
DataType.WORD -> { DataType.WORD -> {
actualResultReg2 = tr.resultReg actualResultReg2 = tr.resultReg
@ -297,13 +269,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) { when(cast.value.type) {
DataType.BYTE -> { DataType.BYTE -> {
// byte -> word: sign extend // byte -> word: sign extend
actualResultReg2 = codeGen.registers.nextFree() actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null) addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
} }
DataType.UBYTE -> { DataType.UBYTE -> {
// byte -> word: sign extend // byte -> word: sign extend
actualResultReg2 = codeGen.registers.nextFree() actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null) addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
} }
DataType.UWORD -> { DataType.UWORD -> {
actualResultReg2 = tr.resultReg actualResultReg2 = tr.resultReg
@ -340,6 +312,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult { private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
require(!codeGen.options.useNewExprCode)
val vmDt = irType(binExpr.left.type) val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes val signed = binExpr.left.type in SignedDatatypes
return when(binExpr.operator) { return when(binExpr.operator) {
@ -455,7 +428,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
greaterEquals: Boolean greaterEquals: Boolean
): ExpressionCodeResult { ): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val cmpResultReg = codeGen.registers.nextFree()
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg) addToResult(result, leftTr, -1, leftTr.resultFpReg)
@ -470,10 +442,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (greaterEquals) Opcode.SGE else Opcode.SGT if (greaterEquals) Opcode.SGE else Opcode.SGT
} }
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultReg, reg2 = resultRegister, reg3 = zeroRegister), null) addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare") throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -485,8 +457,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (greaterEquals) Opcode.SGE else Opcode.SGT if (greaterEquals) Opcode.SGE else Opcode.SGT
} }
addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null) addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
} }
} }
} }
@ -498,7 +470,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
lessEquals: Boolean lessEquals: Boolean
): ExpressionCodeResult { ): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val cmpResultRegister = codeGen.registers.nextFree()
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg) addToResult(result, leftTr, -1, leftTr.resultFpReg)
@ -513,10 +484,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (lessEquals) Opcode.SLE else Opcode.SLT if (lessEquals) Opcode.SLE else Opcode.SLT
} }
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultRegister, reg2 = resultRegister, reg3 = zeroRegister), null) addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare") throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -528,8 +499,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (lessEquals) Opcode.SLE else Opcode.SLT if (lessEquals) Opcode.SLE else Opcode.SLT
} }
addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultRegister, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null) addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
} }
} }
} }
@ -555,7 +526,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += IRCodeChunk(label, null) result += IRCodeChunk(label, null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare") throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else { } else {
return if(constValue(binExpr.right)==0.0) { return if(constValue(binExpr.right)==0.0) {
@ -571,9 +542,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightTr = translateExpression(binExpr.right) val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, rightTr.resultReg, -1) addToResult(result, rightTr, rightTr.resultReg, -1)
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
val resultReg = codeGen.registers.nextFree() addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null) ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
} }
} }
@ -1243,7 +1213,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
): MutableList<IRCodeChunkBase> { ): MutableList<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val valueReg = codeGen.registers.nextFree() val valueReg = codeGen.registers.nextFree()
val cmpResultReg = codeGen.registers.nextFree()
if(operand is PtNumber) { if(operand is PtNumber) {
val numberReg = codeGen.registers.nextFree() val numberReg = codeGen.registers.nextFree()
if (knownAddress != null) { if (knownAddress != null) {
@ -1251,16 +1220,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt()) it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = cmpResultReg, reg2 = valueReg, reg3 = numberReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
} }
} else { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt()) it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = numberReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol)
} }
} }
} else { } else {
@ -1270,15 +1239,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
} }
} else { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol)
} }
} }
} }
@ -1297,7 +1266,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val zeroReg = codeGen.registers.nextFree() val zeroReg = codeGen.registers.nextFree()
if(operand is PtNumber) { if(operand is PtNumber) {
val numberReg = codeGen.registers.nextFreeFloat() val numberReg = codeGen.registers.nextFreeFloat()
val cmpResultReg = codeGen.registers.nextFree()
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
@ -1305,8 +1273,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
} }
} else { } else {
@ -1316,14 +1284,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
} }
} }
} else { } else {
val tr = translateExpression(operand) val tr = translateExpression(operand)
val cmpResultReg = codeGen.registers.nextFree()
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
@ -1331,8 +1298,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
} }
} else { } else {
@ -1341,8 +1308,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
} }
} }

View File

@ -1,6 +1,9 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.* import prog8.code.StMemVar
import prog8.code.StNode
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.intermediate.* import prog8.intermediate.*
@ -55,10 +58,6 @@ class IRCodeGen(
val optimizer = IRPeepholeOptimizer(irProg) val optimizer = IRPeepholeOptimizer(irProg)
optimizer.optimize(options.optimize, errors) optimizer.optimize(options.optimize, errors)
irProg.validate() irProg.validate()
val regOptimizer = IRRegisterOptimizer(irProg)
regOptimizer.optimize()
return irProg return irProg
} }
@ -99,16 +98,14 @@ class IRCodeGen(
// make sure that first chunks in Blocks and Subroutines share the name of the block/sub as label. // make sure that first chunks in Blocks and Subroutines share the name of the block/sub as label.
irProg.blocks.forEach { block -> irProg.blocks.forEach { block ->
if(block.isNotEmpty()) { block.children.firstOrNull { it is IRInlineAsmChunk }?.let { first->
val firstAsm = block.children[0] as? IRInlineAsmChunk first as IRInlineAsmChunk
if(firstAsm!=null) { if(first.label==null) {
if(firstAsm.label==null) { val replacement = IRInlineAsmChunk(block.label, first.assembly, first.isIR, first.next)
val replacement = IRInlineAsmChunk(block.label, firstAsm.assembly, firstAsm.isIR, firstAsm.next) block.children.removeAt(0)
block.children.removeAt(0) block.children.add(0, replacement)
block.children.add(0, replacement) } else if(first.label != block.label) {
} else if(firstAsm.label != block.label) { throw AssemblyError("first chunk in block has label that differs from block name")
throw AssemblyError("first chunk in block has label that differs from block name")
}
} }
} }
@ -142,7 +139,7 @@ class IRCodeGen(
// note: we do still export the memory mapped symbols so a code generator can use those // note: we do still export the memory mapped symbols so a code generator can use those
// for instance when a piece of inlined assembly references them. // for instance when a piece of inlined assembly references them.
val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>() val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>()
irProg.foreachCodeChunk { chunk -> irProg.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }.forEach { chunk ->
chunk.instructions.withIndex().forEach { chunk.instructions.withIndex().forEach {
(idx, instr) -> (idx, instr) ->
val symbolExpr = instr.labelSymbol val symbolExpr = instr.labelSymbol
@ -167,22 +164,16 @@ class IRCodeGen(
replacements.forEach { replacements.forEach {
val old = it.first.instructions[it.second] val old = it.first.instructions[it.second]
val formats = instructionFormats.getValue(old.opcode)
val format = formats.getOrElse(old.type) { throw IllegalArgumentException("type ${old.type} invalid for ${old.opcode}") }
val immediateValue = if(format.immediate) it.third.toInt() else null
val addressValue = if(format.immediate) null else it.third.toInt()
it.first.instructions[it.second] = IRInstruction( it.first.instructions[it.second] = IRInstruction(
old.opcode, old.opcode,
old.type, old.type,
old.reg1, old.reg1,
old.reg2, old.reg2,
old.reg3,
old.fpReg1, old.fpReg1,
old.fpReg2, old.fpReg2,
immediate = immediateValue,
null, null,
address = addressValue, null,
address = it.third.toInt(),
null, null,
null null
) )
@ -400,35 +391,42 @@ class IRCodeGen(
} }
private fun translate(whenStmt: PtWhen): IRCodeChunks { private fun translate(whenStmt: PtWhen): IRCodeChunks {
if(whenStmt.choices.children.isEmpty())
return emptyList()
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val valueDt = irType(whenStmt.value.type) val valueDt = irType(whenStmt.value.type)
val valueTr = expressionEval.translateExpression(whenStmt.value) val valueTr = expressionEval.translateExpression(whenStmt.value)
addToResult(result, valueTr, valueTr.resultReg, -1) addToResult(result, valueTr, valueTr.resultReg, -1)
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
val choices = mutableListOf<Pair<String, PtWhenChoice>>()
val endLabel = createLabelName() val endLabel = createLabelName()
whenStmt.choices.children.forEach { for (choice in choices) {
val choice = it as PtWhenChoice
if(choice.isElse) { if(choice.isElse) {
result += translateNode(choice.statements) result += translateNode(choice.statements)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
} else { } else {
val choiceLabel = createLabelName() val skipLabel = createLabelName()
choices.add(choiceLabel to choice) val values = choice.values.children.map {it as PtNumber}
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value -> if(values.size==1) {
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), null) val chunk = IRCodeChunk(null, null)
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, immediate = values[0].number.toInt(), labelSymbol = skipLabel)
result += chunk
result += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
} else {
val matchLabel = createLabelName()
val chunk = IRCodeChunk(null, null)
for (value in values) {
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = matchLabel)
}
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
result += chunk
result += labelFirstChunk(translateNode(choice.statements), matchLabel)
if(choice.statements.children.last() !is PtReturn)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
} }
result += IRCodeChunk(skipLabel, null)
} }
} }
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
choices.forEach { (label, choice) ->
result += labelFirstChunk(translateNode(choice.statements), label)
val lastStatement = choice.statements.children.last()
if(lastStatement !is PtReturn && lastStatement !is PtJump)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
}
result += IRCodeChunk(endLabel, null) result += IRCodeChunk(endLabel, null)
return result return result
} }
@ -445,63 +443,54 @@ class IRCodeGen(
result += translateForInNonConstantRange(forLoop, loopvar) result += translateForInNonConstantRange(forLoop, loopvar)
} }
is PtIdentifier -> { is PtIdentifier -> {
val iterableVar = symbolTable.lookup(iterable.name) as StStaticVariable
require(forLoop.variable.name == loopvar.scopedName) require(forLoop.variable.name == loopvar.scopedName)
val iterableLength = symbolTable.getLength(iterable.name)
val loopvarSymbol = forLoop.variable.name val loopvarSymbol = forLoop.variable.name
val indexReg = registers.nextFree() val indexReg = registers.nextFree()
val tmpReg = registers.nextFree() val tmpReg = registers.nextFree()
val loopLabel = createLabelName() val loopLabel = createLabelName()
val endLabel = createLabelName() val endLabel = createLabelName()
when (iterable.type) { if(iterableVar.dt==DataType.STR) {
DataType.STR -> { // iterate over a zero-terminated string
// iterate over a zero-terminated string addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null) val chunk = IRCodeChunk(loopLabel, null)
result += IRCodeChunk(loopLabel, null).also { chunk += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = iterable.name)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name) chunk += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=tmpReg, immediate = 0, labelSymbol = endLabel)
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1 = tmpReg, immediate = 0, labelSymbol = endLabel) chunk += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol) result += chunk
} result += translateNode(forLoop.statements)
result += translateNode(forLoop.statements) val jumpChunk = IRCodeChunk(null, null)
val jumpChunk = IRCodeChunk(null, null) jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1 = indexReg) jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel) result += jumpChunk
result += jumpChunk result += IRCodeChunk(endLabel, null)
result += IRCodeChunk(endLabel, null) } else {
} // iterate over array
in SplitWordArrayTypes -> { val elementDt = ArrayToElementTypes.getValue(iterable.type)
// iterate over lsb/msb split word array val elementSize = program.memsizer.memorySize(elementDt)
val elementDt = ArrayToElementTypes.getValue(iterable.type) val lengthBytes = iterableVar.length!! * elementSize
if(elementDt !in WordDatatypes) if(lengthBytes<256) {
throw AssemblyError("weird dt") val chunk = IRCodeChunk(null, null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null) chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0)
result += IRCodeChunk(loopLabel, null).also { result += chunk
val tmpRegLsb = registers.nextFree() val chunk2 = IRCodeChunk(loopLabel, null)
val tmpRegMsb = registers.nextFree() chunk2 += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
val concatReg = registers.nextFree() chunk2 += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_lsb") result += chunk2
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_msb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegLsb, reg3=tmpRegMsb)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
it += IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(iterableLength==256) 0 else iterableLength, labelSymbol = loopLabel)
}
}
else -> {
// iterate over regular array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
val lengthBytes = iterableLength!! * elementSize
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements) result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize) result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(lengthBytes==256) 0 else lengthBytes, labelSymbol = loopLabel), null) addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = lengthBytes, labelSymbol = loopLabel), null)
} else if(lengthBytes==256) {
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
val chunk = IRCodeChunk(loopLabel, null)
chunk += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
chunk += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = 0, labelSymbol = loopLabel), null)
} else {
throw AssemblyError("iterator length should never exceed 256")
} }
} }
} }
@ -547,24 +536,22 @@ class IRCodeGen(
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null) addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel) result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
if(step==1 || step==-1) { result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
// if endvalue == loopvar, stop loop, else iterate addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null) // if endvalue >= index, iterate loop
addInstr(result, IRInstruction(Opcode.BEQR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol = labelAfterFor), null) val branchInstr = if(loopvarDt in SignedDatatypes) {
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step) if(step>0)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = loopLabel), null) IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel)
} else {
// ind/dec index, then:
// ascending: if endvalue >= loopvar, iterate
// descending: if loopvar >= endvalue, iterate
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol = loopvarSymbol), null)
if(step > 0)
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = toTr.resultReg, fromTr.resultReg), null)
else else
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = fromTr.resultReg, toTr.resultReg), null) IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=fromTr.resultReg, reg2=toTr.resultReg, labelSymbol=loopLabel)
addInstr(result, IRInstruction(Opcode.BSTPOS, labelSymbol = loopLabel), null) } else {
if(step>0)
IRInstruction(Opcode.BGER, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel)
else
IRInstruction(Opcode.BGER, loopvarDtIr, reg1=fromTr.resultReg, reg2=toTr.resultReg, labelSymbol=loopLabel)
} }
addInstr(result, branchInstr, null)
result += IRCodeChunk(labelAfterFor, null) result += IRCodeChunk(labelAfterFor, null)
return result return result
} }
@ -909,6 +896,7 @@ class IRCodeGen(
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
when (condition) { when (condition) {
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!options.useNewExprCode)
if(condition.operator !in ComparisonOperators) if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression") throw AssemblyError("if condition should only be a binary comparison expression")
@ -922,6 +910,7 @@ class IRCodeGen(
} }
else -> { else -> {
// if X --> meaning: if X!=0 // if X --> meaning: if X!=0
require(options.useNewExprCode)
val irDt = irType(condition.type) val irDt = irType(condition.type)
val signed = condition.type in SignedDatatypes val signed = condition.type in SignedDatatypes
return if(goto!=null && ifElse.elseScope.children.isEmpty()) { return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
@ -999,10 +988,10 @@ class IRCodeGen(
val opcode = when (condition.operator) { val opcode = when (condition.operator) {
"==" -> Opcode.BEQ "==" -> Opcode.BEQ
"!=" -> Opcode.BNE "!=" -> Opcode.BNE
"<" -> if (signed) Opcode.BLTS else Opcode.BLT "<" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
">" -> if (signed) Opcode.BGTS else Opcode.BGT ">" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
"<=" -> if (signed) Opcode.BLES else Opcode.BLE "<=" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
">=" -> if (signed) Opcode.BGES else Opcode.BGE ">=" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
else -> throw AssemblyError("invalid comparison operator") else -> throw AssemblyError("invalid comparison operator")
} }
if (goto.address != null) if (goto.address != null)
@ -1136,10 +1125,10 @@ class IRCodeGen(
elseBranch = when (condition.operator) { elseBranch = when (condition.operator) {
"==" -> Opcode.BNE "==" -> Opcode.BNE
"!=" -> Opcode.BEQ "!=" -> Opcode.BEQ
"<" -> if (signed) Opcode.BGES else Opcode.BGE "<" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
">" -> if (signed) Opcode.BLES else Opcode.BLE ">" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
"<=" -> if (signed) Opcode.BGTS else Opcode.BGT "<=" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
">=" -> if (signed) Opcode.BLTS else Opcode.BLT ">=" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
else -> throw AssemblyError("weird operator") else -> throw AssemblyError("weird operator")
} }
} }
@ -1223,7 +1212,7 @@ class IRCodeGen(
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
addInstr( addInstr(
result, result,
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = elseLabel), IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = elseLabel),
null null
) )
result += translateNode(ifElse.ifScope) result += translateNode(ifElse.ifScope)
@ -1235,7 +1224,7 @@ class IRCodeGen(
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
addInstr( addInstr(
result, result,
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel), IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = afterIfLabel),
null null
) )
result += translateNode(ifElse.ifScope) result += translateNode(ifElse.ifScope)
@ -1361,126 +1350,59 @@ class IRCodeGen(
} }
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks { private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val operationMem: Opcode
val operationRegister: Opcode
when(postIncrDecr.operator) {
"++" -> {
operationMem = Opcode.INCM
operationRegister = Opcode.INC
}
"--" -> {
operationMem = Opcode.DECM
operationRegister = Opcode.DEC
}
else -> throw AssemblyError("weird operator")
}
val ident = postIncrDecr.target.identifier
val memory = postIncrDecr.target.memory
val array = postIncrDecr.target.array val array = postIncrDecr.target.array
if(array?.splitWords==true) { val irDt = irType(postIncrDecr.target.type)
val result = mutableListOf<IRCodeChunkBase>()
if(ident!=null) {
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
} else if(memory!=null) {
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
} else {
val tr = expressionEval.translateExpression(memory.address)
addToResult(result, tr, tr.resultReg, -1)
val chunk = IRCodeChunk(null, null)
val incReg = registers.nextFree()
chunk += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
chunk += IRInstruction(operationRegister, irDt, reg1 = incReg)
chunk += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
result += chunk
}
} else if (array!=null) {
val variable = array.variable.name val variable = array.variable.name
val itemsize = program.memsizer.memorySize(array.type)
val fixedIndex = constIntValue(array.index) val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) { if(fixedIndex!=null) {
val skipLabel = createLabelName() val offset = fixedIndex*itemsize
when(postIncrDecr.operator) { addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
"++" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
}
result += IRCodeChunk(skipLabel, null)
}
"--" -> {
val valueReg=registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
}
result += IRCodeChunk(skipLabel, null).also {
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
}
}
else -> throw AssemblyError("weird operator")
}
} else { } else {
val arrayLength = symbolTable.getLength(array.variable.name)
val indexTr = expressionEval.translateExpression(array.index) val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1) addToResult(result, indexTr, indexTr.resultReg, -1)
val chunk = IRCodeChunk(null, null)
val incReg = registers.nextFree() val incReg = registers.nextFree()
val skipLabel = createLabelName() chunk += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
when(postIncrDecr.operator) { chunk += IRInstruction(operationRegister, irDt, reg1=incReg)
"++" -> { chunk += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
result += IRCodeChunk(null, null).also { result += chunk
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
result += IRCodeChunk(skipLabel, null)
}
"--" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
result += IRCodeChunk(skipLabel, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
}
}
else -> throw AssemblyError("weird operator")
}
} }
} else { } else
val ident = postIncrDecr.target.identifier throw AssemblyError("weird assigntarget")
val memory = postIncrDecr.target.memory
val irDt = irType(postIncrDecr.target.type)
val operationMem: Opcode
val operationRegister: Opcode
when(postIncrDecr.operator) {
"++" -> {
operationMem = Opcode.INCM
operationRegister = Opcode.INC
}
"--" -> {
operationMem = Opcode.DECM
operationRegister = Opcode.DEC
}
else -> throw AssemblyError("weird operator")
}
if(ident!=null) {
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
} else if(memory!=null) {
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
} else {
val tr = expressionEval.translateExpression(memory.address)
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
val incReg = registers.nextFree()
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
}
}
} else if (array!=null) {
val variable = array.variable.name
val itemsize = program.memsizer.memorySize(array.type)
val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
} else {
val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
if(itemsize>1)
result += multiplyByConst(IRDataType.BYTE, indexTr.resultReg, itemsize)
result += IRCodeChunk(null, null).also {
val incReg = registers.nextFree()
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
it += IRInstruction(operationRegister, irDt, reg1=incReg)
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
}
}
} else
throw AssemblyError("weird assigntarget")
}
return result return result
} }
@ -1501,41 +1423,29 @@ class IRCodeGen(
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val countTr = expressionEval.translateExpression(repeat.count) val countTr = expressionEval.translateExpression(repeat.count)
addToResult(result, countTr, countTr.resultReg, -1) addToResult(result, countTr, countTr.resultReg, -1)
if(constIntValue(repeat.count)==null) { addInstr(result, IRInstruction(Opcode.BEQ, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = skipRepeatLabel), null)
// check if the counter is already zero
addInstr(result, IRInstruction(Opcode.BEQ, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = skipRepeatLabel), null)
}
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel) result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
result += IRCodeChunk(null, null).also { val chunk = IRCodeChunk(null, null)
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg) chunk += IRInstruction(Opcode.DEC, irDt, reg1=countTr.resultReg)
it += IRInstruction(Opcode.BNE, irDt, reg1 = countTr.resultReg, immediate = 0, labelSymbol = repeatLabel) chunk += IRInstruction(Opcode.BNE, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = repeatLabel)
} result += chunk
result += IRCodeChunk(skipRepeatLabel, null) result += IRCodeChunk(skipRepeatLabel, null)
return result return result
} }
private fun translate(jump: PtJump): IRCodeChunks { private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, null) val instr = if(jump.address!=null) {
if(jump.address!=null) { IRInstruction(Opcode.JUMPA, address = jump.address!!.toInt())
chunk += IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
} else { } else {
if (jump.generatedLabel != null) if (jump.generatedLabel != null)
chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!) IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
else if (jump.identifier != null) { else if (jump.identifier != null)
val symbol = symbolTable.lookup(jump.identifier!!.name) IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
if(symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR) {
val jumpReg = registers.nextFree()
chunk += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = jumpReg, labelSymbol = jump.identifier!!.name)
chunk += IRInstruction(Opcode.JUMPI, reg1 = jumpReg)
} else {
chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
}
}
else else
throw AssemblyError("weird jump") throw AssemblyError("weird jump")
} }
result += chunk addInstr(result, instr, null)
return result return result
} }
@ -1623,7 +1533,7 @@ class IRCodeGen(
private fun translate(parameters: List<PtSubroutineParameter>) = private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map { parameters.map {
val flattenedName = it.definingSub()!!.name + "." + it.name val flattenedName = it.definingSub()!!.name + "." + it.name
val orig = symbolTable.lookup(flattenedName) as StStaticVariable val orig = symbolTable.flat.getValue(flattenedName) as StStaticVariable
IRSubroutine.IRParam(flattenedName, orig.dt) IRSubroutine.IRParam(flattenedName, orig.dt)
} }

View File

@ -23,7 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun optimizeOnlyJoinChunks() { private fun optimizeOnlyJoinChunks() {
irprog.foreachSub { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
joinChunks(sub) joinChunks(sub)
removeEmptyChunks(sub) removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
@ -32,11 +32,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun peepholeOptimize() { private fun peepholeOptimize() {
irprog.foreachSub { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
joinChunks(sub) joinChunks(sub)
removeEmptyChunks(sub) removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
sub.chunks.withIndex().forEach { (index, chunk1) -> sub.chunks.withIndex().forEach { (index, chunk1) ->
// we don't optimize Inline Asm chunks here. // we don't optimize Inline Asm chunks here.
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
@ -47,19 +46,17 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
val changed = removeNops(chunk1, indexedInstructions) val changed = removeNops(chunk1, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented || removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk1, indexedInstructions) || removeUselessArithmetic(chunk1, indexedInstructions)
|| removeNeedlessCompares(chunk1, indexedInstructions)
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions) || removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions) || removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions) || cleanupPushPop(chunk1, indexedInstructions)
// TODO other optimizations // TODO other optimizations:
// more complex optimizations such as unused registers
} while (changed) } while (changed)
} }
} }
removeEmptyChunks(sub) removeEmptyChunks(sub)
} }
// TODO also do register optimization step here at the end?
irprog.linkChunks() // re-link irprog.linkChunks() // re-link
} }
@ -267,28 +264,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed return changed
} }
private fun removeNeedlessCompares(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// a CMPI with 0, after an instruction like LOAD that already sets the status bits, can be removed.
// but only if the instruction after it is not using the Carry bit because that won't be set by a LOAD instruction etc.
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(idx>0 && idx<(indexedInstructions.size-1) && ins.opcode==Opcode.CMPI && ins.immediate==0) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
chunk.instructions.removeAt(idx)
changed = true
} else if(previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
val next = indexedInstructions[idx+1].value
if(next.opcode !in arrayOf(Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTPOS, Opcode.BSTNEG)) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
return changed
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean { private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first // note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false var changed = false

View File

@ -1,103 +0,0 @@
package prog8.codegen.intermediate
import prog8.intermediate.IRProgram
class IRRegisterOptimizer(private val irProg: IRProgram) {
fun optimize() {
// reuseRegisters()
}
/*
TODO: this register re-use renumbering isn't going to work like this,
because subroutines will be clobbering the registers that the subroutine
which is calling them might be using...
private fun reuseRegisters() {
fun addToUsage(usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>>,
regnum: Int,
dt: IRDataType,
chunk: IRCodeChunkBase) {
val key = regnum to dt
val chunks = usage[key] ?: mutableSetOf()
chunks.add(chunk)
usage[key] = chunks
}
val usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>> = mutableMapOf()
irProg.foreachCodeChunk { chunk ->
chunk.usedRegisters().regsTypes.forEach { (regNum, types) ->
types.forEach { dt ->
addToUsage(usage, regNum, dt, chunk)
}
}
}
val registerReplacements = usage.asSequence()
.filter { it.value.size==1 }
.map { it.key to it.value.iterator().next() }
.groupBy({ it.second }, {it.first})
.asSequence()
.associate { (chunk, registers) ->
chunk to registers.withIndex().associate { (index, reg) -> reg to 50000+index }
}
registerReplacements.forEach { replaceRegisters(it.key, it.value) }
}
private fun replaceRegisters(chunk: IRCodeChunkBase, replacements: Map<Pair<Int, IRDataType>, Int>) {
val (rF, rI) = replacements.asSequence().partition { it.key.second==IRDataType.FLOAT }
val replacementsInt = rI.associate { it.key.first to it.value }
val replacementsFloat = rF.associate { it.key.first to it.value }
fun replaceRegs(fcallArgs: FunctionCallArgs?): FunctionCallArgs? {
if(fcallArgs==null)
return null
val args = if(fcallArgs.arguments.isEmpty()) fcallArgs.arguments else {
fcallArgs.arguments.map {
FunctionCallArgs.ArgumentSpec(
it.name,
it.address,
FunctionCallArgs.RegSpec(
it.reg.dt,
if(it.reg.dt==IRDataType.FLOAT)
replacementsFloat.getOrDefault(it.reg.registerNum, it.reg.registerNum)
else
replacementsInt.getOrDefault(it.reg.registerNum, it.reg.registerNum),
it.reg.cpuRegister
)
)
}
}
val rt = fcallArgs.returns
val returns = if(rt==null) null else {
FunctionCallArgs.RegSpec(
rt.dt,
if(rt.dt==IRDataType.FLOAT)
replacementsFloat.getOrDefault(rt.registerNum, rt.registerNum)
else
replacementsInt.getOrDefault(rt.registerNum, rt.registerNum),
rt.cpuRegister
)
}
return FunctionCallArgs(args, returns)
}
fun replaceRegs(instruction: IRInstruction): IRInstruction {
val reg1 = replacementsInt.getOrDefault(instruction.reg1, instruction.reg1)
val reg2 = replacementsInt.getOrDefault(instruction.reg2, instruction.reg2)
val fpReg1 = replacementsFloat.getOrDefault(instruction.fpReg1, instruction.fpReg1)
val fpReg2 = replacementsFloat.getOrDefault(instruction.fpReg2, instruction.fpReg2)
return instruction.copy(reg1 = reg1, reg2 = reg2, fpReg1 = fpReg1, fpReg2 = fpReg2, fcallArgs = replaceRegs(instruction.fcallArgs))
}
val newInstructions = chunk.instructions.map {
replaceRegs(it)
}
chunk.instructions.clear()
chunk.instructions.addAll(newInstructions)
}
*/
}

View File

@ -16,7 +16,7 @@ class IRUnusedCodeRemover(
irprog.blocks.reversed().forEach { block -> irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) { if(block.isEmpty()) {
irprog.blocks.remove(block) irprog.blocks.remove(block)
pruneSymboltable(block.label) irprog.st.removeTree(block.label)
numRemoved++ numRemoved++
} }
} }
@ -24,26 +24,12 @@ class IRUnusedCodeRemover(
return numRemoved return numRemoved
} }
private fun pruneSymboltable(blockLabel: String) {
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction
val prefix = "$blockLabel."
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
blockVars.forEach { stVar ->
irprog. allSubs().flatMap { it.chunks }.forEach { chunk ->
chunk.instructions.forEach { ins ->
if(ins.labelSymbol == stVar.name) {
return
}
}
}
}
irprog.st.removeTree(blockLabel)
}
private fun removeUnusedSubroutines(): Int { private fun removeUnusedSubroutines(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>() val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
irprog.foreachCodeChunk { chunk -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
chunk.label?.let { allLabeledChunks[it] = chunk } sub.chunks.forEach { chunk ->
chunk.label?.let { allLabeledChunks[it] = chunk }
}
} }
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks) var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
@ -111,10 +97,12 @@ class IRUnusedCodeRemover(
} }
// check if asmsub is linked or called from another regular subroutine // check if asmsub is linked or called from another regular subroutine
irprog.foreachCodeChunk { chunk -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
chunk.instructions.forEach { sub.chunks.forEach { chunk ->
it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } } chunk.instructions.forEach {
// note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } }
// note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
}
} }
} }
@ -135,22 +123,9 @@ class IRUnusedCodeRemover(
} }
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int { private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
val entrypointSub = irprog.blocks.single { it.label=="main" } val entrypointSub = irprog.blocks.single { it.label=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
.children.single { it is IRSubroutine && it.label=="main.start" }
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first()) val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
// all chunks referenced in array initializer values are also 'reachable':
irprog.st.allVariables()
.filter { !it.uninitialized }
.forEach {
it.onetimeInitializationArrayValue?.let { array ->
array.forEach {elt ->
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
reachable.add(irprog.getChunkWithLabel(elt.addressOfSymbol!!))
}
}
}
fun grow() { fun grow() {
val new = mutableSetOf<IRCodeChunkBase>() val new = mutableSetOf<IRCodeChunkBase>()
reachable.forEach { reachable.forEach {
@ -179,29 +154,19 @@ class IRUnusedCodeRemover(
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int { private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
val linkedChunks = mutableSetOf<IRCodeChunkBase>() val linkedChunks = mutableSetOf<IRCodeChunkBase>()
// all chunks referenced in array initializer values are linked as well!: irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
irprog.st.allVariables() sub.chunks.forEach { chunk ->
.filter { !it.uninitialized } chunk.next?.let { next -> linkedChunks += next }
.forEach { chunk.instructions.forEach {
it.onetimeInitializationArrayValue?.let { array -> if(it.branchTarget==null) {
array.forEach {elt -> it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null) } else {
linkedChunks += irprog.getChunkWithLabel(elt.addressOfSymbol!!) linkedChunks += it.branchTarget!!
} }
} }
if (chunk.label == "main.start")
linkedChunks += chunk
} }
irprog.foreachCodeChunk { chunk ->
chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach {
if(it.branchTarget==null) {
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
} else {
linkedChunks += it.branchTarget!!
}
}
if (chunk.label == "main.start")
linkedChunks += chunk
} }
return removeUnlinkedChunks(linkedChunks) return removeUnlinkedChunks(linkedChunks)
@ -211,7 +176,7 @@ class IRUnusedCodeRemover(
linkedChunks: Set<IRCodeChunkBase> linkedChunks: Set<IRCodeChunkBase>
): Int { ): Int {
var numRemoved = 0 var numRemoved = 0
irprog.foreachSub { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
sub.chunks.withIndex().reversed().forEach { (index, chunk) -> sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
if (chunk !in linkedChunks) { if (chunk !in linkedChunks) {
if (chunk === sub.chunks[0]) { if (chunk === sub.chunks[0]) {

View File

@ -43,10 +43,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text) warnings.add(text)
} }
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
override fun noErrors(): Boolean = errors.isEmpty() override fun noErrors(): Boolean = errors.isEmpty()
override fun report() { override fun report() {

View File

@ -36,7 +36,7 @@ class TestIRPeepholeOpt: FunSpec({
return makeIRProgram(listOf(chunk)) return makeIRProgram(listOf(chunk))
} }
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.allSubs().flatMap { it.chunks }.toList() fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }
test("remove nops") { test("remove nops") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(

View File

@ -41,7 +41,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY)) sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@ -91,7 +91,7 @@ class TestVmCodeGen: FunSpec({
program.add(block) program.add(block)
// define the "cx16.r0" virtual register // define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY)) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block) program.add(cx16block)
@ -120,7 +120,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -183,7 +183,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -242,7 +242,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -289,7 +289,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -352,7 +352,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -411,7 +411,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -451,7 +451,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY) val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(romsub) block.add(romsub)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)

View File

@ -3,17 +3,16 @@ package prog8.optimizer
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.base.UndefinedSymbolError
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.maySwapOperandOrder import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.ForLoop import prog8.ast.statements.ForLoop
import prog8.ast.statements.RepeatLoop
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType import prog8.ast.statements.VarDeclType
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType import prog8.code.core.DataType
import kotlin.math.floor
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@ -308,7 +307,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
val rangeTo = iterableRange.to as? NumericLiteral val rangeTo = iterableRange.to as? NumericLiteral
if(rangeFrom==null || rangeTo==null) return noModifications if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar)
val stepLiteral = iterableRange.step as? NumericLiteral val stepLiteral = iterableRange.step as? NumericLiteral
when(loopvar.datatype) { when(loopvar.datatype) {
@ -365,16 +364,6 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
return noModifications return noModifications
} }
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
val count = (repeatLoop.iterations as? NumericLiteral)?.number
if(count!=null && floor(count)!=count) {
val integer = NumericLiteral.optimalInteger(count.toInt(), repeatLoop.position)
repeatLoop.iterations = integer
integer.linkParents(repeatLoop)
}
return noModifications
}
private class ShuffleOperands(val expr: BinaryExpression, private class ShuffleOperands(val expr: BinaryExpression,
val exprOperator: String?, val exprOperator: String?,
val subExpr: BinaryExpression, val subExpr: BinaryExpression,

View File

@ -53,8 +53,7 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
} }
if(to==null) { if(to==null) {
val toType = range.to.inferType(program) if(!range.to.inferType(program).isInteger)
if(toType.isKnown && !range.to.inferType(program).isInteger)
errors.err("range expression to value must be integer", range.to.position) errors.err("range expression to value must be integer", range.to.position)
} else if(to-to.toInt()>0) { } else if(to-to.toInt()>0) {
errors.err("range expression to value must be integer", range.to.position) errors.err("range expression to value must be integer", range.to.position)
@ -69,124 +68,6 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
return noModifications return noModifications
} }
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
// choose specific builtin function for the given types
val func = functionCallExpr.target.nameInSource
if(func==listOf("clamp")) {
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val replaceFunc: String
if(t1.isBytes) {
replaceFunc = if(t1.istype(DataType.BYTE)) "clamp__byte" else "clamp__ubyte"
} else if(t1.isInteger) {
replaceFunc = if(t1.istype(DataType.WORD)) "clamp__word" else "clamp__uword"
} else {
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
return noModifications
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
else if(func==listOf("min") || func==listOf("max")) {
val t1 = functionCallExpr.args[0].inferType(program)
val t2 = functionCallExpr.args[1].inferType(program)
if(t1.isKnown && t2.isKnown) {
val funcName = func[0]
val replaceFunc: String
if(t1.isBytes && t2.isBytes) {
replaceFunc = if(t1.istype(DataType.BYTE) || t2.istype(DataType.BYTE))
"${funcName}__byte"
else
"${funcName}__ubyte"
} else if(t1.isInteger && t2.isInteger) {
replaceFunc = if(t1.istype(DataType.WORD) || t2.istype(DataType.WORD))
"${funcName}__word"
else
"${funcName}__uword"
} else if(t1.isNumeric && t2.isNumeric) {
errors.err("min/max not supported for floats", functionCallExpr.position)
return noModifications
} else {
errors.err("expected numeric arguments", functionCallExpr.args[0].position)
return noModifications
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
else if(func==listOf("abs")) {
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.BYTE -> "abs__byte"
DataType.WORD -> "abs__word"
DataType.FLOAT -> "abs__float"
DataType.UBYTE, DataType.UWORD -> {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
else -> {
errors.err("expected numeric argument", functionCallExpr.args[0].position)
return noModifications
}
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
else if(func==listOf("sqrt")) {
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "sqrt__ubyte"
DataType.UWORD -> "sqrt__uword"
DataType.FLOAT -> "sqrt__float"
else -> {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications
}
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
// choose specific builtin function for the given types
val func = functionCallStatement.target.nameInSource
if(func==listOf("divmod")) {
val argTypes = functionCallStatement.args.map {it.inferType(program)}.toSet()
if(argTypes.size!=1) {
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
return noModifications
}
val t1 = argTypes.single()
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "divmod__ubyte"
DataType.UWORD -> "divmod__uword"
else -> {
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
return noModifications
}
}
return listOf(IAstModification.SetExpression({functionCallStatement.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallStatement.target.position),
functionCallStatement))
}
}
return noModifications
}
} }
@ -276,7 +157,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl)) return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
} }
} }
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
val rangeExpr = decl.value as? RangeExpression val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) { if(rangeExpr!=null) {
// convert the initializer range expression to an actual array // convert the initializer range expression to an actual array

View File

@ -18,7 +18,7 @@ import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.pow import kotlin.math.pow
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has? // TODO add more peephole expression optimizations? Investigate what optimizations binaryen has
class ExpressionSimplifier(private val program: Program, class ExpressionSimplifier(private val program: Program,
private val errors: IErrorReporter, private val errors: IErrorReporter,

View File

@ -43,9 +43,9 @@ fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget)
fun Program.optimizeStatements(errors: IErrorReporter, fun Program.optimizeStatements(errors: IErrorReporter,
functions: IBuiltinFunctions, functions: IBuiltinFunctions,
options: CompilationOptions compTarget: ICompilationTarget
): Int { ): Int {
val optimizer = StatementOptimizer(this, errors, functions, options) val optimizer = StatementOptimizer(this, errors, functions, compTarget)
optimizer.visit(this) optimizer.visit(this)
val optimizationCount = optimizer.applyModifications() val optimizationCount = optimizer.applyModifications()
@ -54,8 +54,8 @@ fun Program.optimizeStatements(errors: IErrorReporter,
return optimizationCount return optimizationCount
} }
fun Program.inlineSubroutines(options: CompilationOptions): Int { fun Program.inlineSubroutines(): Int {
val inliner = Inliner(this, options) val inliner = Inliner(this)
inliner.visit(this) inliner.visit(this)
return inliner.applyModifications() return inliner.applyModifications()
} }

View File

@ -8,9 +8,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException import prog8.code.core.InternalCompilerException
import prog8.code.target.VMTarget
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
@ -18,7 +16,7 @@ private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.va
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined. // inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() { class Inliner(val program: Program): AstWalker() {
class DetermineInlineSubs(val program: Program): IAstVisitor { class DetermineInlineSubs(val program: Program): IAstVisitor {
private val modifications = mutableListOf<IAstModification>() private val modifications = mutableListOf<IAstModification>()
@ -213,7 +211,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
return if(sub==null || !canInline(sub, functionCallStatement)) return if(sub==null)
noModifications noModifications
else else
possibleInlineFcallStmt(sub, functionCallStatement, parent) possibleInlineFcallStmt(sub, functionCallStatement, parent)
@ -221,7 +219,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> { override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) { if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) { require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
"invalid inline sub at ${sub.position}" "invalid inline sub at ${sub.position}"
} }
@ -248,17 +246,5 @@ class Inliner(private val program: Program, private val options: CompilationOpti
return noModifications return noModifications
} }
private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean {
if(!sub.inline)
return false
if(options.compTarget.name!=VMTarget.NAME) {
val stmt = sub.statements.single()
if (stmt is IFunctionCall) {
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
return existing !is VarDecl
}
}
return true
}
} }

View File

@ -13,7 +13,7 @@ import kotlin.math.floor
class StatementOptimizer(private val program: Program, class StatementOptimizer(private val program: Program,
private val errors: IErrorReporter, private val errors: IErrorReporter,
private val functions: IBuiltinFunctions, private val functions: IBuiltinFunctions,
private val options: CompilationOptions private val compTarget: ICompilationTarget
) : AstWalker() { ) : AstWalker() {
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
@ -39,7 +39,7 @@ class StatementOptimizer(private val program: Program,
if(string!=null) { if(string!=null) {
val pos = functionCallStatement.position val pos = functionCallStatement.position
if (string.value.length == 1) { if (string.value.length == 1) {
val firstCharEncoded = options.compTarget.encodeString(string.value, string.encoding)[0] val firstCharEncoded = compTarget.encodeString(string.value, string.encoding)[0]
val chrout = FunctionCallStatement( val chrout = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos), IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), pos)), mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), pos)),
@ -51,7 +51,7 @@ class StatementOptimizer(private val program: Program,
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer) IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
) )
} else if (string.value.length == 2) { } else if (string.value.length == 2) {
val firstTwoCharsEncoded = options.compTarget.encodeString(string.value.take(2), string.encoding) val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.encoding)
val chrout1 = FunctionCallStatement( val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos), IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)), mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
@ -108,16 +108,6 @@ class StatementOptimizer(private val program: Program,
} }
} }
// remove obvious dangling elses (else after a return)
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
if(options.slowCodegenWarnings)
errors.warn("else can be omitted", ifElse.elsepart.position)
return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
)
}
return noModifications return noModifications
} }
@ -151,7 +141,7 @@ class StatementOptimizer(private val program: Program,
val size = sv.value.length val size = sv.value.length
if(size==1) { if(size==1) {
// loop over string of length 1 -> just assign the single character // loop over string of length 1 -> just assign the single character
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0] val character = compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position) val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
@ -176,6 +166,16 @@ class StatementOptimizer(private val program: Program,
} }
} }
val iterationCount = forLoop.constIterationCount(program)
if(iterationCount!=null) {
val loopName = forLoop.loopVar.nameInSource
if(!forLoop.iterable.referencesIdentifier(loopName) && !forLoop.body.referencesIdentifier(loopName)) {
errors.warn("for loop can be replaced with repeat loop", forLoop.position)
val repeat = RepeatLoop(NumericLiteral.optimalNumeric(iterationCount, forLoop.position), forLoop.body, forLoop.position)
return listOf(IAstModification.ReplaceNode(forLoop, repeat, parent))
}
}
return noModifications return noModifications
} }
@ -326,7 +326,7 @@ class StatementOptimizer(private val program: Program,
if (rightCv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) { } else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) { if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && compTarget.name!=VMTarget.NAME) {
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well) // replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
val incs = AnonymousScope(mutableListOf(), assignment.position) val incs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) { repeat(rightCv.toInt()) {
@ -340,7 +340,7 @@ class StatementOptimizer(private val program: Program,
if (rightCv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) { } else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) { if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && compTarget.name!=VMTarget.NAME) {
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well) // replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
val decs = AnonymousScope(mutableListOf(), assignment.position) val decs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) { repeat(rightCv.toInt()) {
@ -391,47 +391,4 @@ class StatementOptimizer(private val program: Program,
else else
noModifications noModifications
} }
override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> {
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position)
errors.warn("for boolean condition a normal if statement is preferred", whenStmt.position)
return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent))
}
if(whenStmt.condition.inferType(program).isBool) {
if(whenStmt.choices.all { it.values?.size==1 }) {
if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) {
// it's a when statement on booleans that can just be replaced by an if or if..else.
if (whenStmt.choices.size == 1) {
return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null)
} else {
val notCondition = BinaryExpression(whenStmt.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, whenStmt.condition.position), whenStmt.condition.position)
replaceWithIf(notCondition, whenStmt.choices[0].statements, null)
}
} else if (whenStmt.choices.size == 2) {
var trueBlock: AnonymousScope? = null
var elseBlock: AnonymousScope? = null
if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[0].statements
} else {
elseBlock = whenStmt.choices[0].statements
}
if(whenStmt.choices[1].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[1].statements
} else {
elseBlock = whenStmt.choices[1].statements
}
if(trueBlock!=null && elseBlock!=null) {
return replaceWithIf(whenStmt.condition, trueBlock, elseBlock)
}
}
}
}
}
return noModifications
}
} }

View File

@ -75,9 +75,7 @@ class UnusedCodeRemover(private val program: Program,
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.warn("removing unused block '${block.name}'", block.position) errors.warn("removing unused block '${block.name}'", block.position)
} }
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) { program.removeInternedStringsFromRemovedBlock(block)
program.removeInternedStringsFromRemovedBlock(block)
}
return listOf(IAstModification.Remove(block, parent as IStatementContainer)) return listOf(IAstModification.Remove(block, parent as IStatementContainer))
} }
} }

View File

@ -1,10 +1,9 @@
plugins { plugins {
id 'java' id 'java'
id 'application' id 'application'
id 'org.jetbrains.kotlin.jvm' id "org.jetbrains.kotlin.jvm"
id 'com.github.johnrengelman.shadow' version '7.1.2' id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'io.kotest' version '0.3.9' id "io.kotest" version "0.3.9"
id 'com.peterabeles.gversion' version '1.10.2'
} }
java { java {
@ -25,6 +24,8 @@ compileTestKotlin {
} }
} }
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
dependencies { dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':codeOptimizers') implementation project(':codeOptimizers')
@ -33,14 +34,14 @@ dependencies {
implementation project(':codeGenIntermediate') implementation project(':codeGenIntermediate')
implementation project(':codeGenExperimental') implementation project(':codeGenExperimental')
implementation project(':virtualmachine') implementation project(':virtualmachine')
implementation "org.antlr:antlr4-runtime:4.13.0" implementation "org.antlr:antlr4-runtime:4.12.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation project(':intermediate') testImplementation project(':intermediate')
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
} }
configurations.all { configurations.all {
@ -81,7 +82,7 @@ application {
shadowJar { shadowJar {
archiveBaseName = 'prog8compiler' archiveBaseName = 'prog8compiler'
archiveVersion = version archiveVersion = prog8version
// minimize() // minimize()
} }
@ -99,16 +100,4 @@ test {
} }
} }
gversion {
srcDir = "src/" // path is relative to the sub-project by default
classPackage = "prog8.buildversion"
className = "BuildVersion"
language = "kotlin"
}
build.finalizedBy installDist, installShadowDist build.finalizedBy installDist, installShadowDist
compileKotlin.dependsOn createVersionFile // , failDirtyNotSnapshot
compileJava.dependsOn createVersionFile

View File

@ -2,41 +2,44 @@
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
atari { atari {
%option no_symbol_prefixing
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in &uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in &uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in &uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
; ---- kernal routines ----
; TODO
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; TODO
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc
clv
cli
rts
}}
} }
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the Atari
}}
}
}
sys { sys {
; ------- lowlevel system routines -------- ; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; TODO
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc
clv
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the Atari
}}
}
asmsub reset_system() { asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
@ -185,19 +188,6 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
; TODO ; TODO
@ -218,7 +208,6 @@ _longcopy
} }
cx16 { cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the Atari as well but their location in memory is different ; they are simulated on the Atari as well but their location in memory is different
@ -324,31 +313,4 @@ cx16 {
&byte r13sH = $1b1b &byte r13sH = $1b1b
&byte r14sH = $1b1d &byte r14sH = $1b1d
&byte r15sH = $1b1f &byte r15sH = $1b1f
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
} }

View File

@ -6,8 +6,6 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 24 const ubyte DEFAULT_HEIGHT = 24
@ -72,7 +70,7 @@ sub uppercase() {
; TODO ; TODO
} }
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) { asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left ; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -83,7 +81,7 @@ asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
}} }}
} }
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -93,7 +91,7 @@ asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
}} }}
} }
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up ; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -103,7 +101,7 @@ asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
}} }}
} }
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down ; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -120,6 +118,7 @@ romsub $F2Fd = waitkey()
asmsub chrout(ubyte char @ A) { asmsub chrout(ubyte char @ A) {
%asm {{ %asm {{
sta _tmp_outchar+1 sta _tmp_outchar+1
pha
txa txa
pha pha
tya tya
@ -131,6 +130,7 @@ _tmp_outchar
tay tay
pla pla
tax tax
pla
rts rts
}} }}
} }
@ -210,7 +210,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}} }}
} }
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
@ -228,7 +228,7 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
@ -249,7 +249,7 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -261,7 +261,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{

View File

@ -6,8 +6,6 @@
; TODO c128 actually implement the graphics routines. Ideally a way to 'borrow' the code form the C64 version without just copy-pasting that here? ; TODO c128 actually implement the graphics routines. Ideally a way to 'borrow' the code form the C64 version without just copy-pasting that here?
graphics { graphics {
%option no_symbol_prefixing
const uword WIDTH = 320 const uword WIDTH = 320
const ubyte HEIGHT = 200 const ubyte HEIGHT = 200

View File

@ -1,9 +1,7 @@
; Prog8 definitions for the Commodore-128 ; Prog8 definitions for the Commodore-128
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
cbm { c64 {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte &ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte &ubyte TIME_MID = $a1 ; .. mid byte
@ -49,100 +47,6 @@ cbm {
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
; STROUT --> use txt.print
; CLEARSCR -> use txt.clear_screen
; HOMECRSR -> use txt.home or txt.plot
romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
; ---- end of C64 compatible ROM kernal routines ----
; ---- utilities -----
asmsub STOP2() -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{
txa
pha
jsr cbm.STOP
beq +
pla
tax
lda #0
rts
+ pla
tax
lda #1
rts
}}
}
asmsub RDTIM16() -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
%asm {{
stx P8ZP_SCRATCH_REG
jsr cbm.RDTIM
pha
txa
tay
pla
ldx P8ZP_SCRATCH_REG
rts
}}
}
}
c64 {
; C64 I/O registers (VIC, SID, CIA)
%option no_symbol_prefixing
; the default locations of the 8 sprite pointers (store address of sprite / 64) ; the default locations of the 8 sprite pointers (store address of sprite / 64)
&ubyte SPRPTR0 = 2040 &ubyte SPRPTR0 = 2040
&ubyte SPRPTR1 = 2041 &ubyte SPRPTR1 = 2041
@ -292,93 +196,95 @@ c64 {
; ---- end of SID registers ---- ; ---- end of SID registers ----
} ; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
c128 { ; STROUT --> use txt.print
; ---- C128 specific registers ---- ; CLEARSCR -> use txt.clear_screen
%option no_symbol_prefixing ; HOMECRSR -> use txt.home or txt.plot
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
&ubyte VM3 = $0A2E ; starting page for VDC screen mem
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
; ---- C128 specific system utility routines: ---- romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
asmsub disable_basic() clobbers(A) { ; ---- end of C64 compatible ROM kernal routines ----
; ---- utilities -----
asmsub STOP2() -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{ %asm {{
lda $0a04 ; disable BASIC shadow registers txa
and #$fe pha
sta $0a04 jsr c64.STOP
beq +
lda #$01 ; disable BASIC IRQ service routine pla
sta $12fd tax
lda #0
lda #$ff ; disable screen editor IRQ setup rts
sta $d8 + pla
tax
lda #$b7 ; skip programmable function key check lda #1
sta $033c
lda #$0e ; bank out BASIC ROM
sta $ff00
rts rts
}} }}
} }
; ---- end of C128 specific system utility routines ---- asmsub RDTIM16() -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
}
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
; Also a different color scheme is chosen to identify ourselves a little.
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
%asm {{ %asm {{
sei stx P8ZP_SCRATCH_REG
cld jsr c64.RDTIM
lda #0 pha
sta $ff00 ; select default bank 15 txa
jsr cbm.IOINIT tay
jsr cbm.RESTOR pla
jsr cbm.CINT ldx P8ZP_SCRATCH_REG
lda #6
sta c64.EXTCOL
lda #7
sta cbm.COLOR
lda #0
sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch
clc
clv
cli
rts rts
}} }}
} }
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the C128
}}
}
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp sys.enable_runstop_and_charsetswitch
}}
}
; ---- system utility routines that are essentially the same as on the C64: -----
asmsub disable_runstop_and_charsetswitch() clobbers(A) { asmsub disable_runstop_and_charsetswitch() clobbers(A) {
%asm {{ %asm {{
lda #$80 lda #$80
@ -399,7 +305,7 @@ asmsub enable_runstop_and_charsetswitch() clobbers(A) {
}} }}
} }
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) { asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -408,9 +314,9 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
sta _use_kernal sta _use_kernal
sei sei
lda #<_irq_handler lda #<_irq_handler
sta cbm.CINV sta c64.CINV
lda #>_irq_handler lda #>_irq_handler
sta cbm.CINV+1 sta c64.CINV+1
cli cli
rts rts
_irq_handler jsr _irq_handler_init _irq_handler jsr _irq_handler_init
@ -428,7 +334,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp cbm.IRQDFRT ; continue with normal kernal irq routine + jmp c64.IRQDFRT ; continue with normal kernal irq routine
_use_kernal .byte 0 _use_kernal .byte 0
@ -480,10 +386,10 @@ IRQ_SCRATCH_ZPWORD2 .word 0
asmsub restore_irq() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
sei sei
lda #<cbm.IRQDFRT lda #<c64.IRQDFRT
sta cbm.CINV sta c64.CINV
lda #>cbm.IRQDFRT lda #>c64.IRQDFRT
sta cbm.CINV+1 sta c64.CINV+1
lda #0 lda #0
sta c64.IREQMASK ; disable raster irq sta c64.IREQMASK ; disable raster irq
lda #%10000001 lda #%10000001
@ -493,7 +399,7 @@ asmsub restore_irq() clobbers(A) {
}} }}
} }
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -505,9 +411,9 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc
sei sei
jsr _setup_raster_irq jsr _setup_raster_irq
lda #<_raster_irq_handler lda #<_raster_irq_handler
sta cbm.CINV sta c64.CINV
lda #>_raster_irq_handler lda #>_raster_irq_handler
sta cbm.CINV+1 sta c64.CINV+1
cli cli
rts rts
@ -515,8 +421,8 @@ _raster_irq_handler
jsr set_irq._irq_handler_init jsr set_irq._irq_handler_init
_modified jsr $ffff ; modified _modified jsr $ffff ; modified
jsr set_irq._irq_handler_end jsr set_irq._irq_handler_end
lda #$ff lda #$ff
sta c64.VICIRQ ; acknowledge raster irq sta c64.VICIRQ ; acknowledge raster irq
lda set_irq._use_kernal lda set_irq._use_kernal
bne + bne +
; end irq processing - don't use kernal's irq handling ; end irq processing - don't use kernal's irq handling
@ -526,7 +432,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp cbm.IRQDFRT ; continue with kernal irq routine + jmp c64.IRQDFRT ; continue with kernal irq routine
_setup_raster_irq _setup_raster_irq
pha pha
@ -550,13 +456,99 @@ _setup_raster_irq
}} }}
} }
}
c128 {
; ---- C128 specific registers ----
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
&ubyte VM3 = $0A2E ; starting page for VDC screen mem
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
; ---- C128 specific system utility routines: ----
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
; Also a different color scheme is chosen to identify ourselves a little.
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
%asm {{
sei
cld
lda #0
sta $ff00 ; select default bank 15
jsr c64.IOINIT
jsr c64.RESTOR
jsr c64.CINT
lda #6
sta c64.EXTCOL
lda #7
sta c64.COLOR
lda #0
sta c64.BGCOL0
jsr c64.disable_runstop_and_charsetswitch
clc
clv
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the C128
}}
}
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp c64.enable_runstop_and_charsetswitch
}}
}
asmsub disable_basic() clobbers(A) {
%asm {{
lda $0a04 ; disable BASIC shadow registers
and #$fe
sta $0a04
lda #$01 ; disable BASIC IRQ service routine
sta $12fd
lda #$ff ; disable screen editor IRQ setup
sta $d8
lda #$b7 ; skip programmable function key check
sta $033c
lda #$0e ; bank out BASIC ROM
sta $ff00
rts
}}
}
; ---- end of C128 specific system utility routines ----
}
sys {
; ------- lowlevel system routines --------
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub reset_system() { asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
%asm {{ %asm {{
sei sei
lda #0 lda #0
sta $ff00 ; default bank 15 sta $ff00 ; default bank 15
jmp (cbm.RESET_VEC) jmp (c64.RESET_VEC)
}} }}
} }
@ -574,9 +566,9 @@ _loop lda P8ZP_SCRATCH_W1
ldx P8ZP_SCRATCH_B1 ldx P8ZP_SCRATCH_B1
rts rts
+ lda cbm.TIME_LO + lda c64.TIME_LO
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- lda cbm.TIME_LO - lda c64.TIME_LO
cmp P8ZP_SCRATCH_B1 cmp P8ZP_SCRATCH_B1
beq - beq -
@ -733,26 +725,13 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
lda #0 lda #0
sta $ff00 ; default bank 15 sta $ff00 ; default bank 15
jsr cbm.CLRCHN ; reset i/o channels jsr c64.CLRCHN ; reset i/o channels
jsr sys.enable_runstop_and_charsetswitch jsr c64.enable_runstop_and_charsetswitch
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
txs txs
rts ; return to original caller rts ; return to original caller
@ -769,7 +748,6 @@ _longcopy
} }
cx16 { cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the C128 as well but their location in memory is different ; they are simulated on the C128 as well but their location in memory is different
@ -879,30 +857,4 @@ cx16 {
&byte r13sH = $1b1b &byte r13sH = $1b1b
&byte r14sH = $1b1d &byte r14sH = $1b1d
&byte r15sH = $1b1f &byte r15sH = $1b1f
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
} }

View File

@ -5,7 +5,6 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25 const ubyte DEFAULT_HEIGHT = 25
@ -31,10 +30,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
sec sec
jsr cbm.PLOT jsr c64.PLOT
tay tay
clc clc
jmp cbm.PLOT jmp c64.PLOT
}} }}
} }
@ -58,10 +57,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
; (assumes screen matrix is at the default address) ; (assumes screen matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta cbm.Screen+250*0-1,y - sta c64.Screen+250*0-1,y
sta cbm.Screen+250*1-1,y sta c64.Screen+250*1-1,y
sta cbm.Screen+250*2-1,y sta c64.Screen+250*2-1,y
sta cbm.Screen+250*3-1,y sta c64.Screen+250*3-1,y
dey dey
bne - bne -
rts rts
@ -73,10 +72,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
; (assumes color matrix is at the default address) ; (assumes color matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta cbm.Colors+250*0-1,y - sta c64.Colors+250*0-1,y
sta cbm.Colors+250*1-1,y sta c64.Colors+250*1-1,y
sta cbm.Colors+250*2-1,y sta c64.Colors+250*2-1,y
sta cbm.Colors+250*3-1,y sta c64.Colors+250*3-1,y
dey dey
bne - bne -
rts rts
@ -84,7 +83,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
} }
sub color (ubyte txtcol) { sub color (ubyte txtcol) {
cbm.COLOR = txtcol c64.COLOR = txtcol
} }
sub lowercase() { sub lowercase() {
@ -97,7 +96,7 @@ sub uppercase() {
c128.VM1 &= ~2 c128.VM1 &= ~2
} }
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) { asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left ; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -111,10 +110,10 @@ asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 1,x lda c64.Screen + 40*row + 1,x
sta cbm.Screen + 40*row + 0,x sta c64.Screen + 40*row + 0,x
lda cbm.Colors + 40*row + 1,x lda c64.Colors + 40*row + 1,x
sta cbm.Colors + 40*row + 0,x sta c64.Colors + 40*row + 0,x
.next .next
inx inx
dey dey
@ -126,8 +125,8 @@ _scroll_screen ; scroll only the screen memory
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 1,x lda c64.Screen + 40*row + 1,x
sta cbm.Screen + 40*row + 0,x sta c64.Screen + 40*row + 0,x
.next .next
inx inx
dey dey
@ -138,7 +137,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -150,10 +149,10 @@ asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 0,x lda c64.Screen + 40*row + 0,x
sta cbm.Screen + 40*row + 1,x sta c64.Screen + 40*row + 1,x
lda cbm.Colors + 40*row + 0,x lda c64.Colors + 40*row + 0,x
sta cbm.Colors + 40*row + 1,x sta c64.Colors + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -163,8 +162,8 @@ _scroll_screen ; scroll only the screen memory
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 0,x lda c64.Screen + 40*row + 0,x
sta cbm.Screen + 40*row + 1,x sta c64.Screen + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -174,7 +173,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up ; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -186,10 +185,10 @@ asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row-1),x sta c64.Screen + 40*(row-1),x
lda cbm.Colors + 40*row,x lda c64.Colors + 40*row,x
sta cbm.Colors + 40*(row-1),x sta c64.Colors + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -199,8 +198,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row-1),x sta c64.Screen + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -210,7 +209,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down ; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -222,10 +221,10 @@ asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda cbm.Colors + 40*row,x lda c64.Colors + 40*row,x
sta cbm.Colors + 40*(row+1),x sta c64.Colors + 40*(row+1),x
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row+1),x sta c64.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -235,8 +234,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row+1),x sta c64.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -246,20 +245,20 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y ; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char, ; a call to this subroutine with a string argument of just one char,
; by just one call to cbm.CHROUT of that single char. ; by just one call to c64.CHROUT of that single char.
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_B1),y - lda (P8ZP_SCRATCH_B1),y
beq + beq +
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ rts + rts
@ -273,11 +272,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
pha pha
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
jsr cbm.CHROUT jsr c64.CHROUT
txa txa
jsr cbm.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -293,16 +292,16 @@ _print_byte_digits
cpy #'0' cpy #'0'
beq + beq +
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
jsr cbm.CHROUT jsr c64.CHROUT
jmp _ones jmp _ones
+ pla + pla
cmp #'0' cmp #'0'
beq _ones beq _ones
jsr cbm.CHROUT jsr c64.CHROUT
_ones txa _ones txa
jsr cbm.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -316,45 +315,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
cmp #0 cmp #0
bpl + bpl +
lda #'-' lda #'-'
jsr cbm.CHROUT jsr c64.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
jmp print_ub._print_byte_digits jmp print_ub._print_byte_digits
}} }}
} }
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
bcc + bcc +
pha pha
lda #'$' lda #'$'
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
+ jsr conv.ubyte2hex + jsr conv.ubyte2hex
jsr cbm.CHROUT jsr c64.CHROUT
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
} }
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
bcc + bcc +
lda #'%' lda #'%'
jsr cbm.CHROUT jsr c64.CHROUT
+ ldy #8 + ldy #8
- lda #'0' - lda #'0'
asl P8ZP_SCRATCH_B1 asl P8ZP_SCRATCH_B1
bcc + bcc +
lda #'1' lda #'1'
+ jsr cbm.CHROUT + jsr c64.CHROUT
dey dey
bne - bne -
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
@ -362,7 +361,7 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -374,7 +373,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
@ -395,7 +394,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
beq + beq +
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ ldx P8ZP_SCRATCH_REG + ldx P8ZP_SCRATCH_REG
@ -418,14 +417,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
bne - bne -
_gotdigit _gotdigit
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
lda conv.uword2decimal.decTenThousands,y lda conv.uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
rts rts
_allzero _allzero
lda #'0' lda #'0'
jmp cbm.CHROUT jmp c64.CHROUT
}} }}
} }
@ -436,7 +435,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
bpl + bpl +
pha pha
lda #'-' lda #'-'
jsr cbm.CHROUT jsr c64.CHROUT
tya tya
eor #255 eor #255
tay tay
@ -458,7 +457,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0 ldy #0 ; char counter = 0
- jsr cbm.CHRIN - jsr c64.CHRIN
cmp #$0d ; return (ascii 13) pressed? cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end. beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
@ -589,7 +588,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
tax tax
clc clc
jsr cbm.PLOT jsr c64.PLOT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -598,7 +597,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
asmsub width() clobbers(X,Y) -> ubyte @A { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
jsr cbm.SCREEN jsr c64.SCREEN
txa txa
rts rts
}} }}
@ -607,7 +606,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
jsr cbm.SCREEN jsr c64.SCREEN
tya tya
rts rts
}} }}

View File

@ -5,7 +5,6 @@
floats { floats {
; ---- this block contains C-64 floating point related functions ---- ; ---- this block contains C-64 floating point related functions ----
%option no_symbol_prefixing
const float PI = 3.141592653589793 const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586 const float TWOPI = 6.283185307179586

View File

@ -144,19 +144,3 @@ func_all_f_stack .proc
jsr a_times_5 jsr a_times_5
jmp prog8_lib.func_all_b_stack jmp prog8_lib.func_all_b_stack
.pend .pend
func_abs_f_into_FAC1 .proc
stx P8ZP_SCRATCH_REG
jsr MOVFM
jsr ABS
ldx P8ZP_SCRATCH_REG
rts
.pend
func_sqrt_into_FAC1 .proc
stx P8ZP_SCRATCH_REG
jsr MOVFM
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
.pend

View File

@ -5,8 +5,6 @@
; assumes bitmap screen memory is $2000-$3fff ; assumes bitmap screen memory is $2000-$3fff
graphics { graphics {
%option no_symbol_prefixing
const uword BITMAP_ADDRESS = $2000 const uword BITMAP_ADDRESS = $2000
const uword WIDTH = 320 const uword WIDTH = 320
const ubyte HEIGHT = 200 const ubyte HEIGHT = 200

View File

@ -1,13 +1,7 @@
; Prog8 definitions for the Commodore-64 ; Prog8 definitions for the Commodore-64
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
c64 {
cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte &ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte &ubyte TIME_MID = $a1 ; .. mid byte
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec &ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
@ -56,93 +50,6 @@ cbm {
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
; ---- CBM ROM kernal routines (C64 addresses) ----
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
asmsub STOP2() -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{
txa
pha
jsr cbm.STOP
beq +
pla
tax
lda #0
rts
+ pla
tax
lda #1
rts
}}
}
asmsub RDTIM16() -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
%asm {{
stx P8ZP_SCRATCH_REG
jsr cbm.RDTIM
pha
txa
tay
pla
ldx P8ZP_SCRATCH_REG
rts
}}
}
}
c64 {
; C64 I/O registers (VIC, SID, CIA)
%option no_symbol_prefixing
; the default locations of the 8 sprite pointers (store address of sprite / 64) ; the default locations of the 8 sprite pointers (store address of sprite / 64)
&ubyte SPRPTR0 = 2040 &ubyte SPRPTR0 = 2040
&ubyte SPRPTR1 = 2041 &ubyte SPRPTR1 = 2041
@ -292,15 +199,93 @@ c64 {
; ---- end of SID registers ---- ; ---- end of SID registers ----
; ---- C64 ROM kernal routines ----
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
; ---- end of C64 ROM kernal routines ----
; ---- utilities -----
asmsub STOP2() -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{
txa
pha
jsr c64.STOP
beq +
pla
tax
lda #0
rts
+ pla
tax
lda #1
rts
}}
} }
sys { asmsub RDTIM16() -> uword @AY {
; ------- lowlevel system routines -------- ; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
pha
txa
tay
pla
ldx P8ZP_SCRATCH_REG
rts
}}
}
%option no_symbol_prefixing
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
; ---- C64 specific system utility routines: ----
asmsub init_system() { asmsub init_system() {
; Initializes the machine to a sane starting state. ; Initializes the machine to a sane starting state.
@ -316,13 +301,13 @@ asmsub init_system() {
sta $00 sta $00
lda #%00100111 lda #%00100111
sta $01 sta $01
jsr cbm.IOINIT jsr c64.IOINIT
jsr cbm.RESTOR jsr c64.RESTOR
jsr cbm.CINT jsr c64.CINT
lda #6 lda #6
sta c64.EXTCOL sta c64.EXTCOL
lda #7 lda #7
sta cbm.COLOR sta c64.COLOR
lda #0 lda #0
sta c64.BGCOL0 sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch jsr disable_runstop_and_charsetswitch
@ -342,7 +327,7 @@ asmsub init_system_phase2() {
asmsub cleanup_at_exit() { asmsub cleanup_at_exit() {
; executed when the main subroutine does rts ; executed when the main subroutine does rts
%asm {{ %asm {{
jmp sys.enable_runstop_and_charsetswitch jmp c64.enable_runstop_and_charsetswitch
}} }}
} }
@ -366,7 +351,7 @@ asmsub enable_runstop_and_charsetswitch() clobbers(A) {
}} }}
} }
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) { asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -375,9 +360,9 @@ asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
sta _use_kernal sta _use_kernal
sei sei
lda #<_irq_handler lda #<_irq_handler
sta cbm.CINV sta c64.CINV
lda #>_irq_handler lda #>_irq_handler
sta cbm.CINV+1 sta c64.CINV+1
cli cli
rts rts
_irq_handler jsr _irq_handler_init _irq_handler jsr _irq_handler_init
@ -395,7 +380,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp cbm.IRQDFRT ; continue with normal kernal irq routine + jmp c64.IRQDFRT ; continue with normal kernal irq routine
_use_kernal .byte 0 _use_kernal .byte 0
@ -447,10 +432,10 @@ IRQ_SCRATCH_ZPWORD2 .word 0
asmsub restore_irq() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
sei sei
lda #<cbm.IRQDFRT lda #<c64.IRQDFRT
sta cbm.CINV sta c64.CINV
lda #>cbm.IRQDFRT lda #>c64.IRQDFRT
sta cbm.CINV+1 sta c64.CINV+1
lda #0 lda #0
sta c64.IREQMASK ; disable raster irq sta c64.IREQMASK ; disable raster irq
lda #%10000001 lda #%10000001
@ -460,7 +445,7 @@ asmsub restore_irq() clobbers(A) {
}} }}
} }
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -472,9 +457,9 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc
sei sei
jsr _setup_raster_irq jsr _setup_raster_irq
lda #<_raster_irq_handler lda #<_raster_irq_handler
sta cbm.CINV sta c64.CINV
lda #>_raster_irq_handler lda #>_raster_irq_handler
sta cbm.CINV+1 sta c64.CINV+1
cli cli
rts rts
@ -482,8 +467,8 @@ _raster_irq_handler
jsr set_irq._irq_handler_init jsr set_irq._irq_handler_init
_modified jsr $ffff ; modified _modified jsr $ffff ; modified
jsr set_irq._irq_handler_end jsr set_irq._irq_handler_end
lda #$ff lda #$ff
sta c64.VICIRQ ; acknowledge raster irq sta c64.VICIRQ ; acknowledge raster irq
lda set_irq._use_kernal lda set_irq._use_kernal
bne + bne +
; end irq processing - don't use kernal's irq handling ; end irq processing - don't use kernal's irq handling
@ -493,7 +478,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp cbm.IRQDFRT ; continue with kernal irq routine + jmp c64.IRQDFRT ; continue with kernal irq routine
_setup_raster_irq _setup_raster_irq
pha pha
@ -517,14 +502,23 @@ _setup_raster_irq
}} }}
} }
; ---- end of C64 specific system utility routines ----
asmsub reset_system() { }
sys {
; ------- lowlevel system routines --------
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
%asm {{ %asm {{
sei sei
lda #14 lda #14
sta $01 ; bank the kernal in sta $01 ; bank the kernal in
jmp (cbm.RESET_VEC) jmp (c64.RESET_VEC)
}} }}
} }
@ -542,9 +536,9 @@ _loop lda P8ZP_SCRATCH_W1
ldx P8ZP_SCRATCH_B1 ldx P8ZP_SCRATCH_B1
rts rts
+ lda cbm.TIME_LO + lda c64.TIME_LO
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- lda cbm.TIME_LO - lda c64.TIME_LO
cmp P8ZP_SCRATCH_B1 cmp P8ZP_SCRATCH_B1
beq - beq -
@ -701,26 +695,13 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
lda #31 lda #31
sta $01 ; bank the kernal in sta $01 ; bank the kernal in
jsr cbm.CLRCHN ; reset i/o channels jsr c64.CLRCHN ; reset i/o channels
jsr sys.enable_runstop_and_charsetswitch jsr c64.enable_runstop_and_charsetswitch
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
txs txs
rts ; return to original caller rts ; return to original caller
@ -738,8 +719,6 @@ _longcopy
cx16 { cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the C64 as well but their location in memory is different ; they are simulated on the C64 as well but their location in memory is different
; (because there's no room for them in the zeropage) ; (because there's no room for them in the zeropage)
@ -847,31 +826,4 @@ cx16 {
&byte r13sH = $cf1b &byte r13sH = $cf1b
&byte r14sH = $cf1d &byte r14sH = $cf1d
&byte r15sH = $cf1f &byte r15sH = $cf1f
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
} }

View File

@ -6,8 +6,6 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25 const ubyte DEFAULT_HEIGHT = 25
@ -32,10 +30,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
sec sec
jsr cbm.PLOT jsr c64.PLOT
tay tay
clc clc
jmp cbm.PLOT jmp c64.PLOT
}} }}
} }
@ -59,10 +57,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
; (assumes screen matrix is at the default address) ; (assumes screen matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta cbm.Screen+250*0-1,y - sta c64.Screen+250*0-1,y
sta cbm.Screen+250*1-1,y sta c64.Screen+250*1-1,y
sta cbm.Screen+250*2-1,y sta c64.Screen+250*2-1,y
sta cbm.Screen+250*3-1,y sta c64.Screen+250*3-1,y
dey dey
bne - bne -
rts rts
@ -74,10 +72,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
; (assumes color matrix is at the default address) ; (assumes color matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta cbm.Colors+250*0-1,y - sta c64.Colors+250*0-1,y
sta cbm.Colors+250*1-1,y sta c64.Colors+250*1-1,y
sta cbm.Colors+250*2-1,y sta c64.Colors+250*2-1,y
sta cbm.Colors+250*3-1,y sta c64.Colors+250*3-1,y
dey dey
bne - bne -
rts rts
@ -85,7 +83,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
} }
sub color (ubyte txtcol) { sub color (ubyte txtcol) {
cbm.COLOR = txtcol c64.COLOR = txtcol
} }
sub lowercase() { sub lowercase() {
@ -96,7 +94,7 @@ sub uppercase() {
c64.VMCSB &= ~2 c64.VMCSB &= ~2
} }
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) { asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left ; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -110,10 +108,10 @@ asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 1,x lda c64.Screen + 40*row + 1,x
sta cbm.Screen + 40*row + 0,x sta c64.Screen + 40*row + 0,x
lda cbm.Colors + 40*row + 1,x lda c64.Colors + 40*row + 1,x
sta cbm.Colors + 40*row + 0,x sta c64.Colors + 40*row + 0,x
.next .next
inx inx
dey dey
@ -125,8 +123,8 @@ _scroll_screen ; scroll only the screen memory
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 1,x lda c64.Screen + 40*row + 1,x
sta cbm.Screen + 40*row + 0,x sta c64.Screen + 40*row + 0,x
.next .next
inx inx
dey dey
@ -137,7 +135,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -149,10 +147,10 @@ asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 0,x lda c64.Screen + 40*row + 0,x
sta cbm.Screen + 40*row + 1,x sta c64.Screen + 40*row + 1,x
lda cbm.Colors + 40*row + 0,x lda c64.Colors + 40*row + 0,x
sta cbm.Colors + 40*row + 1,x sta c64.Colors + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -162,8 +160,8 @@ _scroll_screen ; scroll only the screen memory
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda cbm.Screen + 40*row + 0,x lda c64.Screen + 40*row + 0,x
sta cbm.Screen + 40*row + 1,x sta c64.Screen + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -173,7 +171,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up ; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -185,10 +183,10 @@ asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row-1),x sta c64.Screen + 40*(row-1),x
lda cbm.Colors + 40*row,x lda c64.Colors + 40*row,x
sta cbm.Colors + 40*(row-1),x sta c64.Colors + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -198,8 +196,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row-1),x sta c64.Screen + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -209,7 +207,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) { asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down ; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -221,10 +219,10 @@ asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda cbm.Colors + 40*row,x lda c64.Colors + 40*row,x
sta cbm.Colors + 40*(row+1),x sta c64.Colors + 40*(row+1),x
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row+1),x sta c64.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -234,8 +232,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda cbm.Screen + 40*row,x lda c64.Screen + 40*row,x
sta cbm.Screen + 40*(row+1),x sta c64.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -245,20 +243,20 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y ; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char, ; a call to this subroutine with a string argument of just one char,
; by just one call to cbm.CHROUT of that single char. ; by just one call to c64.CHROUT of that single char.
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_B1),y - lda (P8ZP_SCRATCH_B1),y
beq + beq +
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ rts + rts
@ -272,11 +270,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
pha pha
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
jsr cbm.CHROUT jsr c64.CHROUT
txa txa
jsr cbm.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -292,16 +290,16 @@ _print_byte_digits
cpy #'0' cpy #'0'
beq + beq +
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
jsr cbm.CHROUT jsr c64.CHROUT
jmp _ones jmp _ones
+ pla + pla
cmp #'0' cmp #'0'
beq _ones beq _ones
jsr cbm.CHROUT jsr c64.CHROUT
_ones txa _ones txa
jsr cbm.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -315,45 +313,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
cmp #0 cmp #0
bpl + bpl +
lda #'-' lda #'-'
jsr cbm.CHROUT jsr c64.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
jmp print_ub._print_byte_digits jmp print_ub._print_byte_digits
}} }}
} }
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
bcc + bcc +
pha pha
lda #'$' lda #'$'
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
+ jsr conv.ubyte2hex + jsr conv.ubyte2hex
jsr cbm.CHROUT jsr c64.CHROUT
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
} }
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
bcc + bcc +
lda #'%' lda #'%'
jsr cbm.CHROUT jsr c64.CHROUT
+ ldy #8 + ldy #8
- lda #'0' - lda #'0'
asl P8ZP_SCRATCH_B1 asl P8ZP_SCRATCH_B1
bcc + bcc +
lda #'1' lda #'1'
+ jsr cbm.CHROUT + jsr c64.CHROUT
dey dey
bne - bne -
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
@ -361,7 +359,7 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -373,7 +371,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
@ -394,7 +392,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
beq + beq +
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ ldx P8ZP_SCRATCH_REG + ldx P8ZP_SCRATCH_REG
@ -417,14 +415,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
bne - bne -
_gotdigit _gotdigit
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
lda conv.uword2decimal.decTenThousands,y lda conv.uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
rts rts
_allzero _allzero
lda #'0' lda #'0'
jmp cbm.CHROUT jmp c64.CHROUT
}} }}
} }
@ -435,7 +433,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
bpl + bpl +
pha pha
lda #'-' lda #'-'
jsr cbm.CHROUT jsr c64.CHROUT
tya tya
eor #255 eor #255
tay tay
@ -457,7 +455,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0 ldy #0 ; char counter = 0
- jsr cbm.CHRIN - jsr c64.CHRIN
cmp #$0d ; return (ascii 13) pressed? cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end. beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
@ -588,7 +586,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
tax tax
clc clc
jsr cbm.PLOT jsr c64.PLOT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -597,7 +595,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
asmsub width() clobbers(X,Y) -> ubyte @A { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
jsr cbm.SCREEN jsr c64.SCREEN
txa txa
rts rts
}} }}
@ -606,7 +604,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
jsr cbm.SCREEN jsr c64.SCREEN
tya tya
rts rts
}} }}

View File

@ -2,8 +2,6 @@
conv { conv {
%option no_symbol_prefixing
; ----- number conversions to decimal strings ---- ; ----- number conversions to decimal strings ----
str @shared string_out = "????????????????" ; result buffer for the string conversion routines str @shared string_out = "????????????????" ; result buffer for the string conversion routines
@ -508,7 +506,8 @@ asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X) ; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{ %asm {{
ldy #uword2decimal.ASCII_0_OFFSET ldy #uword2decimal.ASCII_0_OFFSET
jmp uword2decimal.hex_try200 bne uword2decimal.hex_try200
rts
}} }}
} }

View File

@ -0,0 +1,234 @@
; Cx16 specific disk drive I/O routines.
%import diskio
%import string
cx16diskio {
; Same as diskio.load() but with additional bank parameter to select the Ram bank to load into.
; Use kernal LOAD routine to load the given program file in memory.
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred.
; You can use the load_size() function to calcuate the size of the file that was loaded.
sub load(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
cx16.rambank(bank)
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, false)
}
; Same as diskio.load_raw() but with additional bank parameter to select the Ram bank to load into.
; Use kernal LOAD routine to load the given file in memory.
; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
; The load address is mandatory. Returns the number of bytes loaded.
; If you load into regular system ram, use cx16.getrambank() for the bank argument,
; or alternatively make sure to reset the correct ram bank yourself after the load!
; Returns the end load address+1 if successful or 0 if a load error occurred.
; You can use the load_size() function to calcuate the size of the file that was loaded.
sub load_raw(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
cx16.rambank(bank)
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, true)
}
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
}
asmsub vload(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command VLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file has to have the usual 2 byte header (which will be skipped)
%asm {{
clc
internal_vload:
phx
pha
tya
tax
bcc +
ldy #%00000010 ; headerless load mode
bne ++
+ ldy #0 ; normal load mode
+ lda #1
jsr c64.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr c64.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr c64.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ jsr c64.CLRCHN
lda #1
jsr c64.CLOSE
plx
lda P8ZP_SCRATCH_B1
rts
}}
}
asmsub vload_raw(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file is read fully including the first two bytes.
%asm {{
sec
jmp vload.internal_vload
}}
}
; Replacement function that makes use of fast block read capability of the X16,
; and can wrap over multiple ram banks while reading.
; Use this in place of regular diskio.f_read() on X16.
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
if not diskio.iteration_in_progress or not num_bytes
return 0
diskio.list_blocks = 0 ; we reuse this variable for the total number of bytes read
; commander X16 supports fast block-read via macptr() kernal call
uword size
while num_bytes {
size = 255
if num_bytes<size
size = num_bytes
size = cx16.macptr(lsb(size), bufferpointer, false)
if_cs
goto byte_read_loop ; macptr block read not supported, do fallback loop
diskio.list_blocks += size
bufferpointer += size
if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
num_bytes -= size
if c64.READST() & $40 {
diskio.f_close() ; end of file, close it
break
}
}
return diskio.list_blocks ; number of bytes read
byte_read_loop: ; fallback if macptr() isn't supported on the device
%asm {{
lda bufferpointer
sta m_in_buffer+1
lda bufferpointer+1
sta m_in_buffer+2
}}
while num_bytes {
if c64.READST() {
diskio.f_close()
if c64.READST() & $40 ; eof?
return diskio.list_blocks ; number of bytes read
return 0 ; error.
}
%asm {{
jsr c64.CHRIN
m_in_buffer sta $ffff
inc m_in_buffer+1
bne +
inc m_in_buffer+2
+
}}
diskio.list_blocks++
num_bytes--
}
return diskio.list_blocks ; number of bytes read
}
; replacement function that makes use of fast block read capability of the X16
; use this in place of regular diskio.f_read_all() on X16
sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read.
if not diskio.iteration_in_progress
return 0
uword total_read = 0
while not c64.READST() {
cx16.r0 = cx16diskio.f_read(bufferpointer, 256)
total_read += cx16.r0
bufferpointer += cx16.r0
}
return total_read
}
sub chdir(ubyte drivenumber, str path) {
; -- change current directory.
diskio.list_filename[0] = 'c'
diskio.list_filename[1] = 'd'
diskio.list_filename[2] = ':'
void string.copy(path, &diskio.list_filename+3)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub mkdir(ubyte drivenumber, str name) {
; -- make a new subdirectory.
diskio.list_filename[0] = 'm'
diskio.list_filename[1] = 'd'
diskio.list_filename[2] = ':'
void string.copy(name, &diskio.list_filename+3)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub rmdir(ubyte drivenumber, str name) {
; -- remove a subdirectory.
void string.find(name, '*')
if_cs
return ; refuse to act on a wildcard *
diskio.list_filename[0] = 'r'
diskio.list_filename[1] = 'd'
diskio.list_filename[2] = ':'
void string.copy(name, &diskio.list_filename+3)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub relabel(ubyte drivenumber, str name) {
; -- change the disk label.
diskio.list_filename[0] = 'r'
diskio.list_filename[1] = '-'
diskio.list_filename[2] = 'h'
diskio.list_filename[3] = ':'
void string.copy(name, &diskio.list_filename+4)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub f_seek(uword pos_hiword, uword pos_loword) {
; -- seek in the reading file opened with f_open, to the given 32-bits position
ubyte[6] command = ['p',0,0,0,0,0]
command[1] = 12 ; f_open uses channel 12
command[2] = lsb(pos_loword)
command[3] = msb(pos_loword)
command[4] = lsb(pos_hiword)
command[5] = msb(pos_hiword)
send_command:
c64.SETNAM(sizeof(command), &command)
c64.SETLFS(15, diskio.last_drivenumber, 15)
void c64.OPEN()
c64.CLOSE(15)
}
; TODO see if we can get this to work as well:
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
; cx16diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
; cx16diskio.f_seek.command[2] = lsb(pos_loword)
; cx16diskio.f_seek.command[3] = msb(pos_loword)
; cx16diskio.f_seek.command[4] = lsb(pos_hiword)
; cx16diskio.f_seek.command[5] = msb(pos_hiword)
; goto cx16diskio.f_seek.send_command
; }
}

View File

@ -1,797 +0,0 @@
; Commander X16 disk drive I/O routines.
; Largely compatible with the default C64 ones, but adds more stuff specific to the X16 as well.
%import textio
%import string
%import syslib
diskio {
%option no_symbol_prefixing
ubyte drivenumber = 8
sub set_drive(ubyte number) {
drivenumber = number
}
sub directory() -> bool {
; -- Prints the directory contents to the screen. Returns success.
cbm.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0)
ubyte status = 1
void cbm.OPEN() ; open 12,8,0,"$"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
}
; while not stop key pressed / EOF encountered, read data.
status = cbm.READST()
if status!=0 {
status = 1
goto io_error
}
while status==0 {
ubyte low = cbm.CHRIN()
ubyte high = cbm.CHRIN()
txt.print_uw(mkword(high, low))
txt.spc()
ubyte @zp char
repeat {
char = cbm.CHRIN()
if char==0
break
txt.chrout(char)
}
txt.nl()
void cbm.CHRIN() ; skip 2 bytes
void cbm.CHRIN()
status = cbm.READST()
if cbm.STOP2()
break
}
status = cbm.READST()
io_error:
cbm.CLRCHN() ; restore default i/o devices
cbm.CLOSE(12)
if status and status & $40 == 0 { ; bit 6=end of file
txt.print("\ni/o error, status: ")
txt.print_ub(status)
txt.nl()
return false
}
return true
}
sub diskname() -> uword {
; returns disk label name or 0 if error
cbm.SETNAM(3, "$")
cbm.SETLFS(12, diskio.drivenumber, 0)
ubyte status = 1
void cbm.OPEN() ; open 12,8,0,"$=c"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
while cbm.CHRIN()!='"' {
; skip up to entry name
}
cx16.r0 = &diskio.list_filename
repeat {
@(cx16.r0) = cbm.CHRIN()
if @(cx16.r0)=='"' {
@(cx16.r0) = ' '
while @(cx16.r0)==' ' and cx16.r0>=&diskio.list_filename {
@(cx16.r0) = 0
cx16.r0--
}
break
}
cx16.r0++
}
status = cbm.READST()
io_error:
cbm.CLRCHN()
cbm.CLOSE(12)
if status and status & $40 == 0
return 0
return diskio.list_filename
}
; internal variables for the iterative file lister / loader
bool list_skip_disk_name
uword list_pattern
uword list_blocks
bool iteration_in_progress = false
str list_filetype = "???" ; prg, seq, dir
str list_filename = "?" * 50
; ----- get a list of files (uses iteration functions internally) -----
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
; After the last filename one additional 0 byte is placed to indicate the end of the list.
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
uword buffer_start = filenames_buffer
ubyte files_found = 0
if lf_start_list(pattern_ptr) {
while lf_next_entry() {
if list_filetype!="dir" {
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
files_found++
if filenames_buffer - buffer_start > filenames_buf_size-20 {
@(filenames_buffer)=0
lf_end_list()
sys.set_carry()
return files_found
}
}
}
lf_end_list()
}
@(filenames_buffer)=0
sys.clear_carry()
return files_found
}
; ----- iterative file lister functions (uses io channel 12) -----
sub lf_start_list(uword pattern_ptr) -> bool {
; -- start an iterative file listing with optional pattern matching.
; note: only a single iteration loop can be active at a time!
lf_end_list()
list_pattern = pattern_ptr
list_skip_disk_name = true
iteration_in_progress = true
cbm.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0)
void cbm.OPEN() ; open 12,8,0,"$"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
}
if cbm.READST()==0
return true
io_error:
lf_end_list()
return false
}
sub lf_next_entry() -> bool {
; -- retrieve the next entry from an iterative file listing session.
; results will be found in list_blocks, list_filename, and list_filetype.
; if it returns false though, there are no more entries (or an error occurred).
if not iteration_in_progress
return false
repeat {
void cbm.CHKIN(12) ; use #12 as input channel again
uword nameptr = &list_filename
ubyte blocks_lsb = cbm.CHRIN()
ubyte blocks_msb = cbm.CHRIN()
if cbm.READST()
goto close_end
list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first "
while cbm.CHRIN()!='\"' {
if cbm.READST()
goto close_end
}
; read the filename
repeat {
ubyte char = cbm.CHRIN()
if char==0
break
if char=='\"'
break
@(nameptr) = char
nameptr++
}
@(nameptr) = 0
do {
cx16.r15L = cbm.CHRIN()
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
list_filetype[0] = cx16.r15L
list_filetype[1] = cbm.CHRIN()
list_filetype[2] = cbm.CHRIN()
while cbm.CHRIN() {
; read the rest of the entry until the end
}
void cbm.CHRIN() ; skip 2 bytes
void cbm.CHRIN()
if not list_skip_disk_name {
if not list_pattern
return true
if string.pattern_match(list_filename, list_pattern)
return true
}
list_skip_disk_name = false
}
close_end:
lf_end_list()
return false
}
sub lf_end_list() {
; -- end an iterative file listing session (close channels).
if iteration_in_progress {
cbm.CLRCHN()
cbm.CLOSE(12)
iteration_in_progress = false
}
}
; ----- iterative file loader functions (uses io channel 12) -----
sub f_open(uword filenameptr) -> bool {
; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time!
f_close()
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
void cbm.OPEN() ; open 12,8,12,"filename"
if_cc {
if cbm.READST()==0 {
iteration_in_progress = true
void cbm.CHKIN(12) ; use #12 as input channel
if_cc {
void cbm.CHRIN() ; read first byte to test for file not found
if not cbm.READST() {
cbm.CLOSE(12) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file
void cbm.CHKIN(12)
return true
}
}
}
}
f_close()
return false
}
; optimized for Commander X16 to use MACPTR block read kernal call
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
if not iteration_in_progress or not num_bytes
return 0
list_blocks = 0 ; we reuse this variable for the total number of bytes read
; commander X16 supports fast block-read via macptr() kernal call
uword size
while num_bytes {
size = 255
if num_bytes<size
size = num_bytes
size = cx16.macptr(lsb(size), bufferpointer, false)
if_cs
goto byte_read_loop ; macptr block read not supported, do fallback loop
list_blocks += size
bufferpointer += size
if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
num_bytes -= size
if cbm.READST() & $40 {
f_close() ; end of file, close it
break
}
}
return list_blocks ; number of bytes read
byte_read_loop: ; fallback if macptr() isn't supported on the device
%asm {{
lda bufferpointer
sta m_in_buffer+1
lda bufferpointer+1
sta m_in_buffer+2
}}
while num_bytes {
if cbm.READST() {
f_close()
if cbm.READST() & $40 ; eof?
return list_blocks ; number of bytes read
return 0 ; error.
}
%asm {{
jsr cbm.CHRIN
m_in_buffer sta $ffff
inc m_in_buffer+1
bne +
inc m_in_buffer+2
+
}}
list_blocks++
num_bytes--
}
return list_blocks ; number of bytes read
}
; optimized for Commander X16 to use MACPTR block read kernal call
sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read.
if not iteration_in_progress
return 0
uword total_read = 0
while not cbm.READST() {
cx16.r0 = f_read(bufferpointer, 256)
total_read += cx16.r0
bufferpointer += cx16.r0
}
return total_read
}
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
; Routine to read text lines from a text file. Lines must be less than 255 characters.
; Reads characters from the input file UNTIL a newline or return character (or EOF).
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
; I/O error status should be checked by the caller itself via READST() routine.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldx #12
jsr cbm.CHKIN ; use channel 12 again for input
ldy #0
_loop jsr cbm.CHRIN
sta (P8ZP_SCRATCH_W1),y
beq _end
iny
cmp #$0a
beq _line_end
cmp #$0d
bne _loop
_line_end dey ; get rid of the trailing end-of-line char
lda #0
sta (P8ZP_SCRATCH_W1),y
_end rts
}}
}
sub f_close() {
; -- end an iterative file loading session (close channels).
if iteration_in_progress {
cbm.CLRCHN()
cbm.CLOSE(12)
iteration_in_progress = false
}
}
; ----- iterative file writing functions (uses io channel 13) -----
sub f_open_w(uword filenameptr) -> bool {
; -- open a file for iterative writing with f_write
f_close_w()
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(13, drivenumber, 1)
void cbm.OPEN() ; open 13,8,1,"filename"
if_cc {
cbm.CHKOUT(13) ; use #13 as output channel
return not cbm.READST()
}
f_close_w()
return false
}
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
; -- write the given number of bytes to the currently open file
if num_bytes!=0 {
cbm.CHKOUT(13) ; use #13 as output channel again
repeat num_bytes {
cbm.CHROUT(@(bufferpointer))
bufferpointer++
}
return not cbm.READST()
}
return true
}
sub f_close_w() {
; -- end an iterative file writing session (close channels).
cbm.CLRCHN()
cbm.CLOSE(13)
}
; ---- other functions ----
sub status() -> uword {
; -- retrieve the disk drive's current status message
uword messageptr = &list_filename
cbm.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN() ; open 15,8,15
if_cs
goto io_error
void cbm.CHKIN(15) ; use #15 as input channel
if_cs
goto io_error
while not cbm.READST() {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
@(messageptr) = cx16.r5L
messageptr++
}
@(messageptr) = 0
done:
cbm.CLRCHN() ; restore default i/o devices
cbm.CLOSE(15)
return list_filename
io_error:
list_filename = "?disk error"
goto done
}
; saves a block of memory to disk, including the default 2 byte prg header.
sub save(uword filenameptr, uword address, uword size) -> bool {
return internal_save_routine(filenameptr, address, size, false)
}
; like save() but omits the 2 byte prg header.
sub save_raw(uword filenameptr, uword address, uword size) -> bool {
return internal_save_routine(filenameptr, address, size, true)
}
sub internal_save_routine(uword filenameptr, uword address, uword size, bool headerless) -> bool {
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(1, drivenumber, 0)
uword @shared end_address = address + size
cx16.r0L = 0
%asm {{
lda address
sta P8ZP_SCRATCH_W1
lda address+1
sta P8ZP_SCRATCH_W1+1
stx P8ZP_SCRATCH_REG
ldx end_address
ldy end_address+1
lda headerless
beq +
lda #<P8ZP_SCRATCH_W1
jsr cx16.BSAVE
bra ++
+ lda #<P8ZP_SCRATCH_W1
jsr cbm.SAVE
+ php
ldx P8ZP_SCRATCH_REG
plp
}}
if_cc
cx16.r0L = cbm.READST()==0
cbm.CLRCHN()
cbm.CLOSE(1)
return cx16.r0L
}
; Use kernal LOAD routine to load the given program file in memory.
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred.
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
; (which is possible on the Commander X16), the returned size is not correct,
; because it doesn't take the number of ram banks into account.
; You can use the load_size() function to calcuate the size in this case.
; NOTE: data is read into the current Ram bank if you're reading into banked ram.
; if you require loading into another ram bank, you have to set that
; yourself using cx16.rambank(bank) before calling load().
sub load(uword filenameptr, uword address_override) -> uword {
return internal_load_routine(filenameptr, address_override, false)
}
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
; No program header is assumed in the file. Everything is loaded.
; See comments on load() for more details.
sub load_raw(uword filenameptr, uword address) -> uword {
return internal_load_routine(filenameptr, address, true)
}
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
cbm.SETNAM(string.length(filenameptr), filenameptr)
ubyte secondary = 1
cx16.r1 = 0
if address_override
secondary = 0
if headerless
secondary |= %00000010 ; activate cx16 kernal headerless load support
cbm.SETLFS(1, drivenumber, secondary)
%asm {{
stx P8ZP_SCRATCH_REG
lda #0
ldx address_override
ldy address_override+1
jsr cbm.LOAD
bcs +
stx cx16.r1
sty cx16.r1+1
+ ldx P8ZP_SCRATCH_REG
}}
cbm.CLRCHN()
cbm.CLOSE(1)
return cx16.r1
}
sub delete(uword filenameptr) {
; -- delete a file on the drive
list_filename[0] = 's'
list_filename[1] = ':'
ubyte flen = string.copy(filenameptr, &list_filename+2)
cbm.SETNAM(flen+2, list_filename)
cbm.SETLFS(1, drivenumber, 15)
void cbm.OPEN()
cbm.CLRCHN()
cbm.CLOSE(1)
}
sub rename(uword oldfileptr, uword newfileptr) {
; -- rename a file on the drive
list_filename[0] = 'r'
list_filename[1] = ':'
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
list_filename[flen_new+2] = '='
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
cbm.SETNAM(3+flen_new+flen_old, list_filename)
cbm.SETLFS(1, drivenumber, 15)
void cbm.OPEN()
cbm.CLRCHN()
cbm.CLOSE(1)
}
sub send_command(uword commandptr) {
; -- send a dos command to the drive
cbm.SETNAM(string.length(commandptr), commandptr)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
cbm.CLRCHN()
cbm.CLOSE(15)
}
; CommanderX16 extensions over the basic C64/C128 diskio routines:
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
}
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command VLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file has to have the usual 2 byte header (which will be skipped)
%asm {{
clc
internal_vload:
phx
pha
ldx drivenumber
bcc +
ldy #%00000010 ; headerless load mode
bne ++
+ ldy #0 ; normal load mode
+ lda #1
jsr cbm.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr cbm.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr cbm.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ jsr cbm.CLRCHN
lda #1
jsr cbm.CLOSE
plx
lda P8ZP_SCRATCH_B1
rts
}}
}
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file is read fully including the first two bytes.
%asm {{
sec
jmp vload.internal_vload
}}
}
sub chdir(str path) {
; -- change current directory.
list_filename[0] = 'c'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(path, &list_filename+3)
send_command(list_filename)
}
sub mkdir(str name) {
; -- make a new subdirectory.
list_filename[0] = 'm'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(name, &list_filename+3)
send_command(list_filename)
}
sub rmdir(str name) {
; -- remove a subdirectory.
void string.find(name, '*')
if_cs
return ; refuse to act on a wildcard *
list_filename[0] = 'r'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(name, &list_filename+3)
send_command(list_filename)
}
sub curdir() -> uword {
; return current directory name or 0 if error
; special X16 dos command to only return the current path in the entry list (R42+)
const ubyte MAX_PATH_LEN=80
uword reversebuffer = memory("curdir_buffer", MAX_PATH_LEN, 0)
cx16.r12 = reversebuffer + MAX_PATH_LEN-1
@(cx16.r12)=0
cbm.SETNAM(3, "$=c")
cbm.SETLFS(12, diskio.drivenumber, 0)
void cbm.OPEN() ; open 12,8,0,"$=c"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 6 {
void cbm.CHRIN()
}
while cbm.CHRIN() {
; skip first line (drive label)
}
while cbm.CHRIN()!='"' {
; skip to first name
}
ubyte status = cbm.READST()
cx16.r10 = &list_filename
while status==0 {
repeat {
@(cx16.r10) = cbm.CHRIN()
if @(cx16.r10)==0
break
cx16.r10++
}
while @(cx16.r10)!='"' and cx16.r10>=&list_filename {
@(cx16.r10)=0
cx16.r10--
}
@(cx16.r10)=0
prepend(list_filename)
cx16.r10 = &list_filename
while cbm.CHRIN()!='"' and status==0 {
status = cbm.READST()
; skipping up to next entry name
}
}
io_error:
cbm.CLRCHN()
cbm.CLOSE(12)
if status and status & $40 == 0
return 0
if @(cx16.r12)==0 {
cx16.r12--
@(cx16.r12)='/'
}
return cx16.r12
sub prepend(str dir) {
if dir[0]=='/' and dir[1]==0
return
cx16.r9L = string.length(dir)
cx16.r12 -= cx16.r9L
sys.memcopy(dir, cx16.r12, cx16.r9L)
cx16.r12--
@(cx16.r12)='/'
}
}
sub relabel(str name) {
; -- change the disk label.
list_filename[0] = 'r'
list_filename[1] = '-'
list_filename[2] = 'h'
list_filename[3] = ':'
void string.copy(name, &list_filename+4)
send_command(list_filename)
}
sub f_seek(uword pos_hiword, uword pos_loword) {
; -- seek in the reading file opened with f_open, to the given 32-bits position
ubyte[6] command = ['p',0,0,0,0,0]
command[1] = 12 ; f_open uses channel 12
command[2] = lsb(pos_loword)
command[3] = msb(pos_loword)
command[4] = lsb(pos_hiword)
command[5] = msb(pos_hiword)
send_command:
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
cbm.CLOSE(15)
}
; NOTE: f_seek_w() doesn't work reliably right now. I only manage to corrupt the fat32 filesystem on the sdcard with it...
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
; diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
; diskio.f_seek.command[2] = lsb(pos_loword)
; diskio.f_seek.command[3] = msb(pos_loword)
; diskio.f_seek.command[4] = lsb(pos_hiword)
; diskio.f_seek.command[5] = msb(pos_hiword)
; goto diskio.f_seek.send_command
; }
}

View File

@ -6,8 +6,6 @@
floats { floats {
; ---- this block contains C-64 compatible floating point related functions ---- ; ---- this block contains C-64 compatible floating point related functions ----
%option no_symbol_prefixing
const float PI = 3.141592653589793 const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586 const float TWOPI = 6.283185307179586

View File

@ -13,19 +13,16 @@
; SCREEN MODE LIST: ; SCREEN MODE LIST:
; mode 0 = reset back to default text mode ; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 monochrome ; mode 1 = bitmap 320 x 240 monochrome
; mode 2 = bitmap 320 x 240 x 4c (not yet implemented: just use 256c, there's enough vram for that) ; mode 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
; mode 3 = bitmap 320 x 240 x 16c (not yet implemented: just use 256c, there's enough vram for that) ; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
; mode 4 = bitmap 320 x 240 x 256c (like SCREEN $80 but using this api instead of kernal) ; mode 4 = bitmap 320 x 240 x 256c (like SCREEN $80 but using this api instead of kernal)
; mode 5 = bitmap 640 x 480 monochrome ; mode 5 = bitmap 640 x 480 monochrome
; mode 6 = bitmap 640 x 480 x 4c ; mode 6 = bitmap 640 x 480 x 4c
; higher color dephts in highres are not supported due to lack of VRAM ; higher color dephts in highres are not supported due to lack of VRAM
; TODO remove the phx/plx pairs in non-stack compiler version
gfx2 { gfx2 {
%option no_symbol_prefixing
; read-only control variables: ; read-only control variables:
ubyte active_mode = 0 ubyte active_mode = 0
uword width = 0 uword width = 0
@ -47,7 +44,7 @@ gfx2 {
height = 240 height = 240
bpp = 1 bpp = 1
} }
; TODO modes 2, 3 ; TODO modes 2, 3 not yet implemented
4 -> { 4 -> {
; lores 256c ; lores 256c
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1 cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
@ -87,7 +84,7 @@ gfx2 {
else -> { else -> {
; back to default text mode ; back to default text mode
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
cbm.CINT() c64.CINT()
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
width = 0 width = 0
height = 0 height = 0
@ -110,7 +107,7 @@ gfx2 {
repeat 240/2/8 repeat 240/2/8
cs_innerloop640() cs_innerloop640()
} }
; TODO modes 2, 3 ; TODO mode 2, 3
4 -> { 4 -> {
; lores 256c ; lores 256c
repeat 240/2 repeat 240/2
@ -158,9 +155,6 @@ gfx2 {
} }
sub horizontal_line(uword x, uword y, uword length, ubyte color) { sub horizontal_line(uword x, uword y, uword length, ubyte color) {
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
ubyte[9] masked_starts = [ 0, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111]
if length==0 if length==0
return return
when active_mode { when active_mode {
@ -169,13 +163,12 @@ gfx2 {
ubyte separate_pixels = (8-lsb(x)) & 7 ubyte separate_pixels = (8-lsb(x)) & 7
if separate_pixels as uword > length if separate_pixels as uword > length
separate_pixels = lsb(length) separate_pixels = lsb(length)
if separate_pixels { repeat separate_pixels {
position(x,y) ; TODO optimize this by writing a masked byte in 1 go
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off plot(x, y, color)
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_starts[separate_pixels] x++
length -= separate_pixels
x += separate_pixels
} }
length -= separate_pixels
if length { if length {
position(x, y) position(x, y)
separate_pixels = lsb(length) & 7 separate_pixels = lsb(length) & 7
@ -212,9 +205,11 @@ _loop lda length
bra _loop bra _loop
_done _done
}} }}
repeat separate_pixels {
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off ; TODO optimize this by writing a masked byte in 1 go
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_ends[separate_pixels] plot(x, y, color)
x++
}
} }
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
} }
@ -241,7 +236,8 @@ _done
}} }}
} }
6 -> { 6 -> {
; highres 4c ....also mostly usable for mode 2, lores 4c? ; highres 4c
; TODO also mostly usable for lores 4c?
color &= 3 color &= 3
ubyte[4] colorbits ubyte[4] colorbits
ubyte ii ubyte ii
@ -370,14 +366,8 @@ _done
return return
position2(x,y,true) position2(x,y,true)
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
;; color &= 3 color &= 3
;; color <<= gfx2.plot.shift4c[lsb(x) & 3] color <<= gfx2.plot.shift4c[lsb(x) & 3] ; TODO with lookup table
cx16.r2L = lsb(x) & 3
when color & 3 {
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
}
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3] ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
repeat lheight { repeat lheight {
%asm {{ %asm {{
@ -557,13 +547,10 @@ _done
} }
} }
sub plot(uword @zp x, uword @zp y, ubyte @zp color) { sub plot(uword @zp x, uword y, ubyte color) {
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1] ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100] ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
ubyte[4] @shared shift4c = [6,4,2,0] ubyte[4] @shared shift4c = [6,4,2,0]
ubyte[4] shiftedleft_4c_1 = [1<<6, 1<<4, 1<<2, 1<<0]
ubyte[4] shiftedleft_4c_2 = [2<<6, 2<<4, 2<<2, 2<<0]
ubyte[4] shiftedleft_4c_3 = [3<<6, 3<<4, 3<<2, 3<<0]
when active_mode { when active_mode {
1 -> { 1 -> {
@ -600,7 +587,7 @@ _done
}} }}
} }
} }
; TODO modes 2, 3 ; TODO mode 2,3
4 -> { 4 -> {
; lores 256c ; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@ -652,16 +639,12 @@ _done
} }
} }
6 -> { 6 -> {
; highres 4c ....also mostly usable for mode 2, lores 4c? ; highres 4c
; TODO also mostly usable for lores 4c?
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = lsb(x) & 3 ; xbits cx16.r2L = lsb(x) & 3 ; xbits
; color &= 3 color &= 3
; color <<= shift4c[cx16.r2L] color <<= shift4c[cx16.r2L] ; TODO with lookup table
when color & 3 {
1 -> color = shiftedleft_4c_1[cx16.r2L]
2 -> color = shiftedleft_4c_2[cx16.r2L]
3 -> color = shiftedleft_4c_3[cx16.r2L]
}
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
lda cx16.r1L lda cx16.r1L
@ -706,7 +689,7 @@ _done
+ +
}} }}
} }
; TODO modes 2, 3 ; TODO mode 2 and 3
4 -> { 4 -> {
; lores 256c ; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@ -760,138 +743,27 @@ _done
sta cx16.r0L sta cx16.r0L
}} }}
cx16.r1L = lsb(x) & 3 cx16.r1L = lsb(x) & 3
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L] cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L] ; TODO with lookup table
return cx16.r0L & 3 return cx16.r0L & 3
} }
else -> return 0 else -> return 0
} }
} }
sub fill(word @zp x, word @zp y, ubyte new_color) {
; Non-recursive scanline flood fill.
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
const ubyte MAXDEPTH = 48
word[MAXDEPTH] @split @shared stack_xl
word[MAXDEPTH] @split @shared stack_xr
word[MAXDEPTH] @split @shared stack_y
byte[MAXDEPTH] @shared stack_dy
cx16.r12L = 0 ; stack pointer
word x1
word x2
byte dy
cx16.r10L = new_color
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
if cx16.r12L==MAXDEPTH
return
cx16.r0s = sy+sdy
if cx16.r0s>=0 and cx16.r0s<=height-1 {
;; stack_xl[cx16.r12L] = sxl
;; stack_xr[cx16.r12L] = sxr
;; stack_y[cx16.r12L] = sy
;; stack_dy[cx16.r12L] = sdy
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda sxl
sta stack_xl_lsb,y
lda sxl+1
sta stack_xl_msb,y
lda sxr
sta stack_xr_lsb,y
lda sxr+1
sta stack_xr_msb,y
lda sy
sta stack_y_lsb,y
lda sy+1
sta stack_y_msb,y
ldy cx16.r12L
lda sdy
sta stack_dy,y
inc cx16.r12L
}}
}
}
sub pop_stack() {
;; cx16.r12L--
;; x1 = stack_xl[cx16.r12L]
;; x2 = stack_xr[cx16.r12L]
;; y = stack_y[cx16.r12L]
;; dy = stack_dy[cx16.r12L]
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda stack_xl_lsb,y
sta x1
lda stack_xl_msb,y
sta x1+1
lda stack_xr_lsb,y
sta x2
lda stack_xr_msb,y
sta x2+1
lda stack_y_lsb,y
sta y
lda stack_y_msb,y
sta y+1
ldy cx16.r12L
lda stack_dy,y
sta dy
}}
y+=dy
}
cx16.r11L = pget(x as uword, y as uword) ; old_color
if cx16.r11L == cx16.r10L
return
if x<0 or x > width-1 or y<0 or y > height-1
return
push_stack(x, x, y, 1)
push_stack(x, x, y + 1, -1)
word left = 0
while cx16.r12L {
pop_stack()
x = x1
while x >= 0 and pget(x as uword, y as uword) == cx16.r11L {
plot(x as uword, y as uword, cx16.r10L)
x--
}
if x>= x1
goto skip
left = x + 1
if left < x1
push_stack(left, x1 - 1, y, -dy)
x = x1 + 1
do {
while x <= width-1 and pget(x as uword, y as uword) == cx16.r11L {
plot(x as uword, y as uword, cx16.r10L)
x++
}
push_stack(left, x - 1, y, dy)
if x > x2 + 1
push_stack(x2 + 1, x - 1, y, -dy)
skip:
x++
while x <= x2 and pget(x as uword, y as uword) != cx16.r11L
x++
left = x
} until x>x2
}
}
sub position(uword @zp x, uword y) { sub position(uword @zp x, uword y) {
ubyte bank
when active_mode { when active_mode {
1 -> { 1 -> {
; lores monochrome ; lores monochrome
cx16.r0 = y*(320/8) + x/8 cx16.r0 = y*(320/8) + x/8
cx16.vaddr(0, cx16.r0, 0, 1) cx16.vaddr(0, cx16.r0, 0, 1)
} }
; TODO modes 2, 3 ; TODO modes 2,3
4 -> { 4 -> {
; lores 256c ; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = cx16.r1L bank = lsb(cx16.r1)
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1) cx16.vaddr(bank, cx16.r0, 0, 1)
} }
5 -> { 5 -> {
; highres monochrome ; highres monochrome
@ -901,16 +773,24 @@ skip:
6 -> { 6 -> {
; highres 4c ; highres 4c
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = cx16.r1L bank = lsb(cx16.r1)
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1) cx16.vaddr(bank, cx16.r0, 0, 1)
} }
} }
} }
sub position2(uword @zp x, uword y, bool also_port_1) { sub position2(uword @zp x, uword y, bool also_port_1) {
position(x, y) position(x, y)
if also_port_1 if also_port_1 {
cx16.vaddr_clone(0) when active_mode {
1, 5 -> cx16.vaddr(0, cx16.r0, 1, 1)
; TODO modes 2, 3
4, 6 -> {
ubyte bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 1, 1)
}
}
}
} }
inline asmsub next_pixel(ubyte color @A) { inline asmsub next_pixel(ubyte color @A) {
@ -985,96 +865,48 @@ skip:
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) { sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!). ; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use. ; You must also have called text_charset() first to select and prepare the character set to use.
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to multiples of 8 ! TODO allow per-pixel horizontal positioning
; TODO draw whole horizontal spans using vera auto increment if possible, instead of per-character columns
uword chardataptr uword chardataptr
ubyte[8] @shared char_bitmap_bytes_left
ubyte[8] @shared char_bitmap_bytes_right
when active_mode { when active_mode {
1, 5 -> { 1, 5 -> {
; monochrome mode, either resolution ; monochrome mode, either resolution
cx16.r3 = sctextptr cx16.r2 = 40
while @(cx16.r3) { if active_mode==5
chardataptr = charset_addr + @(cx16.r3) * $0008 cx16.r2 = 80
; copy the character bitmap into RAM while @(sctextptr) {
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1) chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, 1)
position(x,y)
%asm {{ %asm {{
; pre-shift the bits lda cx16.VERA_ADDR_H
phx ; TODO remove in non-stack version and #%111 ; don't auto-increment, we have to do that manually because of the ora
lda text.x sta cx16.VERA_ADDR_H
and #7 lda color
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
ldy #0 ldy #8
- lda cx16.VERA_DATA0 - lda P8ZP_SCRATCH_B1
stz P8ZP_SCRATCH_REG bne + ; white color, plot normally
ldx P8ZP_SCRATCH_B1 lda cx16.VERA_DATA1
cpx #0 eor #255 ; black color, keep only the other pixels
beq + and cx16.VERA_DATA0
- lsr a bra ++
ror P8ZP_SCRATCH_REG + lda cx16.VERA_DATA0
dex ora cx16.VERA_DATA1
+ sta cx16.VERA_DATA0
lda cx16.VERA_ADDR_L
clc
adc cx16.r2
sta cx16.VERA_ADDR_L
bcc +
inc cx16.VERA_ADDR_M
+ inc x
bne +
inc x+1
+ dey
bne - bne -
+ sta char_bitmap_bytes_left,y
lda P8ZP_SCRATCH_REG
sta char_bitmap_bytes_right,y
iny
cpy #8
bne --
plx ; TODO remove in non-stack version
}} }}
; left part of shifted char sctextptr++
position2(x, y, true)
set_autoincrs_mode1_or_5()
if color {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
}
; right part of shifted char
if lsb(x) & 7 {
position2(x+8, y, true)
set_autoincrs_mode1_or_5()
if color {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
}
}
cx16.r3++
x += 8
} }
} }
4 -> { 4 -> {
@ -1086,7 +918,7 @@ skip:
position(x,y) position(x,y)
y++ y++
%asm {{ %asm {{
phx ; TODO remove in non-stack version phx
ldx color ldx color
lda cx16.VERA_DATA1 lda cx16.VERA_DATA1
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
@ -1098,7 +930,7 @@ skip:
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address + lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
+ dey + dey
bne - bne -
plx ; TODO remove in non-stack version plx
}} }}
} }
x+=8 x+=8
@ -1110,80 +942,30 @@ skip:
; hires 4c ; hires 4c
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop. ; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
cx16.r11L = color cx16.r11L = color
cx16.r8 = y
while @(sctextptr) { while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8 chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
position(x, y) ; only calculated once, we update vera address in the loop instead
cx16.VERA_ADDR_H &= $0f ; no auto increment
repeat 8 { repeat 8 {
cx16.r10L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits ; TODO rewrite this inner loop partly in assembly
; requires expanding the charbits to 2-bits per pixel (based on color)
; also it's way more efficient to draw whole horizontal spans instead of per-character
cx16.r9L = cx16.vpeek(charset_bank, chardataptr) ; get the 8 horizontal character bits
cx16.r7 = x cx16.r7 = x
repeat 8 { repeat 8 {
cx16.r10L <<= 1 cx16.r9L <<= 1
if_cs { if_cs
cx16.r2L = cx16.r7L & 3 ; xbits plot(cx16.r7, cx16.r8, cx16.r11L)
when cx16.r11L & 3 {
1 -> cx16.r12L = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> cx16.r12L = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> cx16.r12L = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
else -> cx16.r12L = 0
}
cx16.VERA_DATA0 = cx16.VERA_DATA0 & gfx2.plot.mask4c[cx16.r2L] | cx16.r12L
}
cx16.r7++ cx16.r7++
if (cx16.r7 & 3) == 0 {
; increment the pixel address by one
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #1
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
}
} }
chardataptr++
; increment pixel address to the next line cx16.r8++
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #(640-8)/4
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
} }
x+=8 x+=8
cx16.r8-=8
sctextptr++ sctextptr++
} }
} }
} }
sub set_autoincrs_mode1_or_5() {
; set autoincrements to go to next pixel row (40 or 80 increment)
if active_mode==1 {
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
} else {
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
}
}
} }
asmsub cs_innerloop640() clobbers(Y) { asmsub cs_innerloop640() clobbers(Y) {

View File

@ -10,8 +10,6 @@
graphics { graphics {
%option no_symbol_prefixing
const uword WIDTH = 320 const uword WIDTH = 320
const ubyte HEIGHT = 240 const ubyte HEIGHT = 240

View File

@ -2,10 +2,9 @@
; Should you want to restore the default palette, you have to reinitialize the Vera yourself. ; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
palette { palette {
%option no_symbol_prefixing
uword vera_palette_ptr uword vera_palette_ptr
ubyte cc ubyte c
sub set_color(ubyte index, uword color) { sub set_color(ubyte index, uword color) {
vera_palette_ptr = $fa00+(index as uword * 2) vera_palette_ptr = $fa00+(index as uword * 2)
@ -80,13 +79,13 @@ palette {
sub set_grayscale() { sub set_grayscale() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
cc=0 c=0
repeat 16 { repeat 16 {
cx16.vpoke(1, vera_palette_ptr, cc) cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, cc) cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++ vera_palette_ptr++
cc += $11 c += $11
} }
} }
} }
@ -151,11 +150,11 @@ palette {
sub set_c64pepto() { sub set_c64pepto() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
for cc in 0 to 15 { for c in 0 to 15 {
uword ccp = C64_colorpalette_pepto[cc] uword cc = C64_colorpalette_pepto[c]
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
vera_palette_ptr++ vera_palette_ptr++
} }
} }
@ -164,11 +163,11 @@ palette {
sub set_c64light() { sub set_c64light() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
for cc in 0 to 15 { for c in 0 to 15 {
uword ccp = C64_colorpalette_light[cc] uword cc = C64_colorpalette_light[c]
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
vera_palette_ptr++ vera_palette_ptr++
} }
} }
@ -177,11 +176,11 @@ palette {
sub set_c64dark() { sub set_c64dark() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
for cc in 0 to 15 { for c in 0 to 15 {
uword ccp = C64_colorpalette_dark[cc] uword cc = C64_colorpalette_dark[c]
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
vera_palette_ptr++ vera_palette_ptr++
} }
} }

View File

@ -1,8 +1,6 @@
%import syslib %import syslib
psg { psg {
%option no_symbol_prefixing
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices) ; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
; 00 frequency word LSB ; 00 frequency word LSB
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914 ; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
@ -24,7 +22,10 @@ psg {
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE. ; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE. ; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
envelope_states[voice_num] = 255 envelope_states[voice_num] = 255
sys.irqsafe_set_irqd() %asm {{
php
sei
}}
cx16.r0 = $f9c2 + voice_num * 4 cx16.r0 = $f9c2 + voice_num * 4
cx16.VERA_CTRL = 0 cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0) cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -35,7 +36,9 @@ psg {
cx16.VERA_DATA0 = waveform | pulsewidth cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0) envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume envelope_maxvolumes[voice_num] = volume
sys.irqsafe_clear_irqd() %asm {{
plp
}}
} }
; sub freq_hz(ubyte voice_num, float hertz) { ; sub freq_hz(ubyte voice_num, float hertz) {
@ -50,7 +53,10 @@ psg {
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation. ; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md) ; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
; Write freq MSB first and then LSB to reduce the chance on clicks ; Write freq MSB first and then LSB to reduce the chance on clicks
sys.irqsafe_set_irqd() %asm {{
php
sei
}}
cx16.r0 = $f9c1 + voice_num * 4 cx16.r0 = $f9c1 + voice_num * 4
cx16.VERA_CTRL = 0 cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0) cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -59,7 +65,9 @@ psg {
cx16.VERA_DATA0 = msb(vera_freq) cx16.VERA_DATA0 = msb(vera_freq)
cx16.VERA_ADDR_L-- cx16.VERA_ADDR_L--
cx16.VERA_DATA0 = lsb(vera_freq) cx16.VERA_DATA0 = lsb(vera_freq)
sys.irqsafe_clear_irqd() %asm {{
plp
}}
} }
sub volume(ubyte voice_num, ubyte vol) { sub volume(ubyte voice_num, ubyte vol) {
@ -173,7 +181,7 @@ psg {
} }
ubyte[16] envelope_states ubyte[16] envelope_states
uword[16] @split envelope_volumes ; scaled by 256 uword[16] envelope_volumes ; scaled by 256
ubyte[16] envelope_attacks ubyte[16] envelope_attacks
ubyte[16] envelope_sustains ubyte[16] envelope_sustains
ubyte[16] envelope_releases ubyte[16] envelope_releases

View File

@ -1,11 +1,9 @@
; Prog8 definitions for the CommanderX16 ; Prog8 definitions for the CommanderX16
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
cbm { c64 {
; Commodore (CBM) common variables, vectors and kernal routines ; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
%option no_symbol_prefixing
; STROUT --> use txt.print ; STROUT --> use txt.print
; CLEARSCR -> use txt.clear_screen ; CLEARSCR -> use txt.clear_screen
@ -15,12 +13,12 @@ romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ) romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks() romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
@ -32,23 +30,23 @@ romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read. romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high) romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X. romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
; ---- utility ; ---- utility
@ -57,7 +55,7 @@ asmsub STOP2() -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag. ; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{ %asm {{
phx phx
jsr cbm.STOP jsr c64.STOP
beq + beq +
plx plx
lda #0 lda #0
@ -69,14 +67,13 @@ asmsub STOP2() -> ubyte @A {
} }
asmsub RDTIM16() -> uword @AY { asmsub RDTIM16() -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs. ; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
%asm {{ %asm {{
phx phx
php php
sei sei
jsr cbm.RDTIM jsr c64.RDTIM
plp plp
cli
pha pha
txa txa
tay tay
@ -90,40 +87,38 @@ asmsub RDTIM16() -> uword @AY {
cx16 { cx16 {
%option no_symbol_prefixing
; irq, system and hardware vectors: ; irq, system and hardware vectors:
&uword IERROR = $0300 &uword IERROR = $0300
&uword IMAIN = $0302 &uword IMAIN = $0302
&uword ICRNCH = $0304 &uword ICRNCH = $0304
&uword IQPLOP = $0306 &uword IQPLOP = $0306
&uword IGONE = $0308 &uword IGONE = $0308
&uword IEVAL = $030a &uword IEVAL = $030a
&ubyte SAREG = $030c ; register storage for A for SYS calls &ubyte SAREG = $030c ; register storage for A for SYS calls
&ubyte SXREG = $030d ; register storage for X for SYS calls &ubyte SXREG = $030d ; register storage for X for SYS calls
&ubyte SYREG = $030e ; register storage for Y for SYS calls &ubyte SYREG = $030e ; register storage for Y for SYS calls
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls &ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
&uword USRADD = $0311 ; vector for the USR() basic command &uword USRADD = $0311 ; vector for the USR() basic command
; $0313 is unused. ; $0313 is unused.
&uword CINV = $0314 ; IRQ vector (in ram) &uword CINV = $0314 ; IRQ vector (in ram)
&uword CBINV = $0316 ; BRK vector (in ram) &uword CBINV = $0316 ; BRK vector (in ram)
&uword NMINV = $0318 ; NMI vector (in ram) &uword NMINV = $0318 ; NMI vector (in ram)
&uword IOPEN = $031a &uword IOPEN = $031a
&uword ICLOSE = $031c &uword ICLOSE = $031c
&uword ICHKIN = $031e &uword ICHKIN = $031e
&uword ICKOUT = $0320 &uword ICKOUT = $0320
&uword ICLRCH = $0322 &uword ICLRCH = $0322
&uword IBASIN = $0324 &uword IBASIN = $0324
&uword IBSOUT = $0326 &uword IBSOUT = $0326
&uword ISTOP = $0328 &uword ISTOP = $0328
&uword IGETIN = $032a &uword IGETIN = $032a
&uword ICLALL = $032c &uword ICLALL = $032c
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8 &uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
&uword ILOAD = $0330 &uword ILOAD = $0330
&uword ISAVE = $0332 &uword ISAVE = $0332
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in &uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in &uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in &uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s) ; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
@ -279,44 +274,44 @@ cx16 {
; I/O ; I/O
const uword VIA1_BASE = $9f00 ;VIA 6522 #1 const uword VIA1_BASE = $9f00 ;VIA 6522 #1
&ubyte via1prb = VIA1_BASE + 0 &ubyte via1prb = VIA1_BASE + 0
&ubyte via1pra = VIA1_BASE + 1 &ubyte via1pra = VIA1_BASE + 1
&ubyte via1ddrb = VIA1_BASE + 2 &ubyte via1ddrb = VIA1_BASE + 2
&ubyte via1ddra = VIA1_BASE + 3 &ubyte via1ddra = VIA1_BASE + 3
&ubyte via1t1l = VIA1_BASE + 4 &ubyte via1t1l = VIA1_BASE + 4
&ubyte via1t1h = VIA1_BASE + 5 &ubyte via1t1h = VIA1_BASE + 5
&ubyte via1t1ll = VIA1_BASE + 6 &ubyte via1t1ll = VIA1_BASE + 6
&ubyte via1t1lh = VIA1_BASE + 7 &ubyte via1t1lh = VIA1_BASE + 7
&ubyte via1t2l = VIA1_BASE + 8 &ubyte via1t2l = VIA1_BASE + 8
&ubyte via1t2h = VIA1_BASE + 9 &ubyte via1t2h = VIA1_BASE + 9
&ubyte via1sr = VIA1_BASE + 10 &ubyte via1sr = VIA1_BASE + 10
&ubyte via1acr = VIA1_BASE + 11 &ubyte via1acr = VIA1_BASE + 11
&ubyte via1pcr = VIA1_BASE + 12 &ubyte via1pcr = VIA1_BASE + 12
&ubyte via1ifr = VIA1_BASE + 13 &ubyte via1ifr = VIA1_BASE + 13
&ubyte via1ier = VIA1_BASE + 14 &ubyte via1ier = VIA1_BASE + 14
&ubyte via1ora = VIA1_BASE + 15 &ubyte via1ora = VIA1_BASE + 15
const uword VIA2_BASE = $9f10 ;VIA 6522 #2 const uword VIA2_BASE = $9f10 ;VIA 6522 #2
&ubyte via2prb = VIA2_BASE + 0 &ubyte via2prb = VIA2_BASE + 0
&ubyte via2pra = VIA2_BASE + 1 &ubyte via2pra = VIA2_BASE + 1
&ubyte via2ddrb = VIA2_BASE + 2 &ubyte via2ddrb = VIA2_BASE + 2
&ubyte via2ddra = VIA2_BASE + 3 &ubyte via2ddra = VIA2_BASE + 3
&ubyte via2t1l = VIA2_BASE + 4 &ubyte via2t1l = VIA2_BASE + 4
&ubyte via2t1h = VIA2_BASE + 5 &ubyte via2t1h = VIA2_BASE + 5
&ubyte via2t1ll = VIA2_BASE + 6 &ubyte via2t1ll = VIA2_BASE + 6
&ubyte via2t1lh = VIA2_BASE + 7 &ubyte via2t1lh = VIA2_BASE + 7
&ubyte via2t2l = VIA2_BASE + 8 &ubyte via2t2l = VIA2_BASE + 8
&ubyte via2t2h = VIA2_BASE + 9 &ubyte via2t2h = VIA2_BASE + 9
&ubyte via2sr = VIA2_BASE + 10 &ubyte via2sr = VIA2_BASE + 10
&ubyte via2acr = VIA2_BASE + 11 &ubyte via2acr = VIA2_BASE + 11
&ubyte via2pcr = VIA2_BASE + 12 &ubyte via2pcr = VIA2_BASE + 12
&ubyte via2ifr = VIA2_BASE + 13 &ubyte via2ifr = VIA2_BASE + 13
&ubyte via2ier = VIA2_BASE + 14 &ubyte via2ier = VIA2_BASE + 14
&ubyte via2ora = VIA2_BASE + 15 &ubyte via2ora = VIA2_BASE + 15
; YM-2151 sound chip ; YM-2151 sound chip
&ubyte YM_ADDRESS = $9f40 &ubyte YM_ADDRESS = $9f40
&ubyte YM_DATA = $9f41 &ubyte YM_DATA = $9f41
const uword extdev = $9f60 const uword extdev = $9f60
@ -328,7 +323,7 @@ cx16 {
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y) romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y) romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y) romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
romsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) clobbers(X, Y) -> ubyte @A, bool @Pc ; note: X,Y size result is not supported, use SCREEN or get_screen_mode routine for that romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr() romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y) ; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
@ -345,12 +340,12 @@ romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y) romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y) romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y) romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y)
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, bool fill @Pc) clobbers(A,X,Y) romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, ubyte fill @Pc) clobbers(A,X,Y)
romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y) romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y)
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, bool fill @Pc) clobbers(A,X,Y) romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, ubyte fill @Pc) clobbers(A,X,Y)
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y) romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y) romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, bool is_control @Pc) clobbers(A,X,Y) romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y)
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y) romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
@ -372,17 +367,16 @@ romsub $ff1a = FB_filter_pixels(uword pointer @ R0, uword count @R1) clobbers(A
romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y) romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y)
; misc ; misc
romsub $FEBA = BSAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; like cbm.SAVE, but omits the 2-byte prg header romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, ubyte @Pc
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> ubyte @Pc
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> bool @Pc romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, bool apply_mask @Pc) clobbers(A,X,Y) -> bool @Pc
romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y) romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y)
romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y) romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y)
romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y) romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y)
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2 romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1 romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y) romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
romsub $fede = console_put_char(ubyte char @A, bool wrapping @Pc) clobbers(A,X,Y) romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y)
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y) romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y) romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
@ -390,7 +384,7 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() clobbers(A,X,Y) romsub $fecc = monitor() clobbers(A,X,Y)
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
romsub $ff47 = enter_basic(bool cold_or_warm @Pc) clobbers(A,X,Y) romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y) romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time() romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
@ -407,37 +401,18 @@ romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
; Audio (rom bank 10) ; Audio (bank 10)
romsub $C04B = psg_init() clobbers(A,X,Y) romsub $C04B = psg_init() clobbers(A,X,Y)
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip romsub $C063 = ym_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> ubyte @Pc ; load default YM patches
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize PSG and YM audio chips romsub $C09F = audio_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)initialize PSG and YM audio chips
; TODO: add more of the audio routines? ; TODO: add more of the audio routines?
asmsub set_screen_mode(ubyte mode @A) clobbers(A,X,Y) -> bool @Pc {
; -- convenience wrapper for screen_mode() to just set a new mode (and return success)
%asm {{
clc
jmp screen_mode
}}
}
asmsub get_screen_mode() -> byte @A, byte @X, byte @Y {
; -- convenience wrapper for screen_mode() to just get the current mode in A, and size in characters in X+Y
; this does need a piece of inlined asm to call it ans store the result values if you call this from prog8 code
; Note: you can also just do the SEC yourself and simply call screen_mode() directly,
; or use the existing SCREEN kernal routine for just getting the size in characters.
%asm {{
sec
jmp screen_mode
}}
}
asmsub kbdbuf_clear() { asmsub kbdbuf_clear() {
; -- convenience helper routine to clear the keyboard buffer ; -- convenience helper routine to clear the keyboard buffer
%asm {{ %asm {{
- jsr cbm.GETIN - jsr c64.GETIN
bne - bne -
rts rts
}} }}
@ -510,7 +485,7 @@ asmsub numbanks() -> uword @AY {
%asm {{ %asm {{
phx phx
sec sec
jsr cbm.MEMTOP jsr c64.MEMTOP
ldy #0 ldy #0
cmp #0 cmp #0
bne + bne +
@ -539,8 +514,6 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrOrDecrByOne @Y) clobbers(A) { asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrOrDecrByOne @Y) clobbers(A) {
; -- setup the VERA's data address register 0 or 1 ; -- setup the VERA's data address register 0 or 1
; with optional auto increment or decrement of 1.
; Note that the vaddr_autoincr() and vaddr_autodecr() routines allow to set all possible strides, not just 1.
%asm {{ %asm {{
and #1 and #1
pha pha
@ -564,105 +537,6 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
}} }}
} }
asmsub vaddr_clone(ubyte port @A) clobbers (A,X,Y) {
; -- clones Vera addresses from the given source port to the other one.
; leaves CTRL on the destination port.
%asm {{
sta VERA_CTRL
ldx VERA_ADDR_L
ldy VERA_ADDR_H
phy
ldy VERA_ADDR_M
eor #1
sta VERA_CTRL
stx VERA_ADDR_L
sty VERA_ADDR_M
ply
sty VERA_ADDR_H
rts
}}
}
asmsub vaddr_autoincr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoIncrAmount @R2) clobbers(A,Y) {
; -- setup the VERA's data address register 0 or 1
; including setting up optional auto increment amount.
; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible.
%asm {{
jsr _setup
lda cx16.r2H
ora cx16.r2L
beq +
jsr _determine_incr_bits
+ ora P8ZP_SCRATCH_REG
sta cx16.VERA_ADDR_H
rts
_setup and #1
sta P8ZP_SCRATCH_REG
lda cx16.r1
and #1
sta cx16.VERA_CTRL
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
rts
_determine_incr_bits
lda cx16.r2H
bne _large
lda cx16.r2L
ldy #13
- cmp _strides_lsb,y
beq +
dey
bpl -
+ tya
asl a
asl a
asl a
asl a
rts
_large ora cx16.r2L
cmp #1 ; 256
bne +
lda #9<<4
rts
+ cmp #2 ; 512
bne +
lda #10<<4
rts
+ cmp #65 ; 320
bne +
lda #14<<4
rts
+ cmp #130 ; 640
bne +
lda #15<<4
rts
+ lda #0
rts
_strides_lsb .byte 0,1,2,4,8,16,32,64,128,255,255,40,80,160,255,255
}}
}
asmsub vaddr_autodecr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoDecrAmount @R2) clobbers(A,Y) {
; -- setup the VERA's data address register 0 or 1
; including setting up optional auto decrement amount.
; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible.
%asm {{
jsr vaddr_autoincr._setup
lda cx16.r2H
ora cx16.r2L
beq +
jsr vaddr_autoincr._determine_incr_bits
ora #%00001000 ; autodecrement
+ ora P8ZP_SCRATCH_REG
sta cx16.VERA_ADDR_H
rts
}}
}
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) { asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; -- write a single byte to VERA's video memory ; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes! ; note: inefficient when writing multiple sequential bytes!
@ -753,91 +627,7 @@ asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @
}} }}
} }
asmsub save_virtual_registers() clobbers(A,Y) { ; ---- system stuff -----
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
asmsub save_vera_context() clobbers(A) {
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
%asm {{
; note cannot store this on cpu hardware stack because this gets called as a subroutine
lda cx16.VERA_ADDR_L
sta _vera_storage
lda cx16.VERA_ADDR_M
sta _vera_storage+1
lda cx16.VERA_ADDR_H
sta _vera_storage+2
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
lda cx16.VERA_ADDR_M
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
rts
_vera_storage: .byte 0,0,0,0,0,0,0,0
}}
}
asmsub restore_vera_context() clobbers(A) {
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
%asm {{
lda cx16.save_vera_context._vera_storage+7
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+6
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+5
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+4
sta cx16.VERA_ADDR_L
lda cx16.save_vera_context._vera_storage+3
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+2
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+1
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+0
sta cx16.VERA_ADDR_L
rts
}}
}
}
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub init_system() { asmsub init_system() {
; Initializes the machine to a sane starting state. ; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic. ; Called automatically by the loader program logic.
@ -848,30 +638,29 @@ asmsub init_system() {
tay tay
jsr cx16.mouse_config ; disable mouse jsr cx16.mouse_config ; disable mouse
cld cld
lda cx16.VERA_DC_VIDEO lda VERA_DC_VIDEO
and #%00000111 ; retain chroma + output mode and #%00000111 ; retain chroma + output mode
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG
lda #$0a lda #$0a
sta $01 ; rom bank 10 (audio) sta $01 ; rom bank 10 (audio)
jsr cx16.audio_init ; silence jsr audio_init ; silence
stz $01 ; rom bank 0 (kernal) stz $01 ; rom bank 0 (kernal)
jsr cbm.IOINIT jsr c64.IOINIT
jsr cbm.RESTOR jsr c64.RESTOR
jsr cbm.CINT jsr c64.CINT
lda cx16.VERA_DC_VIDEO lda VERA_DC_VIDEO
and #%11111000 and #%11111000
ora P8ZP_SCRATCH_REG ora P8ZP_SCRATCH_REG
sta cx16.VERA_DC_VIDEO ; restore old output mode sta VERA_DC_VIDEO ; restore old output mode
lda #$90 ; black lda #$90 ; black
jsr cbm.CHROUT jsr c64.CHROUT
lda #1 lda #1
jsr cbm.CHROUT ; swap fg/bg sta $00 ; select ram bank 1
jsr c64.CHROUT ; swap fg/bg
lda #$9e ; yellow lda #$9e ; yellow
jsr cbm.CHROUT jsr c64.CHROUT
lda #147 ; clear screen lda #147 ; clear screen
jsr cbm.CHROUT jsr c64.CHROUT
lda #PROG8_VARSHIGH_RAMBANK
sta $00 ; select ram bank
lda #0 lda #0
tax tax
tay tay
@ -889,8 +678,6 @@ asmsub init_system_phase2() {
sta restore_irq._orig_irqvec sta restore_irq._orig_irqvec
lda cx16.CINV+1 lda cx16.CINV+1
sta restore_irq._orig_irqvec+1 sta restore_irq._orig_irqvec+1
lda #PROG8_VARSHIGH_RAMBANK
sta $00 ; select ram bank
cli cli
rts rts
}} }}
@ -908,7 +695,7 @@ asmsub cleanup_at_exit() {
}} }}
} }
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) { asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -987,6 +774,56 @@ IRQ_SCRATCH_ZPWORD2 .word 0
}} }}
} }
asmsub save_vera_context() clobbers(A) {
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
%asm {{
; note cannot store this on cpu hardware stack because this gets called as a subroutine
lda cx16.VERA_ADDR_L
sta _vera_storage
lda cx16.VERA_ADDR_M
sta _vera_storage+1
lda cx16.VERA_ADDR_H
sta _vera_storage+2
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
lda cx16.VERA_ADDR_M
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
rts
_vera_storage: .byte 0,0,0,0,0,0,0,0
}}
}
asmsub restore_vera_context() clobbers(A) {
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
%asm {{
lda cx16.save_vera_context._vera_storage+7
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+6
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+5
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+4
sta cx16.VERA_ADDR_L
lda cx16.save_vera_context._vera_storage+3
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+2
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+1
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+0
sta cx16.VERA_ADDR_L
rts
}}
}
asmsub restore_irq() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
sei sei
@ -1056,14 +893,23 @@ asmsub set_rasterline(uword line @AY) {
}} }}
} }
}
sys {
; ------- lowlevel system routines --------
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub reset_system() { asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
; We do this via the SMC so that a true reset is performed that also resets the Vera fully. ; We do this via the SMC so that a true reset is performed that also resets the Vera fully.
%asm {{ %asm {{
sei sei
ldx #$42 ldx #$42
ldy #2 ldy #1
lda #0 tya
jsr cx16.i2c_write_byte jsr cx16.i2c_write_byte
bra * bra *
}} }}
@ -1095,11 +941,11 @@ _loop lda P8ZP_SCRATCH_W1
rts rts
+ sei + sei
jsr cbm.RDTIM jsr c64.RDTIM
cli cli
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- sei - sei
jsr cbm.RDTIM jsr c64.RDTIM
cli cli
cmp P8ZP_SCRATCH_B1 cmp P8ZP_SCRATCH_B1
beq - beq -
@ -1240,23 +1086,10 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
jsr cbm.CLRCHN ; reset i/o channels jsr c64.CLRCHN ; reset i/o channels
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
txs txs
rts ; return to original caller rts ; return to original caller

View File

@ -6,8 +6,6 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 80 const ubyte DEFAULT_WIDTH = 80
const ubyte DEFAULT_HEIGHT = 60 const ubyte DEFAULT_HEIGHT = 60
@ -35,10 +33,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
sec sec
jsr cbm.PLOT jsr c64.PLOT
tay tay
clc clc
jmp cbm.PLOT jmp c64.PLOT
}} }}
} }
@ -48,7 +46,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
sty _ly+1 sty _ly+1
phx phx
pha pha
jsr cbm.SCREEN ; get dimensions in X/Y jsr c64.SCREEN ; get dimensions in X/Y
txa txa
lsr a lsr a
lsr a lsr a
@ -96,7 +94,7 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
%asm {{ %asm {{
phx phx
pha pha
jsr cbm.SCREEN ; get dimensions in X/Y jsr c64.SCREEN ; get dimensions in X/Y
txa txa
lsr a lsr a
lsr a lsr a
@ -127,7 +125,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
%asm {{ %asm {{
phx phx
sta _la+1 sta _la+1
jsr cbm.SCREEN ; get dimensions in X/Y jsr c64.SCREEN ; get dimensions in X/Y
txa txa
lsr a lsr a
lsr a lsr a
@ -160,35 +158,35 @@ ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$
sub color (ubyte txtcol) { sub color (ubyte txtcol) {
txtcol &= 15 txtcol &= 15
cbm.CHROUT(color_to_charcode[txtcol]) c64.CHROUT(color_to_charcode[txtcol])
} }
sub color2 (ubyte txtcol, ubyte bgcol) { sub color2 (ubyte txtcol, ubyte bgcol) {
txtcol &= 15 txtcol &= 15
bgcol &= 15 bgcol &= 15
cbm.CHROUT(color_to_charcode[bgcol]) c64.CHROUT(color_to_charcode[bgcol])
cbm.CHROUT(1) ; switch fg and bg colors c64.CHROUT(1) ; switch fg and bg colors
cbm.CHROUT(color_to_charcode[txtcol]) c64.CHROUT(color_to_charcode[txtcol])
} }
sub lowercase() { sub lowercase() {
cbm.CHROUT($0e) c64.CHROUT($0e)
; this is not 100% compatible: cx16.screen_set_charset(3, 0) ; lowercase petscii charset ; this is not 100% compatible: cx16.screen_set_charset(3, 0) ; lowercase petscii charset
} }
sub uppercase() { sub uppercase() {
cbm.CHROUT($8e) c64.CHROUT($8e)
; this is not 100% compatible: cx16.screen_set_charset(2, 0) ; uppercase petscii charset ; this is not 100% compatible: cx16.screen_set_charset(2, 0) ; uppercase petscii charset
} }
sub iso() { sub iso() {
cbm.CHROUT($0f) c64.CHROUT($0f)
; This doesn't enable it completely: cx16.screen_set_charset(1, 0) ; iso charset ; This doesn't enable it completely: cx16.screen_set_charset(1, 0) ; iso charset
} }
sub iso_off() { sub iso_off() {
; -- you have to call this first when switching back from iso charset to regular charset. ; -- you have to call this first when switching back from iso charset to regular charset.
cbm.CHROUT($8f) c64.CHROUT($8f)
} }
@ -197,7 +195,7 @@ asmsub scroll_left() clobbers(A, Y) {
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
%asm {{ %asm {{
phx phx
jsr cbm.SCREEN jsr c64.SCREEN
dex dex
stx _lx+1 stx _lx+1
dey dey
@ -238,12 +236,12 @@ _lx ldx #0 ; modified
}} }}
} }
asmsub scroll_right() clobbers(A,Y) { asmsub scroll_right() clobbers(A) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
%asm {{ %asm {{
phx phx
jsr cbm.SCREEN jsr c64.SCREEN
dex dex
stx _lx+1 stx _lx+1
txa txa
@ -297,7 +295,7 @@ asmsub scroll_up() clobbers(A, Y) {
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
%asm {{ %asm {{
phx phx
jsr cbm.SCREEN jsr c64.SCREEN
stx _nextline+1 stx _nextline+1
dey dey
sty P8ZP_SCRATCH_B1 sty P8ZP_SCRATCH_B1
@ -347,7 +345,7 @@ asmsub scroll_down() clobbers(A, Y) {
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
%asm {{ %asm {{
phx phx
jsr cbm.SCREEN jsr c64.SCREEN
stx _nextline+1 stx _nextline+1
dey dey
sty P8ZP_SCRATCH_B1 sty P8ZP_SCRATCH_B1
@ -398,20 +396,20 @@ _nextline
}} }}
} }
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y ; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char, ; a call to this subroutine with a string argument of just one char,
; by just one call to cbm.CHROUT of that single char. ; by just one call to c64.CHROUT of that single char.
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_B1),y - lda (P8ZP_SCRATCH_B1),y
beq + beq +
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ rts + rts
@ -425,11 +423,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
pha pha
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
jsr cbm.CHROUT jsr c64.CHROUT
txa txa
jsr cbm.CHROUT jsr c64.CHROUT
plx plx
rts rts
}} }}
@ -445,16 +443,16 @@ _print_byte_digits
cpy #'0' cpy #'0'
beq + beq +
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
jsr cbm.CHROUT jsr c64.CHROUT
bra _ones bra _ones
+ pla + pla
cmp #'0' cmp #'0'
beq _ones beq _ones
jsr cbm.CHROUT jsr c64.CHROUT
_ones txa _ones txa
jsr cbm.CHROUT jsr c64.CHROUT
plx plx
rts rts
}} }}
@ -468,45 +466,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
cmp #0 cmp #0
bpl + bpl +
lda #'-' lda #'-'
jsr cbm.CHROUT jsr c64.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
bra print_ub._print_byte_digits bra print_ub._print_byte_digits
}} }}
} }
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
phx phx
bcc + bcc +
pha pha
lda #'$' lda #'$'
jsr cbm.CHROUT jsr c64.CHROUT
pla pla
+ jsr conv.ubyte2hex + jsr conv.ubyte2hex
jsr cbm.CHROUT jsr c64.CHROUT
tya tya
jsr cbm.CHROUT jsr c64.CHROUT
plx plx
rts rts
}} }}
} }
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
phx phx
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
bcc + bcc +
lda #'%' lda #'%'
jsr cbm.CHROUT jsr c64.CHROUT
+ ldy #8 + ldy #8
- lda #'0' - lda #'0'
asl P8ZP_SCRATCH_B1 asl P8ZP_SCRATCH_B1
bcc + bcc +
lda #'1' lda #'1'
+ jsr cbm.CHROUT + jsr c64.CHROUT
dey dey
bne - bne -
plx plx
@ -514,7 +512,7 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -526,7 +524,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
@ -547,7 +545,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
beq + beq +
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ plx + plx
@ -570,14 +568,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
bne - bne -
_gotdigit _gotdigit
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
lda conv.uword2decimal.decTenThousands,y lda conv.uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
rts rts
_allzero _allzero
lda #'0' lda #'0'
jmp cbm.CHROUT jmp c64.CHROUT
}} }}
} }
@ -588,7 +586,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
bpl + bpl +
pha pha
lda #'-' lda #'-'
jsr cbm.CHROUT jsr c64.CHROUT
tya tya
eor #255 eor #255
tay tay
@ -609,7 +607,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0 ldy #0 ; char counter = 0
- jsr cbm.CHRIN - jsr c64.CHRIN
cmp #$0d ; return (ascii 13) pressed? cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end. beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
@ -771,7 +769,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
phx phx
tax tax
clc clc
jsr cbm.PLOT jsr c64.PLOT
plx plx
rts rts
}} }}
@ -780,7 +778,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
asmsub width() clobbers(X,Y) -> ubyte @A { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
jsr cbm.SCREEN jsr c64.SCREEN
txa txa
rts rts
}} }}
@ -789,7 +787,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
jsr cbm.SCREEN jsr c64.SCREEN
tya tya
rts rts
}} }}

View File

@ -3,8 +3,6 @@
%import textio %import textio
cx16logo { cx16logo {
%option no_symbol_prefixing
sub logo_at(ubyte column, ubyte row) { sub logo_at(ubyte column, ubyte row) {
uword strptr uword strptr
for strptr in logo_lines { for strptr in logo_lines {

View File

@ -1,66 +1,59 @@
; C64/C128 disk drive I/O routines. ; C64 and Cx16 disk drive I/O routines.
%import textio %import textio
%import string %import string
%import syslib %import syslib
diskio { diskio {
%option no_symbol_prefixing
ubyte drivenumber = 8 sub directory(ubyte drivenumber) -> bool {
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
sub set_drive(ubyte number) { c64.SETNAM(1, "$")
drivenumber = number c64.SETLFS(12, drivenumber, 0)
}
sub directory() -> bool {
; -- Prints the directory contents to the screen. Returns success.
cbm.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0)
ubyte status = 1 ubyte status = 1
void cbm.OPEN() ; open 12,8,0,"$" void c64.OPEN() ; open 12,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void cbm.CHKIN(12) ; use #12 as input channel void c64.CHKIN(12) ; use #12 as input channel
if_cs if_cs
goto io_error goto io_error
repeat 4 { repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes void c64.CHRIN() ; skip the 4 prologue bytes
} }
; while not stop key pressed / EOF encountered, read data. ; while not stop key pressed / EOF encountered, read data.
status = cbm.READST() status = c64.READST()
if status!=0 { if status!=0 {
status = 1 status = 1
goto io_error goto io_error
} }
while status==0 { while status==0 {
ubyte low = cbm.CHRIN() ubyte low = c64.CHRIN()
ubyte high = cbm.CHRIN() ubyte high = c64.CHRIN()
txt.print_uw(mkword(high, low)) txt.print_uw(mkword(high, low))
txt.spc() txt.spc()
ubyte @zp char ubyte @zp char
repeat { repeat {
char = cbm.CHRIN() char = c64.CHRIN()
if char==0 if char==0
break break
txt.chrout(char) txt.chrout(char)
} }
txt.nl() txt.nl()
void cbm.CHRIN() ; skip 2 bytes void c64.CHRIN() ; skip 2 bytes
void cbm.CHRIN() void c64.CHRIN()
status = cbm.READST() status = c64.READST()
if cbm.STOP2() if c64.STOP2()
break break
} }
status = cbm.READST() status = c64.READST()
io_error: io_error:
cbm.CLRCHN() ; restore default i/o devices c64.CLRCHN() ; restore default i/o devices
cbm.CLOSE(12) c64.CLOSE(12)
if status and status & $40 == 0 { ; bit 6=end of file if status and status & $40 == 0 { ; bit 6=end of file
txt.print("\ni/o error, status: ") txt.print("\ni/o error, status: ")
@ -72,43 +65,37 @@ io_error:
return true return true
} }
sub diskname() -> uword { sub diskname(ubyte drivenumber) -> uword {
; -- Returns pointer to disk name string or 0 if failure. ; -- Returns pointer to disk name string or 0 if failure.
cbm.SETNAM(1, "$") c64.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0) c64.SETLFS(12, drivenumber, 0)
ubyte okay = false ubyte okay = false
void cbm.OPEN() ; open 12,8,0,"$" void c64.OPEN() ; open 12,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void cbm.CHKIN(12) ; use #12 as input channel void c64.CHKIN(12) ; use #12 as input channel
if_cs if_cs
goto io_error goto io_error
while cbm.CHRIN()!='"' { repeat 6 {
; skip up to entry name void c64.CHRIN() ; skip the 6 prologue bytes
} }
if cbm.READST()!=0 if c64.READST()!=0
goto io_error goto io_error
cx16.r0 = &list_filename cx16.r0 = &list_filename
repeat { repeat {
@(cx16.r0) = cbm.CHRIN() @(cx16.r0) = c64.CHRIN()
if @(cx16.r0)=='"' { if @(cx16.r0)==0
@(cx16.r0) = ' '
while @(cx16.r0)==' ' and cx16.r0>=&diskio.list_filename {
@(cx16.r0) = 0
cx16.r0--
}
break break
}
cx16.r0++ cx16.r0++
} }
okay = true okay = true
io_error: io_error:
cbm.CLRCHN() ; restore default i/o devices c64.CLRCHN() ; restore default i/o devices
cbm.CLOSE(12) c64.CLOSE(12)
if okay if okay
return &list_filename return &list_filename
return 0 return 0
@ -119,12 +106,13 @@ io_error:
uword list_pattern uword list_pattern
uword list_blocks uword list_blocks
bool iteration_in_progress = false bool iteration_in_progress = false
ubyte last_drivenumber = 8 ; which drive was last used for a f_open operation?
str list_filetype = "???" ; prg, seq, dir str list_filetype = "???" ; prg, seq, dir
str list_filename = "?" * 50 str list_filename = "?" * 50
; ----- get a list of files (uses iteration functions internally) ----- ; ----- get a list of files (uses iteration functions internally) -----
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte { sub list_filenames(ubyte drivenumber, uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
; -- fill the provided buffer with the names of the files on the disk (until buffer is full). ; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against. ; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
; After the last filename one additional 0 byte is placed to indicate the end of the list. ; After the last filename one additional 0 byte is placed to indicate the end of the list.
@ -132,10 +120,10 @@ io_error:
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer. ; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
uword buffer_start = filenames_buffer uword buffer_start = filenames_buffer
ubyte files_found = 0 ubyte files_found = 0
if lf_start_list(pattern_ptr) { if lf_start_list(drivenumber, pattern_ptr) {
while lf_next_entry() { while lf_next_entry() {
if list_filetype!="dir" { if list_filetype!="dir" {
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1 filenames_buffer += string.copy(diskio.list_filename, filenames_buffer) + 1
files_found++ files_found++
if filenames_buffer - buffer_start > filenames_buf_size-20 { if filenames_buffer - buffer_start > filenames_buf_size-20 {
@(filenames_buffer)=0 @(filenames_buffer)=0
@ -154,7 +142,7 @@ io_error:
; ----- iterative file lister functions (uses io channel 12) ----- ; ----- iterative file lister functions (uses io channel 12) -----
sub lf_start_list(uword pattern_ptr) -> bool { sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> bool {
; -- start an iterative file listing with optional pattern matching. ; -- start an iterative file listing with optional pattern matching.
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
lf_end_list() lf_end_list()
@ -162,20 +150,20 @@ io_error:
list_skip_disk_name = true list_skip_disk_name = true
iteration_in_progress = true iteration_in_progress = true
cbm.SETNAM(1, "$") c64.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0) c64.SETLFS(12, drivenumber, 0)
void cbm.OPEN() ; open 12,8,0,"$" void c64.OPEN() ; open 12,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void cbm.CHKIN(12) ; use #12 as input channel void c64.CHKIN(12) ; use #12 as input channel
if_cs if_cs
goto io_error goto io_error
repeat 4 { repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes void c64.CHRIN() ; skip the 4 prologue bytes
} }
if cbm.READST()==0 if c64.READST()==0
return true return true
io_error: io_error:
@ -192,26 +180,26 @@ io_error:
return false return false
repeat { repeat {
void cbm.CHKIN(12) ; use #12 as input channel again void c64.CHKIN(12) ; use #12 as input channel again
uword nameptr = &list_filename uword nameptr = &list_filename
ubyte blocks_lsb = cbm.CHRIN() ubyte blocks_lsb = c64.CHRIN()
ubyte blocks_msb = cbm.CHRIN() ubyte blocks_msb = c64.CHRIN()
if cbm.READST() if c64.READST()
goto close_end goto close_end
list_blocks = mkword(blocks_msb, blocks_lsb) list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first " ; read until the filename starts after the first "
while cbm.CHRIN()!='\"' { while c64.CHRIN()!='\"' {
if cbm.READST() if c64.READST()
goto close_end goto close_end
} }
; read the filename ; read the filename
repeat { repeat {
ubyte char = cbm.CHRIN() ubyte char = c64.CHRIN()
if char==0 if char==0
break break
if char=='\"' if char=='\"'
@ -223,17 +211,17 @@ io_error:
@(nameptr) = 0 @(nameptr) = 0
do { do {
cx16.r15L = cbm.CHRIN() cx16.r15L = c64.CHRIN()
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type } until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
list_filetype[0] = cx16.r15L list_filetype[0] = cx16.r15L
list_filetype[1] = cbm.CHRIN() list_filetype[1] = c64.CHRIN()
list_filetype[2] = cbm.CHRIN() list_filetype[2] = c64.CHRIN()
while cbm.CHRIN() { while c64.CHRIN() {
; read the rest of the entry until the end ; read the rest of the entry until the end
} }
void cbm.CHRIN() ; skip 2 bytes void c64.CHRIN() ; skip 2 bytes
void cbm.CHRIN() void c64.CHRIN()
if not list_skip_disk_name { if not list_skip_disk_name {
if not list_pattern if not list_pattern
@ -252,8 +240,8 @@ close_end:
sub lf_end_list() { sub lf_end_list() {
; -- end an iterative file listing session (close channels). ; -- end an iterative file listing session (close channels).
if iteration_in_progress { if iteration_in_progress {
cbm.CLRCHN() c64.CLRCHN()
cbm.CLOSE(12) c64.CLOSE(12)
iteration_in_progress = false iteration_in_progress = false
} }
} }
@ -261,24 +249,25 @@ close_end:
; ----- iterative file loader functions (uses io channel 12) ----- ; ----- iterative file loader functions (uses io channel 12) -----
sub f_open(uword filenameptr) -> bool { sub f_open(ubyte drivenumber, uword filenameptr) -> bool {
; -- open a file for iterative reading with f_read ; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
f_close() f_close()
cbm.SETNAM(string.length(filenameptr), filenameptr) c64.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work c64.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
void cbm.OPEN() ; open 12,8,12,"filename" last_drivenumber = drivenumber
void c64.OPEN() ; open 12,8,12,"filename"
if_cc { if_cc {
if cbm.READST()==0 { if c64.READST()==0 {
iteration_in_progress = true iteration_in_progress = true
void cbm.CHKIN(12) ; use #12 as input channel void c64.CHKIN(12) ; use #12 as input channel
if_cc { if_cc {
void cbm.CHRIN() ; read first byte to test for file not found void c64.CHRIN() ; read first byte to test for file not found
if not cbm.READST() { if not c64.READST() {
cbm.CLOSE(12) ; close file because we already consumed first byte c64.CLOSE(12) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file void c64.OPEN() ; re-open the file
void cbm.CHKIN(12) void c64.CHKIN(12)
return true return true
} }
} }
@ -291,6 +280,9 @@ close_end:
sub f_read(uword bufferpointer, uword num_bytes) -> uword { sub f_read(uword bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes. ; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions) ; returns the actual number of bytes read. (checks for End-of-file and error conditions)
; NOTE: on systems with banked ram (such as Commander X16) this routine DOES NOT
; automatically load into subsequent banks if it reaches a bank boundary!
; Consider using cx16diskio.f_read() on X16.
if not iteration_in_progress or not num_bytes if not iteration_in_progress or not num_bytes
return 0 return 0
@ -303,14 +295,14 @@ close_end:
sta m_in_buffer+2 sta m_in_buffer+2
}} }}
while num_bytes { while num_bytes {
if cbm.READST() { if c64.READST() {
f_close() f_close()
if cbm.READST() & $40 ; eof? if c64.READST() & $40 ; eof?
return list_blocks ; number of bytes read return list_blocks ; number of bytes read
return 0 ; error. return 0 ; error.
} }
%asm {{ %asm {{
jsr cbm.CHRIN jsr c64.CHRIN
m_in_buffer sta $ffff m_in_buffer sta $ffff
inc m_in_buffer+1 inc m_in_buffer+1
bne + bne +
@ -325,11 +317,12 @@ m_in_buffer sta $ffff
sub f_read_all(uword bufferpointer) -> uword { sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read. ; -- read the full contents of the file, returns number of bytes read.
; Note: Consider using cx16diskio.f_read_all() on X16!
if not iteration_in_progress if not iteration_in_progress
return 0 return 0
uword total_read = 0 uword total_read = 0
while not cbm.READST() { while not c64.READST() {
cx16.r0 = f_read(bufferpointer, 256) cx16.r0 = f_read(bufferpointer, 256)
total_read += cx16.r0 total_read += cx16.r0
bufferpointer += cx16.r0 bufferpointer += cx16.r0
@ -347,9 +340,9 @@ m_in_buffer sta $ffff
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldx #12 ldx #12
jsr cbm.CHKIN ; use channel 12 again for input jsr c64.CHKIN ; use channel 12 again for input
ldy #0 ldy #0
_loop jsr cbm.CHRIN _loop jsr c64.CHRIN
sta (P8ZP_SCRATCH_W1),y sta (P8ZP_SCRATCH_W1),y
beq _end beq _end
iny iny
@ -364,11 +357,12 @@ _end rts
}} }}
} }
sub f_close() { sub f_close() {
; -- end an iterative file loading session (close channels). ; -- end an iterative file loading session (close channels).
if iteration_in_progress { if iteration_in_progress {
cbm.CLRCHN() c64.CLRCHN()
cbm.CLOSE(12) c64.CLOSE(12)
iteration_in_progress = false iteration_in_progress = false
} }
} }
@ -376,16 +370,16 @@ _end rts
; ----- iterative file writing functions (uses io channel 13) ----- ; ----- iterative file writing functions (uses io channel 13) -----
sub f_open_w(uword filenameptr) -> bool { sub f_open_w(ubyte drivenumber, uword filenameptr) -> bool {
; -- open a file for iterative writing with f_write ; -- open a file for iterative writing with f_write
f_close_w() f_close_w()
cbm.SETNAM(string.length(filenameptr), filenameptr) c64.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(13, drivenumber, 1) c64.SETLFS(13, drivenumber, 1)
void cbm.OPEN() ; open 13,8,1,"filename" void c64.OPEN() ; open 13,8,1,"filename"
if_cc { if_cc {
cbm.CHKOUT(13) ; use #13 as output channel c64.CHKOUT(13) ; use #13 as output channel
return not cbm.READST() return not c64.READST()
} }
f_close_w() f_close_w()
return false return false
@ -394,39 +388,39 @@ _end rts
sub f_write(uword bufferpointer, uword num_bytes) -> bool { sub f_write(uword bufferpointer, uword num_bytes) -> bool {
; -- write the given number of bytes to the currently open file ; -- write the given number of bytes to the currently open file
if num_bytes!=0 { if num_bytes!=0 {
cbm.CHKOUT(13) ; use #13 as output channel again c64.CHKOUT(13) ; use #13 as output channel again
repeat num_bytes { repeat num_bytes {
cbm.CHROUT(@(bufferpointer)) c64.CHROUT(@(bufferpointer))
bufferpointer++ bufferpointer++
} }
return not cbm.READST() return not c64.READST()
} }
return true return true
} }
sub f_close_w() { sub f_close_w() {
; -- end an iterative file writing session (close channels). ; -- end an iterative file writing session (close channels).
cbm.CLRCHN() c64.CLRCHN()
cbm.CLOSE(13) c64.CLOSE(13)
} }
; ---- other functions ---- ; ---- other functions ----
sub status() -> uword { sub status(ubyte drivenumber) -> uword {
; -- retrieve the disk drive's current status message ; -- retrieve the disk drive's current status message
uword messageptr = &list_filename uword messageptr = &list_filename
cbm.SETNAM(0, list_filename) c64.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15) c64.SETLFS(15, drivenumber, 15)
void cbm.OPEN() ; open 15,8,15 void c64.OPEN() ; open 15,8,15
if_cs if_cs
goto io_error goto io_error
void cbm.CHKIN(15) ; use #15 as input channel void c64.CHKIN(15) ; use #15 as input channel
if_cs if_cs
goto io_error goto io_error
while not cbm.READST() { while not c64.READST() {
cx16.r5L = cbm.CHRIN() cx16.r5L = c64.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n' if cx16.r5L=='\r' or cx16.r5L=='\n'
break break
@(messageptr) = cx16.r5L @(messageptr) = cx16.r5L
@ -435,8 +429,8 @@ _end rts
@(messageptr) = 0 @(messageptr) = 0
done: done:
cbm.CLRCHN() ; restore default i/o devices c64.CLRCHN() ; restore default i/o devices
cbm.CLOSE(15) c64.CLOSE(15)
return list_filename return list_filename
io_error: io_error:
@ -444,9 +438,9 @@ io_error:
goto done goto done
} }
sub save(uword filenameptr, uword address, uword size) -> bool { sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> bool {
cbm.SETNAM(string.length(filenameptr), filenameptr) c64.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(1, drivenumber, 0) c64.SETLFS(1, drivenumber, 0)
uword @shared end_address = address + size uword @shared end_address = address + size
cx16.r0L = 0 cx16.r0L = 0
@ -459,17 +453,17 @@ io_error:
lda #<P8ZP_SCRATCH_W1 lda #<P8ZP_SCRATCH_W1
ldx end_address ldx end_address
ldy end_address+1 ldy end_address+1
jsr cbm.SAVE jsr c64.SAVE
php php
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
plp plp
}} }}
if_cc if_cc
cx16.r0L = cbm.READST()==0 cx16.r0L = c64.READST()==0
cbm.CLRCHN() c64.CLRCHN()
cbm.CLOSE(1) c64.CLOSE(1)
return cx16.r0L return cx16.r0L
} }
@ -480,77 +474,99 @@ io_error:
; If you specify a custom address_override, the first 2 bytes in the file are ignored ; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory. ; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred. ; Returns the end load address+1 if successful or 0 if a load error occurred.
sub load(uword filenameptr, uword address_override) -> uword { ; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
cbm.SETNAM(string.length(filenameptr), filenameptr) ; (which is possible on the Commander X16), the returned size is not correct,
ubyte secondary = 1 ; because it doesn't take the number of ram banks into account.
cx16.r1 = 0 ; Also consider using cx16diskio.load() instead on the Commander X16.
if address_override sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
secondary = 0 return internal_load_routine(drivenumber, filenameptr, address_override, false)
cbm.SETLFS(1, drivenumber, secondary)
%asm {{
stx P8ZP_SCRATCH_REG
lda #0
ldx address_override
ldy address_override+1
jsr cbm.LOAD
bcs +
stx cx16.r1
sty cx16.r1+1
+ ldx P8ZP_SCRATCH_REG
}}
cbm.CLRCHN()
cbm.CLOSE(1)
return cx16.r1
} }
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file. ; Use kernal LOAD routine to load the given file in memory.
; No program header is assumed in the file. Everything is loaded. ; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
; See comments on load() for more details. ; This is different from Basic's LOAD instruction which always skips the first two bytes.
sub load_raw(uword filenameptr, uword address) -> uword { ; The load address is mandatory.
; read the 2 header bytes separately to skip them ; Returns the end load address+1 if successful or 0 if a load error occurred.
if not f_open(filenameptr) ; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
; (which is possible on the Commander X16), the returned size is not correct,
; because it doesn't take the number of ram banks into account.
; Also consider using cx16diskio.load_raw() instead on the Commander X16.
sub load_raw(ubyte drivenumber, uword filenameptr, uword address) -> uword {
if sys.target==16 ; are we on commander X16?
return internal_load_routine(drivenumber, filenameptr, address, true)
; fallback to reading the 2 header bytes separately
if not f_open(drivenumber, filenameptr)
return 0 return 0
cx16.r1 = f_read(address, 2) cx16.r1 = f_read(address, 2)
f_close() f_close()
if cx16.r1!=2 if cx16.r1!=2
return 0 return 0
address += 2 address += 2
return load(filenameptr, address) return load(drivenumber, filenameptr, address)
} }
sub delete(uword filenameptr) {
; Internal routine, only to be used on Commander X16 platform if headerless=true,
; because this routine uses kernal support for that to load headerless files.
; On C64 it will always be called with headerless=false.
sub internal_load_routine(ubyte drivenumber, uword filenameptr, uword address_override, bool headerless) -> uword {
c64.SETNAM(string.length(filenameptr), filenameptr)
ubyte secondary = 1
cx16.r1 = 0
if address_override
secondary = 0
if headerless
secondary |= %00000010 ; activate cx16 kernal headerless load support
c64.SETLFS(1, drivenumber, secondary)
%asm {{
stx P8ZP_SCRATCH_REG
lda #0
ldx address_override
ldy address_override+1
jsr c64.LOAD
bcs +
stx cx16.r1
sty cx16.r1+1
+ ldx P8ZP_SCRATCH_REG
}}
c64.CLRCHN()
c64.CLOSE(1)
return cx16.r1
}
sub delete(ubyte drivenumber, uword filenameptr) {
; -- delete a file on the drive ; -- delete a file on the drive
list_filename[0] = 's' list_filename[0] = 's'
list_filename[1] = ':' list_filename[1] = ':'
ubyte flen = string.copy(filenameptr, &list_filename+2) ubyte flen = string.copy(filenameptr, &list_filename+2)
cbm.SETNAM(flen+2, list_filename) c64.SETNAM(flen+2, list_filename)
cbm.SETLFS(1, drivenumber, 15) c64.SETLFS(1, drivenumber, 15)
void cbm.OPEN() void c64.OPEN()
cbm.CLRCHN() c64.CLRCHN()
cbm.CLOSE(1) c64.CLOSE(1)
} }
sub rename(uword oldfileptr, uword newfileptr) { sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
; -- rename a file on the drive ; -- rename a file on the drive
list_filename[0] = 'r' list_filename[0] = 'r'
list_filename[1] = ':' list_filename[1] = ':'
ubyte flen_new = string.copy(newfileptr, &list_filename+2) ubyte flen_new = string.copy(newfileptr, &list_filename+2)
list_filename[flen_new+2] = '=' list_filename[flen_new+2] = '='
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new) ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
cbm.SETNAM(3+flen_new+flen_old, list_filename) c64.SETNAM(3+flen_new+flen_old, list_filename)
cbm.SETLFS(1, drivenumber, 15) c64.SETLFS(1, drivenumber, 15)
void cbm.OPEN() void c64.OPEN()
cbm.CLRCHN() c64.CLRCHN()
cbm.CLOSE(1) c64.CLOSE(1)
} }
sub send_command(uword commandptr) { sub send_command(ubyte drivenumber, uword commandptr) {
; -- send a dos command to the drive ; -- send a dos command to the drive
cbm.SETNAM(string.length(commandptr), commandptr) c64.SETNAM(string.length(commandptr), commandptr)
cbm.SETLFS(15, drivenumber, 15) c64.SETLFS(15, drivenumber, 15)
void cbm.OPEN() void c64.OPEN()
cbm.CLRCHN() c64.CLRCHN()
cbm.CLOSE(15) c64.CLOSE(15)
} }
} }

View File

@ -1,6 +1,6 @@
floats { floats {
; the floating point functions shared across compiler targets ; the floating point functions shared across compiler targets
%option merge, no_symbol_prefixing %option merge
sub print_f(float value) { sub print_f(float value) {
; ---- prints the floating point value (without a newline). ; ---- prints the floating point value (without a newline).
@ -15,7 +15,7 @@ sub print_f(float value) {
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_W1),y - lda (P8ZP_SCRATCH_W1),y
beq + beq +
jsr cbm.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
+ ldx floats_store_reg + ldx floats_store_reg
@ -39,6 +39,18 @@ sub pow(float value, float power) -> float {
}} }}
} }
sub fabs(float value) -> float {
%asm {{
stx P8ZP_SCRATCH_REG
lda #<value
ldy #>value
jsr MOVFM
jsr ABS
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub sin(float angle) -> float { sub sin(float angle) -> float {
%asm {{ %asm {{
lda #<angle lda #<angle
@ -116,6 +128,18 @@ sub log2(float value) -> float {
}} }}
} }
sub sqrt(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub rad(float angle) -> float { sub rad(float angle) -> float {
; -- convert degrees to radians (d * pi / 180) ; -- convert degrees to radians (d * pi / 180)
%asm {{ %asm {{
@ -213,27 +237,4 @@ sub rndseedf(float seed) {
}} }}
} }
sub minf(float f1, float f2) -> float {
if f1<f2
return f1
return f2
}
sub maxf(float f1, float f2) -> float {
if f1>f2
return f1
return f2
}
sub clampf(float value, float minimum, float maximum) -> float {
if value>maximum
value=maximum
if value>minimum
return value
return minimum
}
} }

View File

@ -51,7 +51,8 @@ multiply_bytes_into_word .proc
multiply_words .proc multiply_words .proc
; -- multiply two 16-bit words into a 32-bit result (signed and unsigned) ; -- multiply two 16-bit words into a 32-bit result (signed and unsigned)
; input: A/Y = first 16-bit number, P8ZP_SCRATCH_W1 in ZP = second 16-bit number ; input: A/Y = first 16-bit number, P8ZP_SCRATCH_W1 in ZP = second 16-bit number
; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY. ; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high)
; clobbers: A
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_W2+1
@ -78,8 +79,6 @@ mult16 lda #0
dex dex
bne - bne -
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
lda result
ldy result+1
rts rts
result .byte 0,0,0,0 result .byte 0,0,0,0

View File

@ -1,8 +1,6 @@
; Internal Math library routines - always included by the compiler ; Internal Math library routines - always included by the compiler
math { math {
%option no_symbol_prefixing
%asminclude "library:math.asm" %asminclude "library:math.asm"
asmsub sin8u(ubyte angle @A) clobbers(Y) -> ubyte @A { asmsub sin8u(ubyte angle @A) clobbers(Y) -> ubyte @A {
@ -97,296 +95,4 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
rts rts
}} }}
} }
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
cx16.r0L = 3 ; quadrant
cx16.r1sL = x2-x1 ; xdelta
if_neg {
cx16.r0L--
cx16.r1sL = -cx16.r1sL
}
cx16.r2sL = y2-y1 ; ydelta
if_neg {
cx16.r0L-=2
cx16.r2sL = -cx16.r2sL
}
return direction_qd(cx16.r0L, cx16.r1L, cx16.r2L)
}
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
cx16.r0L = 3 ; quadrant
if x2>=x1 {
cx16.r1L = x2-x1
} else {
cx16.r1L = x1-x2
cx16.r0L--
}
if y2>=y1 {
cx16.r2L = y2-y1
} else {
cx16.r2L = y1-y2
cx16.r0L -= 2
}
return direction_qd(cx16.r0L, cx16.r1L, cx16.r2L)
}
asmsub direction_qd(ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A {
;Arctan https://github.com/dustmop/arctan24
; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23 into A.
; .reg:a @in quadrant Number 0 to 3.
; .reg:x @in x_delta Delta for x direction.
; .reg:y @in y_delta Delta for y direction.
; Returns A as the direction (0-23).
%asm {{
x_delta = cx16.r0L
y_delta = cx16.r1L
quadrant = cx16.r2L
half_value = cx16.r3L
region_number = cx16.r4L
small = cx16.r5L
large = cx16.r5H
sta quadrant
sty y_delta
stx x_delta
cpx y_delta
bcs _XGreaterOrEqualY
_XLessY:
lda #16
sta region_number
stx small
sty large
bne _DetermineRegion
_XGreaterOrEqualY:
lda #0
sta region_number
stx large
sty small
_DetermineRegion:
; set A = small * 2.5
lda small
lsr a
sta half_value
lda small
asl a
bcs _SmallerQuotient
clc
adc half_value
bcs _SmallerQuotient
cmp large
bcc _LargerQuotient
; S * 2.5 > L
_SmallerQuotient:
; set A = S * 1.25
lsr half_value
lda small
clc
adc half_value
cmp large
bcc _Region1 ; if S * 1.25 < L then goto Region1 (L / S > 1.25)
bcs _Region0 ; (L / S < 1.25)
; S * 2.5 < L
_LargerQuotient:
; set A = S * 7.5
lda small
asl a
asl a
asl a
bcs _Region2
sec
sbc half_value
cmp large
bcc _Region3 ; if S * 7.5 < L then goto Region3 (L / S > 7.5)
jmp _Region2 ; (L / S < 7.5)
_Region0:
; L / S < 1.25. d=3,9,15,21
jmp _LookupResult
_Region1:
; 1.25 < L / S < 2.5. d=2,4,8,10,14,16,20,22
lda region_number
clc
adc #4
sta region_number
bpl _LookupResult
_Region2:
; 2.5 < L / S < 7.5. d=1,5,7,11,13,17,19,23
lda region_number
clc
adc #8
sta region_number
bpl _LookupResult
_Region3:
; 7.5 < L / S. d=0,6,12,18
lda region_number
clc
adc #12
sta region_number
_LookupResult:
lda quadrant
clc
adc region_number
tax
lda _quadrant_region_to_direction,x
rts
_quadrant_region_to_direction:
.byte 9, 3,15,21
.byte 10, 2,14,22
.byte 11, 1,13,23
.byte 12, 0,12, 0
.byte 9, 3,15,21
.byte 8, 4,16,20
.byte 7, 5,17,19
.byte 6, 6,18,18
}}
}
asmsub atan2(ubyte x1 @R0, ubyte y1 @R1, ubyte x2 @R2, ubyte y2 @R3) -> ubyte @A {
;; Calculate the angle, in a 256-degree circle, between two points into A.
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
;; https://www.codebase64.org/doku.php?id=base:8bit_atan2_8-bit_angle
;; This uses 2 large lookup tables so uses a lot of memory but is super fast.
%asm {{
x1 = cx16.r0L
y1 = cx16.r1L
x2 = cx16.r2L
y2 = cx16.r3L
octant = cx16.r4L ;; temporary zeropage variable
lda x1
sec
sbc x2
bcs *+4
eor #$ff
tax
rol octant
lda y1
sec
sbc y2
bcs *+4
eor #$ff
tay
rol octant
lda log2_tab,x
sec
sbc log2_tab,y
bcc *+4
eor #$ff
tax
lda octant
rol a
and #%111
tay
lda atan_tab,x
eor octant_adjust,y
rts
octant_adjust
.byte %00111111 ;; x+,y+,|x|>|y|
.byte %00000000 ;; x+,y+,|x|<|y|
.byte %11000000 ;; x+,y-,|x|>|y|
.byte %11111111 ;; x+,y-,|x|<|y|
.byte %01000000 ;; x-,y+,|x|>|y|
.byte %01111111 ;; x-,y+,|x|<|y|
.byte %10111111 ;; x-,y-,|x|>|y|
.byte %10000000 ;; x-,y-,|x|<|y|
;;;;;;;; atan(2^(x/32))*128/pi ;;;;;;;;
atan_tab
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$01,$01,$01
.byte $01,$01,$01,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$02,$02,$02
.byte $02,$02,$02,$02,$02,$02,$02,$02
.byte $02,$02,$02,$02,$02,$02,$02,$02
.byte $03,$03,$03,$03,$03,$03,$03,$03
.byte $03,$03,$03,$03,$03,$04,$04,$04
.byte $04,$04,$04,$04,$04,$04,$04,$04
.byte $05,$05,$05,$05,$05,$05,$05,$05
.byte $06,$06,$06,$06,$06,$06,$06,$06
.byte $07,$07,$07,$07,$07,$07,$08,$08
.byte $08,$08,$08,$08,$09,$09,$09,$09
.byte $09,$0a,$0a,$0a,$0a,$0b,$0b,$0b
.byte $0b,$0c,$0c,$0c,$0c,$0d,$0d,$0d
.byte $0d,$0e,$0e,$0e,$0e,$0f,$0f,$0f
.byte $10,$10,$10,$11,$11,$11,$12,$12
.byte $12,$13,$13,$13,$14,$14,$15,$15
.byte $15,$16,$16,$17,$17,$17,$18,$18
.byte $19,$19,$19,$1a,$1a,$1b,$1b,$1c
.byte $1c,$1c,$1d,$1d,$1e,$1e,$1f,$1f
;;;;;;;; log2(x)*32 ;;;;;;;;
log2_tab
.byte $00,$00,$20,$32,$40,$4a,$52,$59
.byte $60,$65,$6a,$6e,$72,$76,$79,$7d
.byte $80,$82,$85,$87,$8a,$8c,$8e,$90
.byte $92,$94,$96,$98,$99,$9b,$9d,$9e
.byte $a0,$a1,$a2,$a4,$a5,$a6,$a7,$a9
.byte $aa,$ab,$ac,$ad,$ae,$af,$b0,$b1
.byte $b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9
.byte $b9,$ba,$bb,$bc,$bd,$bd,$be,$bf
.byte $c0,$c0,$c1,$c2,$c2,$c3,$c4,$c4
.byte $c5,$c6,$c6,$c7,$c7,$c8,$c9,$c9
.byte $ca,$ca,$cb,$cc,$cc,$cd,$cd,$ce
.byte $ce,$cf,$cf,$d0,$d0,$d1,$d1,$d2
.byte $d2,$d3,$d3,$d4,$d4,$d5,$d5,$d5
.byte $d6,$d6,$d7,$d7,$d8,$d8,$d9,$d9
.byte $d9,$da,$da,$db,$db,$db,$dc,$dc
.byte $dd,$dd,$dd,$de,$de,$de,$df,$df
.byte $df,$e0,$e0,$e1,$e1,$e1,$e2,$e2
.byte $e2,$e3,$e3,$e3,$e4,$e4,$e4,$e5
.byte $e5,$e5,$e6,$e6,$e6,$e7,$e7,$e7
.byte $e7,$e8,$e8,$e8,$e9,$e9,$e9,$ea
.byte $ea,$ea,$ea,$eb,$eb,$eb,$ec,$ec
.byte $ec,$ec,$ed,$ed,$ed,$ed,$ee,$ee
.byte $ee,$ee,$ef,$ef,$ef,$ef,$f0,$f0
.byte $f0,$f1,$f1,$f1,$f1,$f1,$f2,$f2
.byte $f2,$f2,$f3,$f3,$f3,$f3,$f4,$f4
.byte $f4,$f4,$f5,$f5,$f5,$f5,$f5,$f6
.byte $f6,$f6,$f6,$f7,$f7,$f7,$f7,$f7
.byte $f8,$f8,$f8,$f8,$f9,$f9,$f9,$f9
.byte $f9,$fa,$fa,$fa,$fa,$fa,$fb,$fb
.byte $fb,$fb,$fb,$fc,$fc,$fc,$fc,$fc
.byte $fd,$fd,$fd,$fd,$fd,$fd,$fe,$fe
.byte $fe,$fe,$fe,$ff,$ff,$ff,$ff,$ff
}}
}
} }

View File

@ -86,15 +86,16 @@ func_all_w_stack .proc
abs_b_stack .proc abs_b_stack .proc
; -- push abs(A) on stack (as unsigned word) ; -- push abs(A) on stack (as unsigned word)
jsr abs_b_into_A jsr abs_b_into_AY
sta P8ESTACK_LO,x sta P8ESTACK_LO,x
stz P8ESTACK_HI,x stz P8ESTACK_HI,x
dex dex
rts rts
.pend .pend
abs_b_into_A .proc abs_b_into_AY .proc
; -- A = abs(A) ; -- AY = abs(A) (abs always returns unsigned word)
ldy #0
cmp #0 cmp #0
bmi + bmi +
rts rts
@ -555,111 +556,3 @@ func_pokew .proc
sta (P8ZP_SCRATCH_W1),y sta (P8ZP_SCRATCH_W1),y
rts rts
.pend .pend
func_clamp_byte .proc
; signed value in A, result in A
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W1+1
tay
sec
sbc P8ZP_SCRATCH_W1+1
bvc +
eor #$80
+ bmi +
lda P8ZP_SCRATCH_W1+1
tay
jmp ++
+ tya
+ sec
sbc P8ZP_SCRATCH_W1
bvc +
eor #$80
+ bmi +
tya
rts
+ lda P8ZP_SCRATCH_W1
rts
.pend
func_clamp_ubyte .proc
; value in A, result in A
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W1+1
bcc +
lda P8ZP_SCRATCH_W1+1
+ cmp P8ZP_SCRATCH_W1
bcc +
rts
+ lda P8ZP_SCRATCH_W1
rts
.pend
func_clamp_word .proc
; signed value in AY, result in AY
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_W2
cmp P8ZP_SCRATCH_B1
tya
sbc P8ZP_SCRATCH_REG
bvc +
eor #$80
+ bpl +
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
+ ldy P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
cmp P8ZP_SCRATCH_B1
tya
sbc P8ZP_SCRATCH_REG
bvc +
eor #$80
+ bpl +
ldy P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_B1
rts
+ ldy P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
rts
.pend
func_clamp_uword .proc
; value in AY, result in AY
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
cpy P8ZP_SCRATCH_W2+1
bcc ++
bne +
cmp P8ZP_SCRATCH_W2
bcc ++
+ beq +
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
+ ldy P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_B1
cpy P8ZP_SCRATCH_W1+1
bcc ++
bne +
cmp P8ZP_SCRATCH_W1
bcc ++
+ beq +
ldy P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_B1
rts
+ ldy P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
rts
.pend

View File

@ -167,8 +167,9 @@ mul_word .proc
lda P8ESTACK_LO+1,x lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x ldy P8ESTACK_HI+1,x
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta P8ESTACK_LO+1,x sta P8ESTACK_LO+1,x
tya lda math.multiply_words.result+1
sta P8ESTACK_HI+1,x sta P8ESTACK_HI+1,x
rts rts
.pend .pend

View File

@ -1,8 +1,6 @@
; Internal library routines - always included by the compiler ; Internal library routines - always included by the compiler
prog8_lib { prog8_lib {
%option no_symbol_prefixing
%asminclude "library:prog8_lib.asm" %asminclude "library:prog8_lib.asm"
%asminclude "library:prog8_funcs.asm" %asminclude "library:prog8_funcs.asm"
} }

View File

@ -1,7 +1,6 @@
; 0-terminated string manipulation routines. ; 0-terminated string manipulation routines.
string { string {
%option no_symbol_prefixing
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y { asmsub length(uword string @AY) clobbers(A) -> ubyte @Y {
; Returns the number of bytes in the string. ; Returns the number of bytes in the string.
@ -128,7 +127,7 @@ _startloop dey
}} }}
} }
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, bool @Pc { asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc {
; Locates the first position of the given character in the string, ; Locates the first position of the given character in the string,
; returns Carry set if found + index in A, or A=0 + Carry clear if not found. ; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
%asm {{ %asm {{

View File

@ -3,7 +3,6 @@
%import textio %import textio
test_stack { test_stack {
%option no_symbol_prefixing
asmsub test() { asmsub test() {
%asm {{ %asm {{

View File

@ -106,21 +106,19 @@ sub str_uwhex (uword value) {
sub str_uw0 (uword value) { sub str_uw0 (uword value) {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total) ; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
uword value2 = value/10 ubyte tenthousands = (value / 10000) as ubyte
ubyte digits = value-value2*10 as ubyte value -= 10000*tenthousands
uword value3 = value2/10 ubyte thousands = (value / 1000) as ubyte
ubyte tens = value2-value3*10 as ubyte value -= 1000*thousands
uword value4 = value3/10 ubyte hundreds = (value / 100) as ubyte
ubyte hundreds = value3-value4*10 as ubyte value -= 100 as uword * hundreds
uword value5 = value4/10 ubyte tens = (value / 10) as ubyte
ubyte thousands = value4-value5*10 as ubyte value -= 10*tens
uword value6 = value5/10
ubyte tenthousands = value5-value6*10 as ubyte
string_out[0] = tenthousands+'0' string_out[0] = tenthousands+'0'
string_out[1] = thousands+'0' string_out[1] = thousands+'0'
string_out[2] = hundreds+'0' string_out[2] = hundreds+'0'
string_out[3] = tens+'0' string_out[3] = tens+'0'
string_out[4] = digits+'0' string_out[4] = value as ubyte + '0'
string_out[5] = 0 string_out[5] = 0
} }
@ -141,16 +139,14 @@ sub str_w (word value) {
} }
sub internal_str_uw(uword value, uword out_ptr) { sub internal_str_uw(uword value, uword out_ptr) {
uword value2 = value/10 ubyte tenthousands = (value / 10000) as ubyte
ubyte digits = value-value2*10 as ubyte value -= 10000*tenthousands
uword value3 = value2/10 ubyte thousands = (value / 1000) as ubyte
ubyte tens = value2-value3*10 as ubyte value -= 1000*thousands
uword value4 = value3/10 ubyte hundreds = (value / 100) as ubyte
ubyte hundreds = value3-value4*10 as ubyte value -= 100 as uword * hundreds
uword value5 = value4/10 ubyte tens = (value / 10) as ubyte
ubyte thousands = value4-value5*10 as ubyte value -= 10*tens
uword value6 = value5/10
ubyte tenthousands = value5-value6*10 as ubyte
if tenthousands if tenthousands
goto output_tenthousands goto output_tenthousands
if thousands if thousands
@ -173,7 +169,7 @@ output_tens:
@(out_ptr) = tens+'0' @(out_ptr) = tens+'0'
out_ptr++ out_ptr++
output_ones: output_ones:
@(out_ptr) = digits+'0' @(out_ptr) = value as ubyte + '0'
out_ptr++ out_ptr++
@(out_ptr) = 0 @(out_ptr) = 0
} }

View File

@ -1 +0,0 @@
; there is no diskio yet for the VM target.

View File

@ -25,6 +25,14 @@ sub pow(float value, float power) -> float {
}} }}
} }
sub fabs(float value) -> float {
%ir {{
loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0
returnr.f fr0
}}
}
sub sin(float angle) -> float { sub sin(float angle) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.sin.angle loadm.f fr0,floats.sin.angle
@ -73,6 +81,14 @@ sub log2(float value) -> float {
}} }}
} }
sub sqrt(float value) -> float {
%ir {{
loadm.f fr0,floats.sqrt.value
sqrt.f fr0,fr0
returnr.f fr0
}}
}
sub rad(float angle) -> float { sub rad(float angle) -> float {
; -- convert degrees to radians (d * pi / 180) ; -- convert degrees to radians (d * pi / 180)
return angle * PI / 180.0 return angle * PI / 180.0
@ -123,27 +139,4 @@ sub rndseedf(float seed) {
}} }}
} }
sub minf(float f1, float f2) -> float {
if f1<f2
return f1
return f2
}
sub maxf(float f1, float f2) -> float {
if f1>f2
return f1
return f2
}
sub clampf(float value, float minimum, float maximum) -> float {
if value<minimum
value=minimum
if value<maximum
return value
return maximum
}
} }

View File

@ -182,61 +182,4 @@ math {
return return
}} }}
} }
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
; This adjusts the atan() result so that the direction N is centered on the angle=N instead of having it as a boundary
ubyte angle = atan2(x1, y1, x2, y2) - 256/48
return 23-lsb(mkword(angle,0) / 2730)
}
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
; shift the points into the positive quadrant
ubyte px1
ubyte py1
ubyte px2
ubyte py2
if x1<0 or x2<0 {
px1 = x1 as ubyte + 128
px2 = x2 as ubyte + 128
} else {
px1 = x1 as ubyte
px2 = x2 as ubyte
}
if y1<0 or y2<0 {
py1 = y1 as ubyte + 128
py2 = y2 as ubyte + 128
} else {
py1 = y1 as ubyte
py2 = y2 as ubyte
}
return direction(px1, py1, px2, py2)
}
sub direction_qd(ubyte quadrant, ubyte xdelta, ubyte ydelta) -> ubyte {
; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23.
when quadrant {
3 -> return direction(0, 0, xdelta, ydelta)
2 -> return direction(xdelta, 0, 0, ydelta)
1 -> return direction(0, ydelta, xdelta, 0)
else -> return direction(xdelta, ydelta, 0, 0)
}
}
sub atan2(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
;; Calculate the angle, in a 256-degree circle, between two points into A.
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
%ir {{
loadm.b r65532,math.atan2.x1
loadm.b r65533,math.atan2.y1
loadm.b r65534,math.atan2.x2
loadm.b r65535,math.atan2.y2
syscall 44 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b
returnr.b r0
}}
}
} }

View File

@ -218,43 +218,4 @@ cx16 {
&byte r13sH = $ff1d &byte r13sH = $ff1d
&byte r14sH = $ff1f &byte r14sH = $ff1f
&byte r15sH = $ff21 &byte r15sH = $ff21
sub save_virtual_registers() {
uword[32] storage
storage[0] = r0
storage[1] = r1
storage[2] = r2
storage[3] = r3
storage[4] = r4
storage[5] = r5
storage[6] = r6
storage[7] = r7
storage[8] = r8
storage[9] = r9
storage[10] = r10
storage[11] = r11
storage[12] = r12
storage[13] = r13
storage[14] = r14
storage[15] = r15
}
sub restore_virtual_registers() {
r0 = cx16.save_virtual_registers.storage[0]
r1 = cx16.save_virtual_registers.storage[1]
r2 = cx16.save_virtual_registers.storage[2]
r3 = cx16.save_virtual_registers.storage[3]
r4 = cx16.save_virtual_registers.storage[4]
r5 = cx16.save_virtual_registers.storage[5]
r6 = cx16.save_virtual_registers.storage[6]
r7 = cx16.save_virtual_registers.storage[7]
r8 = cx16.save_virtual_registers.storage[8]
r9 = cx16.save_virtual_registers.storage[9]
r10 = cx16.save_virtual_registers.storage[10]
r11 = cx16.save_virtual_registers.storage[11]
r12 = cx16.save_virtual_registers.storage[12]
r13 = cx16.save_virtual_registers.storage[13]
r14 = cx16.save_virtual_registers.storage[14]
r15 = cx16.save_virtual_registers.storage[15]
}
} }

1
compiler/res/version.txt Normal file
View File

@ -0,0 +1 @@
8.14

View File

@ -20,10 +20,8 @@ import kotlin.system.exitProcess
fun main(args: Array<String>) { fun main(args: Array<String>) {
println("\nProg8 compiler v${prog8.buildversion.VERSION} by Irmen de Jong (irmen@razorvine.net)") val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim()
if('-' in prog8.buildversion.VERSION) { println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
println("Prerelease version from git commit ${prog8.buildversion.GIT_SHA.take(8)} in branch ${prog8.buildversion.GIT_BRANCH}")
}
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
val succes = compileMain(args) val succes = compileMain(args)
@ -50,11 +48,11 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results") val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation") val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator) val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}') (required)") val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME)
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM") val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)") val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank (0=keep active), on other systems it is ignored.") val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory") val useNewExprCode by cli.option(ArgType.Boolean, fullName = "newexpr", description = "use new expression code-gen (experimental)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999) val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try { try {
@ -80,20 +78,8 @@ private fun compileMain(args: Array<String>): Boolean {
if(srcdirs.firstOrNull()!=".") if(srcdirs.firstOrNull()!=".")
srcdirs.add(0, ".") srcdirs.add(0, ".")
if(startVm==null) { if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) {
if(compilationTarget==null) { System.err.println("Invalid compilation target: $compilationTarget")
System.err.println("No compilation target specified")
return false
}
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) {
System.err.println("Invalid compilation target: $compilationTarget")
return false
}
}
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
return false return false
} }
@ -140,10 +126,10 @@ private fun compileMain(args: Array<String>): Boolean {
quietAssembler == true, quietAssembler == true,
asmListfile == true, asmListfile == true,
experimentalCodegen == true, experimentalCodegen == true,
varsHighBank, varsHigh == true,
compilationTarget!!, useNewExprCode == true,
compilationTarget,
evalStackAddr, evalStackAddr,
splitWordArrays == true,
processedSymbols, processedSymbols,
srcdirs, srcdirs,
outputPath outputPath
@ -192,10 +178,6 @@ private fun compileMain(args: Array<String>): Boolean {
} }
} else { } else {
if((startEmulator1==true || startEmulator2==true) && moduleFiles.size>1) {
System.err.println("can't start emulator when multiple module files are specified")
return false
}
for(filepathRaw in moduleFiles) { for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize() val filepath = pathFrom(filepathRaw).normalize()
val compilationResult: CompilationResult val compilationResult: CompilationResult
@ -209,10 +191,10 @@ private fun compileMain(args: Array<String>): Boolean {
quietAssembler == true, quietAssembler == true,
asmListfile == true, asmListfile == true,
experimentalCodegen == true, experimentalCodegen == true,
varsHighBank, varsHigh == true,
compilationTarget!!, useNewExprCode == true,
compilationTarget,
evalStackAddr, evalStackAddr,
splitWordArrays == true,
processedSymbols, processedSymbols,
srcdirs, srcdirs,
outputPath outputPath

View File

@ -1,15 +0,0 @@
package prog8.buildversion
/**
* Automatically generated file containing build version information.
*/
const val MAVEN_GROUP = "prog8"
const val MAVEN_NAME = "compiler"
const val VERSION = "9.2-SNAPSHOT"
const val GIT_REVISION = 3964
const val GIT_SHA = "aaa30e4a583fa7c8da61998cf7cbb2a60b52afb0"
const val GIT_DATE = "2023-07-16T21:16:18Z"
const val GIT_BRANCH = "master"
const val BUILD_DATE = "2023-07-16T21:16:23Z"
const val BUILD_UNIX_TIME = 1689542183583L
const val DIRTY = 1

View File

@ -7,7 +7,10 @@ import prog8.ast.base.SyntaxError
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.core.* import prog8.code.core.*
import kotlin.math.* import kotlin.math.abs
import kotlin.math.sign
import kotlin.math.sqrt
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
@ -16,26 +19,12 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"len" to ::builtinLen, "len" to ::builtinLen,
"sizeof" to ::builtinSizeof, "sizeof" to ::builtinSizeof,
"sgn" to ::builtinSgn, "sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } }, "sqrt16" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
"any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, "any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
"all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, "all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } }, "lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} }, "msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
"mkword" to ::builtinMkword, "mkword" to ::builtinMkword
"clamp__ubyte" to ::builtinClampUByte,
"clamp__byte" to ::builtinClampByte,
"clamp__uword" to ::builtinClampUWord,
"clamp__word" to ::builtinClampWord,
"min__ubyte" to ::builtinMinUByte,
"min__byte" to ::builtinMinByte,
"min__uword" to ::builtinMinUWord,
"min__word" to ::builtinMinWord,
"max__ubyte" to ::builtinMaxUByte,
"max__byte" to ::builtinMaxByte,
"max__uword" to ::builtinMaxUWord,
"max__word" to ::builtinMaxWord
) )
private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0 private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0
@ -70,16 +59,6 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position) return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position)
} }
private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Double): NumericLiteral {
if(args.size!=1)
throw SyntaxError("built-in function requires one float argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type != DataType.FLOAT)
throw SyntaxError("built-in function requires one float argument", position)
return NumericLiteral(DataType.FLOAT, function(constval.number), args[0].position)
}
private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Double): NumericLiteral { private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Double): NumericLiteral {
if(args.size!=1) if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position) throw SyntaxError("builtin function requires one non-scalar argument", position)
@ -177,116 +156,3 @@ private fun builtinSgn(args: List<Expression>, position: Position, program: Prog
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
return NumericLiteral(DataType.BYTE, constval.number.sign, position) return NumericLiteral(DataType.BYTE, constval.number.sign, position)
} }
private fun builtinMinByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.BYTE, result.toDouble(), position)
}
private fun builtinMinUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UBYTE, result.toDouble(), position)
}
private fun builtinMinWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.WORD, result.toDouble(), position)
}
private fun builtinMinUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
}
private fun builtinMaxByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.BYTE, result.toDouble(), position)
}
private fun builtinMaxUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UBYTE, result.toDouble(), position)
}
private fun builtinMaxWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.WORD, result.toDouble(), position)
}
private fun builtinMaxUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
}
private fun builtinClampUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.UBYTE, result, position)
}
private fun builtinClampByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.BYTE, result, position)
}
private fun builtinClampUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.UWORD, result, position)
}
private fun builtinClampWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.WORD, result, position)
}

View File

@ -4,11 +4,12 @@ import com.github.michaelbull.result.onFailure
import prog8.ast.IBuiltinFunctions import prog8.ast.IBuiltinFunctions
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.AstException import prog8.ast.base.AstException
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.PtProgram import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.* import prog8.code.target.*
import prog8.codegen.vm.VmCodeGen import prog8.codegen.vm.VmCodeGen
@ -35,10 +36,10 @@ class CompilerArguments(val filepath: Path,
val quietAssembler: Boolean, val quietAssembler: Boolean,
val asmListfile: Boolean, val asmListfile: Boolean,
val experimentalCodegen: Boolean, val experimentalCodegen: Boolean,
val varsHighBank: Int?, val varsHigh: Boolean,
val useNewExprCode: Boolean,
val compilationTarget: String, val compilationTarget: String,
val evalStackBaseAddress: UInt?, val evalStackBaseAddress: UInt?,
val splitWordArrays: Boolean,
val symbolDefs: Map<String, String>, val symbolDefs: Map<String, String>,
val sourceDirs: List<String> = emptyList(), val sourceDirs: List<String> = emptyList(),
val outputDir: Path = Path(""), val outputDir: Path = Path(""),
@ -76,9 +77,9 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
asmQuiet = args.quietAssembler asmQuiet = args.quietAssembler
asmListfile = args.asmListfile asmListfile = args.asmListfile
experimentalCodegen = args.experimentalCodegen experimentalCodegen = args.experimentalCodegen
varsHighBank = args.varsHighBank varsHigh = args.varsHigh
useNewExprCode = args.useNewExprCode
evalStackBaseAddress = args.evalStackBaseAddress evalStackBaseAddress = args.evalStackBaseAddress
splitWordArrays = args.splitWordArrays
outputDir = args.outputDir.normalize() outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs symbolDefs = args.symbolDefs
} }
@ -280,9 +281,10 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase() val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase()
val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.uppercase() as? Directive)?.args?.single()?.name?.uppercase()
val allOptions = program.modules.flatMap { it.options() }.toSet() val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
val floatsEnabled = "enable_floats" in allOptions .flatMap { (it as Directive).args }.toSet()
val noSysInit = "no_sysinit" in allOptions val floatsEnabled = allOptions.any { it.name == "enable_floats" }
val noSysInit = allOptions.any { it.name == "no_sysinit" }
val zpType: ZeropageType = val zpType: ZeropageType =
if (zpoption == null) if (zpoption == null)
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
@ -368,8 +370,8 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
// keep optimizing expressions and statements until no more steps remain // keep optimizing expressions and statements until no more steps remain
val optsDone1 = program.simplifyExpressions(errors, compTarget) val optsDone1 = program.simplifyExpressions(errors, compTarget)
val optsDone2 = program.splitBinaryExpressions(compilerOptions) val optsDone2 = program.splitBinaryExpressions(compilerOptions)
val optsDone3 = program.optimizeStatements(errors, functions, compilerOptions) val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
val optsDone4 = program.inlineSubroutines(compilerOptions) val optsDone4 = program.inlineSubroutines()
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
errors.report() errors.report()
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0) if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
@ -403,12 +405,23 @@ private fun createAssemblyAndAssemble(program: PtProgram,
val asmgen = if(compilerOptions.experimentalCodegen) val asmgen = if(compilerOptions.experimentalCodegen)
prog8.codegen.experimental.ExperiCodeGen() prog8.codegen.experimental.ExperiCodeGen()
else if (compilerOptions.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) else if (compilerOptions.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
prog8.codegen.cpu6502.AsmGen6502(prefixSymbols = true) prog8.codegen.cpu6502.AsmGen6502()
else if (compilerOptions.compTarget.name == VMTarget.NAME) else if (compilerOptions.compTarget.name == VMTarget.NAME)
VmCodeGen() VmCodeGen()
else else
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}") throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}")
if(compilerOptions.useNewExprCode) {
if(compilerOptions.compTarget.machine.cpu !in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
// the IR code gen backend has its own, better, version of dealing with binary expressions.
throw IllegalArgumentException("'newexpr' expression rewrite should not be used with compilation target ${compilerOptions.compTarget.name}")
}
transformNewExpressions(program)
}
// printAst(program, true) { println(it) }
val stMaker = SymbolTableMaker(program, compilerOptions) val stMaker = SymbolTableMaker(program, compilerOptions)
val symbolTable = stMaker.make() val symbolTable = stMaker.make()
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors) val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
@ -420,3 +433,188 @@ private fun createAssemblyAndAssemble(program: PtProgram,
false false
} }
} }
private fun transformNewExpressions(program: PtProgram) {
val newVariables = mutableMapOf<PtSub, MutableList<PtVariable>>()
var countByteVars = 0
var countWordVars = 0
var countFloatVars = 0
// TODO: find a reliable way to reuse more temp vars across expressions
fun getExprVar(type: DataType, pos: Position, scope: PtSub): PtIdentifier {
val count = when(type) {
in ByteDatatypes -> {
countByteVars++
countByteVars
}
in WordDatatypes -> {
countWordVars++
countWordVars
}
DataType.FLOAT -> {
countFloatVars++
countFloatVars
}
else -> throw FatalAstException("weird dt")
}
val name = "p8p_exprvar_${count}_${type.toString().lowercase()}"
var subVars = newVariables[scope]
if(subVars==null) {
subVars = mutableListOf()
newVariables[scope] = subVars
}
if(subVars.all { it.name!=name }) {
subVars.add(PtVariable(name, type, ZeropageWish.DONTCARE, null, null, pos))
}
return PtIdentifier("${scope.scopedName}.$name", type, pos)
}
fun transformExpr(expr: PtBinaryExpression): Pair<PtExpression, List<IPtAssignment>> {
// depth first process the expression tree
val scope = expr.definingSub()!!
val assignments = mutableListOf<IPtAssignment>()
fun transformOperand(node: PtExpression): PtNode {
return when(node) {
is PtNumber, is PtIdentifier, is PtArray, is PtString, is PtMachineRegister -> node
is PtBinaryExpression -> {
val (replacement, subAssigns) = transformExpr(node)
assignments.addAll(subAssigns)
replacement
}
else -> {
val variable = getExprVar(node.type, node.position, scope)
val assign = PtAssignment(node.position)
val target = PtAssignTarget(variable.position)
target.add(variable)
assign.add(target)
assign.add(node)
assignments.add(assign)
variable
}
}
}
val newLeft = transformOperand(expr.left)
val newRight = transformOperand(expr.right)
// process the binexpr
val resultVar =
if(expr.type == expr.left.type) {
getExprVar(expr.type, expr.position, scope)
} else {
if(expr.operator in ComparisonOperators && expr.type in ByteDatatypes) {
// this is very common and should be dealth with correctly; byte==0, word>42
val varType = if(expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
getExprVar(varType, expr.position, scope)
}
else if(expr.left.type in PassByReferenceDatatypes && expr.type==DataType.UBYTE) {
// this is common and should be dealth with correctly; for instance "name"=="john"
val varType = if (expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
getExprVar(varType, expr.position, scope)
} else if(expr.left.type equalsSize expr.type) {
getExprVar(expr.type, expr.position, scope)
} else {
TODO("expression type differs from left operand type! got ${expr.left.type} expected ${expr.type} ${expr.position}")
}
}
if(resultVar.name!=(newLeft as? PtIdentifier)?.name) {
// resultvar = left
val assign1 = PtAssignment(newLeft.position)
val target1 = PtAssignTarget(resultVar.position)
target1.add(resultVar)
assign1.add(target1)
assign1.add(newLeft)
assignments.add(assign1)
}
// resultvar {oper}= right
val operator = if(expr.operator in ComparisonOperators) expr.operator else expr.operator+'='
val assign2 = PtAugmentedAssign(operator, newRight.position)
val target2 = PtAssignTarget(resultVar.position)
target2.add(resultVar.copy())
assign2.add(target2)
assign2.add(newRight)
assignments.add(assign2)
return Pair(resultVar, assignments)
}
fun isProperStatement(node: PtNode): Boolean {
return when(node) {
is PtAssignment -> true
is PtAugmentedAssign -> true
is PtBreakpoint -> true
is PtConditionalBranch -> true
is PtForLoop -> true
is PtIfElse -> true
is PtIncludeBinary -> true
is PtInlineAssembly -> true
is PtJump -> true
is PtAsmSub -> true
is PtLabel -> true
is PtSub -> true
is PtVariable -> true
is PtNop -> true
is PtPostIncrDecr -> true
is PtRepeatLoop -> true
is PtReturn -> true
is PtWhen -> true
is PtBuiltinFunctionCall -> node.void
is PtFunctionCall -> node.void
else -> false
}
}
fun transform(node: PtNode, parent: PtNode) {
if(node is PtBinaryExpression) {
node.children.toTypedArray().forEach {
transform(it, node)
}
val (rep, assignments) = transformExpr(node)
var replacement = rep
if(!(rep.type equalsSize node.type)) {
if(rep.type in NumericDatatypes && node.type in ByteDatatypes) {
replacement = PtTypeCast(node.type, node.position)
replacement.add(rep)
} else
TODO("cast replacement type ${rep.type} -> ${node.type}")
}
var idx = parent.children.indexOf(node)
parent.children[idx] = replacement
replacement.parent = parent
// find the statement above which we should insert the assignments
var stmt = node
while(!isProperStatement(stmt))
stmt = stmt.parent
idx = stmt.parent.children.indexOf(stmt)
assignments.reversed().forEach {
stmt.parent.add(idx, it as PtNode)
}
} else {
node.children.toTypedArray().forEach { child -> transform(child, node) }
}
}
program.allBlocks().forEach { block ->
block.children.toTypedArray().forEach {
transform(it, block)
}
}
// add the new variables
newVariables.forEach { (sub, vars) ->
vars.forEach {
sub.add(0, it)
}
}
// extra check to see that all PtBinaryExpressions have been transformed
fun binExprCheck(node: PtNode) {
if(node is PtBinaryExpression)
throw IllegalArgumentException("still got binexpr $node ${node.position}")
node.children.forEach { binExprCheck(it) }
}
binExprCheck(program)
}

View File

@ -21,10 +21,6 @@ internal class ErrorReporter: IErrorReporter {
messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position)) messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
} }
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
override fun report() { override fun report() {
var numErrors = 0 var numErrors = 0
var numWarnings = 0 var numWarnings = 0

View File

@ -32,7 +32,6 @@ class ModuleImporter(private val program: Program,
val programPath = path.resolve(normalizedFilePath) val programPath = path.resolve(normalizedFilePath)
if(programPath.exists()) { if(programPath.exists()) {
println("Compiling program ${Path("").absolute().relativize(programPath)}") println("Compiling program ${Path("").absolute().relativize(programPath)}")
println("Compiler target: $compilationTargetName")
val source = SourceCode.File(programPath) val source = SourceCode.File(programPath)
return Ok(importModule(source)) return Ok(importModule(source))
} }

View File

@ -0,0 +1,49 @@
package prog8.compiler.astprocessing
import prog8.ast.statements.Block
import prog8.ast.statements.Label
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor
import prog8.code.core.ICompilationTarget
import prog8.code.target.VMTarget
class AsmInstructionNamesFinder(val target: ICompilationTarget): IAstVisitor {
val blocks = mutableSetOf<Block>()
val variables = mutableSetOf<VarDecl>()
val labels = mutableSetOf<Label>()
val subroutines = mutableSetOf<Subroutine>()
private fun isPossibleInstructionName(name: String) = name.length==3 && name.all { it.isLetter() }
fun foundAny(): Boolean = blocks.isNotEmpty() || variables.isNotEmpty() || subroutines.isNotEmpty() || labels.isNotEmpty()
override fun visit(block: Block) {
if(target.name!=VMTarget.NAME && !block.isInLibrary && isPossibleInstructionName(block.name)) {
blocks += block
}
super.visit(block)
}
override fun visit(decl: VarDecl) {
if(target.name!=VMTarget.NAME && !decl.definingModule.isLibrary && isPossibleInstructionName(decl.name)) {
variables += decl
}
super.visit(decl)
}
override fun visit(label: Label) {
if(target.name!=VMTarget.NAME && !label.definingModule.isLibrary && isPossibleInstructionName(label.name)) {
labels += label
}
super.visit(label)
}
override fun visit(subroutine: Subroutine) {
if(target.name!=VMTarget.NAME && !subroutine.definingModule.isLibrary && isPossibleInstructionName(subroutine.name)) {
subroutines += subroutine
}
super.visit(subroutine)
}
}

View File

@ -0,0 +1,101 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
class AsmInstructionNamesReplacer(
val blocks: Set<Block>,
val subroutines: Set<Subroutine>,
val variables: Set<VarDecl>,
val labels: Set<Label>): AstWalker() {
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
val newName = identifier.nameInSource.map { ident ->
if(ident.length==3 && !identifier.definingModule.isLibrary) {
val blockTarget = blocks.firstOrNull { it.name==ident }
val subTarget = subroutines.firstOrNull {it.name==ident }
val varTarget = variables.firstOrNull { it.name==ident }
val labelTarget = labels.firstOrNull { it.name==ident}
if(blockTarget!=null || subTarget!=null || varTarget!=null || labelTarget!=null) {
"p8p_$ident"
} else
ident
} else
ident
}
return if(newName!=identifier.nameInSource)
listOf(IAstModification.ReplaceNode(identifier, IdentifierReference(newName, identifier.position), parent))
else
noModifications
}
override fun after(label: Label, parent: Node): Iterable<IAstModification> {
return if(label in labels)
listOf(IAstModification.ReplaceNode(label, Label("p8p_${label.name}", label.position), parent))
else
noModifications
}
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
return if(block in blocks)
listOf(IAstModification.ReplaceNode(block, Block("p8p_${block.name}", block.address, block.statements, block.isInLibrary, block.position), parent))
else
noModifications
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
return if(decl in variables)
listOf(IAstModification.ReplaceNode(decl, decl.renamed("p8p_${decl.name}"), parent))
else
noModifications
}
private val subsWithParamRefsToFix = mutableListOf<Subroutine>()
override fun applyModifications(): Int {
var count = super.applyModifications()
subsWithParamRefsToFix.forEach { subroutine ->
subroutine.statements.withIndex().reversed().forEach { (index,stmt) ->
if(stmt is VarDecl && stmt.origin==VarDeclOrigin.SUBROUTINEPARAM) {
val param = subroutine.parameters.single { it.name == stmt.name}
val decl = VarDecl.fromParameter(param)
subroutine.statements[index] = decl
decl.linkParents(subroutine)
count++
}
}
}
return count
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val changedParams = mutableListOf<Pair<Int, SubroutineParameter>>()
subroutine.parameters.withIndex().forEach { (index, param) ->
if(param.name.length==3 && param.name.all { it.isLetter() } && !param.definingModule.isLibrary) {
changedParams.add(index to SubroutineParameter("p8p_${param.name}", param.type, param.position))
}
}
changedParams.forEach { (index, newParam) -> subroutine.parameters[index] = newParam }
val newName = if(subroutine in subroutines) "p8p_${subroutine.name}" else subroutine.name
return if(newName!=subroutine.name || changedParams.isNotEmpty()) {
val newSub = Subroutine(newName, subroutine.parameters, subroutine.returntypes,
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, subroutine.asmAddress, subroutine.isAsmSubroutine,
subroutine.inline, false, subroutine.statements, subroutine.position)
if(changedParams.isNotEmpty())
subsWithParamRefsToFix += newSub
listOf(IAstModification.ReplaceNode(subroutine, newSub, parent))
} else {
if(changedParams.isNotEmpty())
subsWithParamRefsToFix += subroutine
noModifications
}
}
}

View File

@ -12,7 +12,6 @@ import prog8.compiler.builtinFunctionReturnType
import java.io.CharConversionException import java.io.CharConversionException
import java.io.File import java.io.File
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.math.floor
/** /**
* Semantic analysis. * Semantic analysis.
@ -63,7 +62,7 @@ internal class AstChecker(private val program: Program,
override fun visit(identifier: IdentifierReference) { override fun visit(identifier: IdentifierReference) {
val stmt = identifier.targetStatement(program) val stmt = identifier.targetStatement(program)
if(stmt==null) if(stmt==null)
errors.undefined(identifier.nameInSource, identifier.position) errors.err("undefined symbol: ${identifier.nameInSource.joinToString(".")}", identifier.position)
else { else {
val target = stmt as? VarDecl val target = stmt as? VarDecl
if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) { if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) {
@ -115,8 +114,6 @@ internal class AstChecker(private val program: Program,
if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) { if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position) errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
} }
} else if(valueDt.isIterable && expectedReturnValues[0]==DataType.UWORD) {
// you can return a string or array when a uword (pointer) is returned
} }
else { else {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position) errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
@ -129,11 +126,8 @@ internal class AstChecker(private val program: Program,
override fun visit(ifElse: IfElse) { override fun visit(ifElse: IfElse) {
val dt = ifElse.condition.inferType(program) val dt = ifElse.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) { if(!dt.isInteger && !dt.istype(DataType.BOOL))
val identifier = ifElse.condition as? IdentifierReference errors.err("condition value should be an integer type or bool", ifElse.condition.position)
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", ifElse.condition.position)
}
super.visit(ifElse) super.visit(ifElse)
} }
@ -171,8 +165,7 @@ internal class AstChecker(private val program: Program,
} }
DataType.UWORD -> { DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR && if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_UW && iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
iterableDt != DataType.ARRAY_UW_SPLIT)
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position) errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression) checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
@ -183,8 +176,7 @@ internal class AstChecker(private val program: Program,
} }
DataType.WORD -> { DataType.WORD -> {
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD && if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W && iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
iterableDt != DataType.ARRAY_W_SPLIT)
errors.err("word loop variable can only loop over bytes or words", forLoop.position) errors.err("word loop variable can only loop over bytes or words", forLoop.position)
} }
DataType.FLOAT -> { DataType.FLOAT -> {
@ -341,7 +333,7 @@ internal class AstChecker(private val program: Program,
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) { for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) { if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL) if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL)
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position) err("parameter '${param.first.name}' should be (u)byte or bool")
} }
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
@ -349,8 +341,8 @@ internal class AstChecker(private val program: Program,
err("parameter '${param.first.name}' should be (u)word (an address) or str") err("parameter '${param.first.name}' should be (u)word (an address) or str")
} }
else if(param.second.statusflag!=null) { else if(param.second.statusflag!=null) {
if (param.first.type != DataType.BOOL) if (param.first.type != DataType.UBYTE && param.first.type != DataType.BOOL)
errors.err("parameter '${param.first.name}' should be of type bool", param.first.position) err("parameter '${param.first.name}' should be bool or ubyte")
} }
} }
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair -> subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
@ -364,8 +356,8 @@ internal class AstChecker(private val program: Program,
err("return type #${index + 1} should be (u)word/address") err("return type #${index + 1} should be (u)word/address")
} }
else if(pair.second.statusflag!=null) { else if(pair.second.statusflag!=null) {
if (pair.first != DataType.BOOL) if (pair.first != DataType.UBYTE && pair.first != DataType.BOOL)
err("return type #${index + 1} should be bool") err("return type #${index + 1} should be bool or ubyte")
} }
} }
@ -436,6 +428,7 @@ internal class AstChecker(private val program: Program,
val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc
if(statusFlagsNoCarry.isNotEmpty()) if(statusFlagsNoCarry.isNotEmpty())
err("can only use Carry as status flag parameter") err("can only use Carry as status flag parameter")
} }
// Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly // Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly
@ -449,30 +442,22 @@ internal class AstChecker(private val program: Program,
override fun visit(untilLoop: UntilLoop) { override fun visit(untilLoop: UntilLoop) {
val dt = untilLoop.condition.inferType(program) val dt = untilLoop.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) { if(!dt.isInteger && !dt.istype(DataType.BOOL))
val identifier = untilLoop.condition as? IdentifierReference errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
}
super.visit(untilLoop) super.visit(untilLoop)
} }
override fun visit(whileLoop: WhileLoop) { override fun visit(whileLoop: WhileLoop) {
val dt = whileLoop.condition.inferType(program) val dt = whileLoop.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) { if(!dt.isInteger && !dt.istype(DataType.BOOL))
val identifier = whileLoop.condition as? IdentifierReference errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
}
super.visit(whileLoop) super.visit(whileLoop)
} }
override fun visit(repeatLoop: RepeatLoop) { override fun visit(repeatLoop: RepeatLoop) {
val iterations = repeatLoop.iterations?.constValue(program) val iterations = repeatLoop.iterations?.constValue(program)
if (iterations != null) { if(iterations != null && iterations.number.toInt() > 65535)
require(floor(iterations.number)==iterations.number) errors.err("repeat cannot go over 65535 iterations", iterations.position)
if (iterations.number.toInt() > 65535) errors.err("repeat cannot go over 65535 iterations", iterations.position)
}
val ident = repeatLoop.iterations as? IdentifierReference val ident = repeatLoop.iterations as? IdentifierReference
if(ident!=null) { if(ident!=null) {
@ -536,7 +521,7 @@ internal class AstChecker(private val program: Program,
val targetName = targetIdentifier.nameInSource val targetName = targetIdentifier.nameInSource
when (val targetSymbol = assignment.definingScope.lookup(targetName)) { when (val targetSymbol = assignment.definingScope.lookup(targetName)) {
null -> { null -> {
errors.undefined(targetIdentifier.nameInSource, targetIdentifier.position) errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
return return
} }
!is VarDecl -> { !is VarDecl -> {
@ -644,9 +629,6 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B, DataType.ARRAY_UB -> DataType.ARRAY_B, DataType.ARRAY_UB ->
if(arraySize > 256) if(arraySize > 256)
err("byte array length must be 1-256") err("byte array length must be 1-256")
in SplitWordArrayTypes ->
if(arraySize > 256)
err("split word array length must be 1-256")
DataType.ARRAY_W, DataType.ARRAY_UW -> DataType.ARRAY_W, DataType.ARRAY_UW ->
if(arraySize > 128) if(arraySize > 128)
err("word array length must be 1-128") err("word array length must be 1-128")
@ -695,10 +677,6 @@ internal class AstChecker(private val program: Program,
if (length == 0 || length > 256) if (length == 0 || length > 256)
err("string and byte array length must be 1-256") err("string and byte array length must be 1-256")
} }
in SplitWordArrayTypes -> {
if (length == 0 || length > 256)
err("split word array length must be 1-256")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
if (length == 0 || length > 128) if (length == 0 || length > 128)
err("word array length must be 1-128") err("word array length must be 1-128")
@ -711,9 +689,6 @@ internal class AstChecker(private val program: Program,
} }
} }
} }
if(decl.splitArray && decl.type==VarDeclType.MEMORY)
err("@split can't be used on memory mapped arrays")
} }
if(decl.datatype==DataType.STR) { if(decl.datatype==DataType.STR) {
@ -746,13 +721,13 @@ internal class AstChecker(private val program: Program,
"%output" -> { "%output" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name !in OutputType.entries.map {it.name.lowercase()}) if(directive.args.size!=1 || directive.args[0].name !in OutputType.values().map {it.name.lowercase()})
err("invalid output directive type") err("invalid output directive type")
} }
"%launcher" -> { "%launcher" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name !in CbmPrgLauncherType.entries.map{it.name.lowercase()}) if(directive.args.size!=1 || directive.args[0].name !in CbmPrgLauncherType.values().map{it.name.lowercase()})
err("invalid launcher directive type") err("invalid launcher directive type")
} }
"%zeropage" -> { "%zeropage" -> {
@ -817,7 +792,7 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level") err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty()) if(directive.args.isEmpty())
err("missing option directive argument(s)") err("missing option directive argument(s)")
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays", "no_symbol_prefixing")}.any { !it }) else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge")}.any { !it })
err("invalid option directive argument(s)") err("invalid option directive argument(s)")
} }
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
@ -980,8 +955,6 @@ internal class AstChecker(private val program: Program,
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte // expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) { } else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) {
// exception allowed: shifting a word by a byte // exception allowed: shifting a word by a byte
} else if((leftDt==DataType.UWORD && rightDt==DataType.STR) || (leftDt==DataType.STR && rightDt==DataType.UWORD)) {
// exception allowed: comparing uword (pointer) with string
} else { } else {
errors.err("left and right operands aren't the same type", expr.left.position) errors.err("left and right operands aren't the same type", expr.left.position)
} }
@ -1167,13 +1140,21 @@ internal class AstChecker(private val program: Program,
} }
} }
} }
else if(funcName[0].startsWith("divmod")) { else if(funcName[0] == "divmod") {
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression) { if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
errors.err("arguments must be all ubyte or all uword", functionCallStatement.position) errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
} else { if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference) errors.err("all arguments must be ubyte", functionCallStatement.position)
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position) if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UBYTE})
} errors.err("all arguments must be ubyte", functionCallStatement.position)
}
else if(funcName[0] == "divmodw") {
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
errors.err("all arguments must be uword", functionCallStatement.position)
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UWORD})
errors.err("all arguments must be uword", functionCallStatement.position)
} }
if(funcName[0] in InplaceModifyingBuiltinFunctions) { if(funcName[0] in InplaceModifyingBuiltinFunctions) {
@ -1251,7 +1232,7 @@ internal class AstChecker(private val program: Program,
val target = postIncrDecr.definingScope.lookup(targetName) val target = postIncrDecr.definingScope.lookup(targetName)
if(target==null) { if(target==null) {
val symbol = postIncrDecr.target.identifier!! val symbol = postIncrDecr.target.identifier!!
errors.undefined(symbol.nameInSource, symbol.position) errors.err("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
} else { } else {
if(target !is VarDecl || target.type== VarDeclType.CONST) { if(target !is VarDecl || target.type== VarDeclType.CONST) {
errors.err("can only increment or decrement a variable", postIncrDecr.position) errors.err("can only increment or decrement a variable", postIncrDecr.position)
@ -1304,7 +1285,7 @@ internal class AstChecker(private val program: Program,
// check index value 0..255 // check index value 0..255
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program) val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(dtxNum.isKnown && dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE) if(dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position) errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
super.visit(arrayIndexedExpression) super.visit(arrayIndexedExpression)
@ -1341,14 +1322,8 @@ internal class AstChecker(private val program: Program,
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position) constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position) constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
conditionType isnot constvalue.type -> { conditionType isnot constvalue.type -> {
if(conditionType.isKnown) { if(conditionType.isKnown)
if(conditionType.istype(DataType.BOOL)) { errors.err("choice value datatype differs from condition value", whenChoice.position)
if(constvalue.number!=0.0 && constvalue.number!=1.0)
errors.err("choice value datatype differs from condition value", whenChoice.position)
} else {
errors.err("choice value datatype differs from condition value", whenChoice.position)
}
}
} }
} }
} }
@ -1390,32 +1365,6 @@ internal class AstChecker(private val program: Program,
super.visit(containment) super.visit(containment)
} }
override fun visit(memread: DirectMemoryRead) {
if(!memread.addressExpression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memread.position)
}
val tc = memread.addressExpression as? TypecastExpression
if(tc!=null && tc.implicit) {
if(!tc.expression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memread.position)
}
}
super.visit(memread)
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!memwrite.addressExpression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memwrite.position)
}
val tc = memwrite.addressExpression as? TypecastExpression
if(tc!=null && tc.implicit) {
if(!tc.expression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memwrite.position)
}
}
super.visit(memwrite)
}
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? { private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
when (val targetStatement = target.targetStatement(program)) { when (val targetStatement = target.targetStatement(program)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
@ -1429,7 +1378,7 @@ internal class AstChecker(private val program: Program,
else else
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position) errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
} }
null -> errors.undefined(target.nameInSource, target.position) null -> errors.err("undefined symbol: ${target.nameInSource.joinToString(".")}", target.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position) else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
} }
return null return null
@ -1486,23 +1435,22 @@ internal class AstChecker(private val program: Program,
} }
return err("invalid byte array initialization value ${value.type}, expected $targetDt") return err("invalid byte array initialization value ${value.type}, expected $targetDt")
} }
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT-> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
// value may be either a single word, or a word arraysize, or a range // value may be either a single word, or a word arraysize, or a range
if(value.type istype targetDt) { if(value.type istype targetDt) {
if(!checkArrayValues(value, targetDt)) if(!checkArrayValues(value, targetDt))
return false return false
val arraySpecSize = arrayspec.constIndex() val arraySpecSize = arrayspec.constIndex()
val arraySize = value.value.size val arraySize = value.value.size
val maxLength = if(targetDt in SplitWordArrayTypes) 256 else 128
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize>maxLength) if(arraySpecSize>128)
return err("array length must be 1-$maxLength") return err("word array length must be 1-128")
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value") val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
if (arraySize != expectedSize) if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true return true
} }
return err("invalid array size, must be 1-$maxLength") return err("invalid word array size, must be 1-128")
} }
return err("invalid word array initialization value ${value.type}, expected $targetDt") return err("invalid word array initialization value ${value.type}, expected $targetDt")
} }
@ -1590,7 +1538,6 @@ internal class AstChecker(private val program: Program,
when (it) { when (it) {
is NumericLiteral -> it.number.toInt() is NumericLiteral -> it.number.toInt()
is AddressOf -> it.identifier.hashCode() and 0xffff is AddressOf -> it.identifier.hashCode() and 0xffff
is IdentifierReference -> it.hashCode() and 0xffff
is TypecastExpression -> { is TypecastExpression -> {
val constVal = it.expression.constValue(program) val constVal = it.expression.constValue(program)
val cast = constVal?.cast(it.type) val cast = constVal?.cast(it.type)
@ -1610,10 +1557,10 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B -> { DataType.ARRAY_B -> {
correct = array.all { it in -128..127 } correct = array.all { it in -128..127 }
} }
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> { DataType.ARRAY_UW -> {
correct = array.all { (it in 0..65535) } correct = array.all { (it in 0..65535) }
} }
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> { DataType.ARRAY_W -> {
correct = array.all { it in -32768..32767 } correct = array.all { it in -32768..32767 }
} }
DataType.ARRAY_F -> correct = true DataType.ARRAY_F -> correct = true

View File

@ -12,6 +12,7 @@ import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) { internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
@ -27,6 +28,20 @@ internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationO
boolRemover.visit(this) boolRemover.visit(this)
boolRemover.applyModifications() boolRemover.applyModifications()
if(compilerOptions.compTarget.name!=VMTarget.NAME) {
val finder = AsmInstructionNamesFinder(compilerOptions.compTarget)
finder.visit(this)
if(finder.foundAny()) {
val replacer = AsmInstructionNamesReplacer(
finder.blocks,
finder.subroutines,
finder.variables,
finder.labels)
replacer.visit(this)
replacer.applyModifications()
}
}
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors) val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
fixer.visit(this) fixer.visit(this)
while (errors.noErrors() && fixer.applyModifications() > 0) { while (errors.noErrors() && fixer.applyModifications() > 0) {

Some files were not shown because too many files have changed in this diff Show More