Compare commits

..

1 Commits
v10.2 ... v8.14

Author SHA1 Message Date
Irmen de Jong
e55a675d2e release 8.14 2023-05-29 23:00:43 +02:00
412 changed files with 23249 additions and 40209 deletions

13
.github/FUNDING.yml vendored
View File

@@ -1,13 +0,0 @@
# These are supported funding model platforms
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: irmen
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -10,18 +10,13 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: build and install recent 64tass - name: Install 64tass
run: | run: sudo apt-get update -y && sudo apt-get install -y 64tass
sudo apt-get install -y make build-essential
git clone --depth=1 https://github.com/irmen/64tass
cd 64tass
make -j4
sudo make install
- name: Set up JDK 11 - name: Set up JDK 11
uses: actions/setup-java@v4 uses: actions/setup-java@v2
with: with:
java-version: 11 java-version: 11
distribution: adopt distribution: adopt
@@ -30,18 +25,7 @@ jobs:
run: ./gradlew build shadowJar --no-daemon run: ./gradlew build shadowJar --no-daemon
- name: Create compiler shadowJar artifact - name: Create compiler shadowJar artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: prog8-compiler-jar-zipped name: prog8-compiler-jar-zipped
path: compiler/build/libs/*-all.jar path: compiler/build/libs/*-all.jar
- uses: MCJack123/ghaction-generate-release-hashes@v4
if: "github.event_name == 'release'"
with:
hash-type: sha256
file-name: hashes.txt
- uses: actions/upload-artifact@v4
if: "github.event_name == 'release'"
with:
name: Artifact Hashes
path: hashes.txt

2
.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
@@ -33,4 +32,3 @@ compiler/lib/
/prog8compiler.jar /prog8compiler.jar
sd*.img sd*.img
*.d64 *.d64

View File

@@ -9,15 +9,6 @@
</inspection_tool> </inspection_tool>
<inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="sphinx_rtd_theme" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true"> <inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="false" /> <option name="processCode" value="false" />
<option name="processLiterals" value="true" /> <option name="processLiterals" value="true" />

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.20" /> <option name="version" value="1.8.21-release-380" />
</component> </component>
</project> </project>

View File

@@ -1,23 +1,19 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository"> <library name="KotlinJavaRuntime">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-javadoc.jar!/" />
</JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-sources.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-sources.jar!/" /> <root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
</component> </component>

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.1"> <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.1/antlr4-4.13.1.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.1/antlr4-runtime-4.13.1.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

@@ -0,0 +1,25 @@
<component name="libraryTable">
<library name="github.hypfvieh.dbus.java" type="repository">
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

10
.idea/libraries/glassfish_javax_json.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="glassfish.javax.json" type="repository">
<properties include-transitive-deps="false" maven-id="org.glassfish:javax.json:1.1.4" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

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.8.0" /> <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.8.0/kotest-assertions-core-jvm-5.8.0.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.8.0/kotest-assertions-shared-jvm-5.8.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/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.3.0/opentest4j-1.3.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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.8.0/kotest-common-jvm-5.8.0.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.8.0/kotest-assertions-api-jvm-5.8.0.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

@@ -0,0 +1,24 @@
<component name="libraryTable">
<library name="io.kotest.property.jvm" type="repository">
<properties maven-id="io.kotest:kotest-property-jvm:5.5.5" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.5.5/kotest-property-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

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.8.0" /> <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.8.0/kotest-runner-junit5-jvm-5.8.0.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.8.0/kotest-framework-api-jvm-5.8.0.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.8.0/kotest-assertions-shared-jvm-5.8.0.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.8.0/kotest-common-jvm-5.8.0.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.8.0/kotest-framework-engine-jvm-5.8.0.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.162/classgraph-4.8.162.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.154/classgraph-4.8.154.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.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.8.0/kotest-framework-discovery-jvm-5.8.0.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.8.0/kotest-assertions-core-jvm-5.8.0.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.8.0/kotest-assertions-api-jvm-5.8.0.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.8.0/kotest-extensions-jvm-5.8.0.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.8.0/kotest-framework-concurrency-jvm-5.8.0.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,8 +1,8 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="jetbrains.kotlinx.cli.jvm" type="repository"> <library name="jetbrains.kotlinx.cli.jvm" type="repository">
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6" /> <properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.5" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.5/kotlinx-cli-jvm-0.3.5.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@@ -1,10 +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.20" /> <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.20/kotlin-result-jvm-1.1.20.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

11
.idea/libraries/slf4j_simple.xml generated Normal file
View File

@@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="slf4j.simple" type="repository">
<properties maven-id="org.slf4j:slf4j-simple:2.0.7" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.7/slf4j-simple-2.0.7.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

18
.idea/libraries/takes.xml generated Normal file
View File

@@ -0,0 +1,18 @@
<component name="libraryTable">
<library name="takes" type="repository">
<properties maven-id="org.takes:takes:1.24.4" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

3
.idea/misc.xml generated
View File

@@ -16,9 +16,6 @@
</list> </list>
</option> </option>
</component> </component>
<component name="Black">
<option name="sdkName" value="Python 3.11" />
</component>
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<type id="Python" /> <type id="Python" />
</component> </component>

2
.idea/modules.xml generated
View File

@@ -10,8 +10,10 @@
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" /> <module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" /> <module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" /> <module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" /> <module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" /> <module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" /> <module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" /> <module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" /> <module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />

View File

@@ -9,19 +9,17 @@ version: 2
build: build:
os: ubuntu-22.04 os: ubuntu-22.04
tools: tools:
python: "3.12" python: "3.11"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf
# Optionally declare the Python requirements required to build your docs # Optionally declare the Python requirements required to build your docs
python: python:
install: install:
- requirements: docs/requirements.txt - requirements: docs/requirements.txt
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
fail_on_warning: true
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf
- epub

View File

@@ -1,12 +0,0 @@
# super simple Makefile to lauch the main gradle targets to build and/or test the prog8 compiler
.PHONY: all test
all:
gradle installdist installshadowdist
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"
test:
gradle build
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"

View File

@@ -1,4 +1,3 @@
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H6S0FFF)
[![Documentation](https://readthedocs.org/projects/prog8/badge/?version=latest)](https://prog8.readthedocs.io/) [![Documentation](https://readthedocs.org/projects/prog8/badge/?version=latest)](https://prog8.readthedocs.io/)
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
@@ -10,35 +9,11 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro
as used in many home computers from that era. It is a medium to low level programming language, as used in many home computers from that era. It is a medium to low level programming language,
which aims to provide many conveniences over raw assembly code (even when using a macro assembler). which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
**Want to buy me a coffee or a pizza perhaps?**
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
Documentation Documentation
------------- -------------
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at: Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
https://prog8.readthedocs.io/ https://prog8.readthedocs.io/
How to get it/build it
----------------------
- Download the latest [official release](https://github.com/irmen/prog8/releases) from github.
- Or, if you want/need a bleeding edge development version, you can:
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
- Alternatively, you can also install the compiler as a package on some linux distros:
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
Community
---------
Most of the development on Prog8 and the use of it is currently centered around
the [Commander X16](https://www.commanderx16.com/) retro computer. Their [discord server](https://discord.gg/nS2PqEC) contains a small channel
dedicated to Prog8. Other than that, use the issue tracker on github.
Software license Software license
---------------- ----------------
GNU GPL 3.0 (see file LICENSE), with exception for generated code: GNU GPL 3.0 (see file LICENSE), with exception for generated code:
@@ -51,8 +26,8 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code:
What does Prog8 provide? What does Prog8 provide?
------------------------ ------------------------
- all advantages of a higher level language over having to write assembly code manually - reduction of source code length over raw assembly
- programs run very fast because compilation to native machine code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects. - fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
- modularity, symbol scoping, subroutines - modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do) - floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
@@ -60,7 +35,6 @@ What does Prog8 provide?
- automatic static variable allocations, automatic string and array variables and string sharing - automatic static variable allocations, automatic string and array variables and string sharing
- subroutines with input parameters and result values - subroutines with input parameters and result values
- high-level program optimizations - high-level program optimizations
- no need for forward declarations
- small program boilerplate/compilersupport overhead - small program boilerplate/compilersupport overhead
- programs can be run multiple times without reloading because of automatic variable (re)initializations. - programs can be run multiple times without reloading because of automatic variable (re)initializations.
- conditional branches - conditional branches
@@ -85,8 +59,6 @@ What does Prog8 provide?
- "c64": Commodore-64 (6502 like CPU) - "c64": Commodore-64 (6502 like CPU)
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported) - "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) - "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- "pet32": Commodore PET (experimental)
- "atari": Atari 8 bit such as 800XL (experimental)
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag) - If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)

View File

@@ -26,16 +26,16 @@ 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.20" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
} }
sourceSets { sourceSets {
main { main {
java { java {
srcDir "${project.projectDir}/src" srcDirs = ["${project.projectDir}/src"]
} }
resources { resources {
srcDir "${project.projectDir}/res" srcDirs = ["${project.projectDir}/res"]
} }
} }
} }

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
}
}
} }
@@ -195,11 +185,14 @@ class StStaticVariable(name: String,
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length) require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
} }
if(onetimeInitializationNumericValue!=null) { if(onetimeInitializationNumericValue!=null) {
require(dt in NumericDatatypes || dt==DataType.BOOL) require(dt in NumericDatatypes)
require(onetimeInitializationNumericValue!=0.0) { "zero as init value should just remain uninitialized"}
} }
if(onetimeInitializationArrayValue!=null) { if(onetimeInitializationArrayValue!=null) {
require(dt in ArrayDatatypes) require(dt in ArrayDatatypes)
require(length==onetimeInitializationArrayValue.size) if(onetimeInitializationArrayValue.all { it.number!=null} ) {
require(onetimeInitializationArrayValue.any { it.number != 0.0 }) { "array of all zerors as init value should just remain uninitialized" }
}
} }
if(onetimeInitializationStringValue!=null) { if(onetimeInitializationStringValue!=null) {
require(dt == DataType.STR) require(dt == DataType.STR)
@@ -210,7 +203,8 @@ class StStaticVariable(name: String,
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) : class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
StNode(name, StNodeType.CONSTANT, astNode) StNode(name, StNodeType.CONSTANT, astNode) {
}
class StMemVar(name: String, class StMemVar(name: String,
@@ -221,7 +215,6 @@ class StMemVar(name: String,
StNode(name, StNodeType.MEMVAR, astNode) { StNode(name, StNodeType.MEMVAR, astNode) {
init{ init{
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
if(dt in ArrayDatatypes || dt == DataType.STR) if(dt in ArrayDatatypes || dt == DataType.STR)
require(length!=null) { "memory mapped array or string must have known length" } require(length!=null) { "memory mapped array or string must have known length" }
} }
@@ -233,11 +226,12 @@ class StMemorySlab(
val align: UInt, val align: UInt,
astNode: PtNode astNode: PtNode
): ):
StNode(name, StNodeType.MEMORYSLAB, astNode) StNode(name, StNodeType.MEMORYSLAB, astNode) {
}
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) : class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
StNode(name, StNodeType.SUBROUTINE, astNode) StNode(name, StNodeType.SUBROUTINE, astNode) {
}
class StRomSub(name: String, class StRomSub(name: String,
@@ -251,7 +245,7 @@ class StRomSub(name: String,
class StSubroutineParameter(val name: String, val type: DataType) class StSubroutineParameter(val name: String, val type: DataType)
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType) class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?) class StArrayElement(val number: Double?, val addressOfSymbol: String?)
typealias StString = Pair<String, Encoding> typealias StString = Pair<String, Encoding>
typealias StArray = List<StArrayElement> typealias StArray = List<StArrayElement>

View File

@@ -26,6 +26,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY),
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY)
).forEach { ).forEach {
it.parent = program it.parent = program
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it)) st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
@@ -74,9 +76,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
numElements = value.value.length + 1 // include the terminating 0-byte numElements = value.value.length + 1 // include the terminating 0-byte
} }
is PtArray -> { is PtArray -> {
initialArray = makeInitialArray(value) val array = makeInitialArray(value)
initialArray = if(array.all { it.number==0.0 }) null else array // all 0 as init value -> just uninitialized
initialString = null initialString = null
numElements = initialArray.size numElements = array.size
require(node.arraySize?.toInt()==numElements) require(node.arraySize?.toInt()==numElements)
} }
else -> { else -> {
@@ -91,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 -> {
@@ -125,14 +125,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
private fun makeInitialArray(value: PtArray): List<StArrayElement> { private fun makeInitialArray(value: PtArray): List<StArrayElement> {
return value.children.map { return value.children.map {
when(it) { when(it) {
is PtAddressOf -> { is PtAddressOf -> StArrayElement(null, it.identifier.name)
if(it.isFromArrayElement) is PtIdentifier -> StArrayElement(null, it.name)
TODO("address-of array element $it in initial array value") is PtNumber -> StArrayElement(it.number, null)
StArrayElement(null, it.identifier.name, null)
}
is PtIdentifier -> StArrayElement(null, it.name, null)
is PtNumber -> StArrayElement(it.number, null, null)
is PtBool -> StArrayElement(null, null, it.value)
else -> throw AssemblyError("invalid array element $it") else -> throw AssemblyError("invalid array element $it")
} }
} }
@@ -175,7 +170,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
// } // }
// VarDeclType.MEMORY -> { // VarDeclType.MEMORY -> {
// val numElements = // val numElements =
// if(decl.isArray) // if(decl.datatype in ArrayDatatypes)
// decl.arraysize!!.constIndex() // decl.arraysize!!.constIndex()
// else null // else null
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position) // val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)

View File

@@ -31,19 +31,15 @@ sealed class PtNode(val position: Position) {
} }
sealed interface IPtStatementContainer class PtNodeGroup : PtNode(Position.DUMMY)
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) { sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
// Note that as an exception, the 'name' is not read-only // Note that as an exception, the 'name' is not read-only
// but a var. This is to allow for cheap node renames. // but a var. This is to allow for cheap node renames.
val scopedName: String val scopedName: String by lazy {
get() {
var namedParent: PtNode = this.parent var namedParent: PtNode = this.parent
return if(namedParent is PtProgram) if(namedParent is PtProgram)
name name
else { else {
while (namedParent !is PtNamedNode) while (namedParent !is PtNamedNode)
@@ -67,31 +63,23 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>() children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? = fun entrypoint(): PtSub? =
// returns the main.start subroutine if it exists allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub?
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
?.children
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?
} }
class PtBlock(name: String, class PtBlock(name: String,
val address: UInt?,
val library: Boolean, val library: Boolean,
val forceOutput: Boolean,
val alignment: BlockAlignment,
val source: SourceCode, // taken from the module the block is defined in. val source: SourceCode, // taken from the module the block is defined in.
val options: Options,
position: Position position: Position
) : PtNamedNode(name, position), IPtStatementContainer { ) : PtNamedNode(name, position) {
enum class BlockAlignment { enum class BlockAlignment {
NONE, NONE,
WORD, WORD,
PAGE PAGE
} }
class Options(val address: UInt? = null,
val forceOutput: Boolean = false,
val noSymbolPrefixing: Boolean = false,
val veraFxMuls: Boolean = false,
val ignoreUnused: Boolean = false,
val alignment: BlockAlignment = BlockAlignment.NONE)
} }

View File

@@ -1,14 +1,19 @@
package prog8.code.ast package prog8.code.ast
import prog8.code.core.* import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.NumericDatatypes
import prog8.code.core.Position
import java.util.* import java.util.*
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.truncate import kotlin.math.round
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) { sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init { init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type==DataType.UNDEFINED) { if(type==DataType.UNDEFINED) {
@Suppress("LeakingThis") @Suppress("LeakingThis")
when(this) { when(this) {
@@ -22,32 +27,14 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
infix fun isSameAs(other: PtExpression): Boolean { infix fun isSameAs(other: PtExpression): Boolean {
return when(this) { return when(this) {
is PtAddressOf -> { is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
if(other !is PtAddressOf) is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
return false is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
if (other.type!==type || !(other.identifier isSameAs identifier))
return false
if(other.children.size!=children.size)
return false
if(children.size==1)
return true
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
}
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
is PtBinaryExpression -> {
if(other !is PtBinaryExpression || other.operator!=operator)
false
else if(operator in AssociativeOperators)
(other.left isSameAs left && other.right isSameAs right) || (other.left isSameAs right && other.right isSameAs left)
else
other.left isSameAs left && other.right isSameAs right
}
is PtContainmentCheck -> 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
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
is PtNumber -> other is PtNumber && other.type==type && other.number==number is PtNumber -> other is PtNumber && other.type==type && other.number==number
is PtBool -> other is PtBool && other.value==value
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
@@ -64,13 +51,13 @@ 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
} }
} }
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt() fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt()
fun isSimple(): Boolean { fun isSimple(): Boolean {
return when(this) { return when(this) {
@@ -78,18 +65,12 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtArray -> true is PtArray -> true
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
is PtBinaryExpression -> false is PtBinaryExpression -> false
is PtBuiltinFunctionCall -> { is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
when (name) {
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
else -> false
}
}
is PtContainmentCheck -> false is PtContainmentCheck -> false
is PtFunctionCall -> false is PtFunctionCall -> false
is PtIdentifier -> true is PtIdentifier -> true
is PtMachineRegister -> true is PtMachineRegister -> true
is PtMemoryByte -> address is PtNumber || address is PtIdentifier is PtMemoryByte -> address is PtNumber || address is PtIdentifier
is PtBool -> true
is PtNumber -> true is PtNumber -> true
is PtPrefix -> value.isSimple() is PtPrefix -> value.isSimple()
is PtRange -> true is PtRange -> true
@@ -116,7 +97,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position)) is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position)) is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position)) is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
is PtBool -> return withClonedChildrenFrom(this, PtBool(value, position))
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position)) is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position)) is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position)) is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
@@ -128,29 +108,18 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
val identifier: PtIdentifier val identifier: PtIdentifier
get() = children[0] as PtIdentifier get() = children.single() as PtIdentifier
val arrayIndexExpr: PtExpression?
get() = if(children.size==2) children[1] as PtExpression else null
val isFromArrayElement: Boolean
get() = children.size==2
} }
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) { class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
val variable: PtIdentifier val variable: PtIdentifier
get() { get() = children[0] as PtIdentifier
require((children[0] as? PtIdentifier)?.type in ArrayDatatypes+DataType.STR) // TODO remove
return children[0] as PtIdentifier
}
val index: PtExpression val index: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
init { init {
require(elementType in NumericDatatypesWithBoolean) require(elementType in NumericDatatypes)
} }
} }
@@ -184,21 +153,15 @@ class PtBuiltinFunctionCall(val name: String,
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) { class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
val left: PtExpression val left: PtExpression
get() = children[0] as PtExpression get() = children[0] as PtExpression
val right: PtExpression val right: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
init {
if(operator in ComparisonOperators + LogicalOperators)
require(type==DataType.BOOL)
else
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
}
} }
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) { class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
val element: PtExpression val element: PtExpression
get() = children[0] as PtExpression get() = children[0] as PtExpression
val iterable: PtIdentifier val iterable: PtIdentifier
@@ -235,27 +198,6 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
} }
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
companion object {
fun fromNumber(number: Number, position: Position): PtBool =
PtBool(if(number==0.0) false else true, position)
}
override fun hashCode(): Int = Objects.hash(type, value)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtBool)
return false
return value==other.value
}
override fun toString() = "PtBool:$value"
fun asInt(): Int = if(value) 1 else 0
}
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
companion object { companion object {
@@ -265,11 +207,11 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
init { init {
if(type==DataType.BOOL) if(type==DataType.BOOL)
throw IllegalArgumentException("use PtBool instead") throw IllegalArgumentException("bool should have become ubyte @$position")
if(type!=DataType.FLOAT) { if(type!=DataType.FLOAT) {
val trunc = truncate(number) val rounded = round(number)
if (trunc != number) if (rounded != number)
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position") throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
} }
} }
@@ -278,10 +220,7 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if(other==null || other !is PtNumber) if(other==null || other !is PtNumber)
return false return false
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
return number==other.number return number==other.number
else
return type==other.type && number==other.number
} }
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number) operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
@@ -295,7 +234,8 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
get() = children.single() as PtExpression get() = children.single() as PtExpression
init { init {
require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" } // note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
} }
} }
@@ -357,5 +297,5 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else if(expr is PtBool) expr.asInt().toDouble() else null fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else if(expr is PtBool) expr.asInt() else null fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null

View File

@@ -15,14 +15,9 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}" is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
is PtBreakpoint -> "%breakpoint" is PtBreakpoint -> "%breakpoint"
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> { is PtAddressOf -> "&"
if(node.isFromArrayElement)
"& array-element"
else
"&"
}
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 ""
@@ -40,7 +35,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex() val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
"$numstr ${type(node.type)}" "$numstr ${type(node.type)}"
} }
is PtBool -> node.value.toString()
is PtPrefix -> node.operator is PtPrefix -> node.operator
is PtRange -> "<range>" is PtRange -> "<range>"
is PtString -> "\"${node.value.escape()}\"" is PtString -> "\"${node.value.escape()}\""
@@ -59,24 +53,15 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"goto ${node.identifier.name}" "goto ${node.identifier.name}"
else if(node.address!=null) else if(node.address!=null)
"goto ${node.address.toHex()}" "goto ${node.address.toHex()}"
else if(node.generatedLabel!=null)
"goto ${node.generatedLabel}"
else else
"???" "???"
} }
is PtAsmSub -> { is PtAsmSub -> {
val params = node.parameters.map { val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
val register = it.first.registerOrPair
val statusflag = it.first.statusflag
"${it.second.type} ${it.second.name} @${register ?: statusflag}"
}.joinToString(", ")
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}" val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
val returns = if (node.returns.isEmpty()) "" else { val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}")
"-> ${node.returns.map {
val register = it.first.registerOrPair
val statusflag = it.first.statusflag
"${it.second} @${register ?: statusflag}"}
.joinToString(", ")
}"
}
val str = if (node.inline) "inline " else "" val str = if (node.inline) "inline " else ""
if(node.address==null) { if(node.address==null) {
str + "asmsub ${node.name}($params) $clobbers $returns" str + "asmsub ${node.name}($params) $clobbers $returns"
@@ -85,16 +70,12 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
} }
} }
is PtBlock -> { is PtBlock -> {
val addr = if(node.options.address==null) "" else "@${node.options.address.toHex()}" val addr = if(node.address==null) "" else "@${node.address.toHex()}"
val align = if(node.options.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.options.alignment}" val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}"
"\nblock '${node.name}' $addr $align" "\nblock '${node.name}' $addr $align"
} }
is PtConstant -> { is PtConstant -> {
val value = when(node.type) { val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString()
DataType.BOOL -> if(node.value==0.0) "false" else "true"
in IntegerDatatypes -> node.value.toInt().toString()
else -> node.value.toString()
}
"const ${node.type.name.lowercase()} ${node.name} = $value" "const ${node.type.name.lowercase()} ${node.name} = $value"
} }
is PtLabel -> "${node.name}:" is PtLabel -> "${node.name}:"
@@ -108,21 +89,20 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
} }
} }
is PtSub -> { is PtSub -> {
val params = node.parameters.map { "${it.type} ${it.name}" }.joinToString(", ") val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
var str = "sub ${node.name}($params) " var str = "sub ${node.name}($params) "
if(node.returntype!=null) if(node.returntype!=null)
str += "-> ${node.returntype.name.lowercase()}" str += "-> ${node.returntype.name.lowercase()}"
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}"
@@ -131,8 +111,9 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else else
str str
} }
is PtNodeGroup -> if(node.children.isNotEmpty()) "<group>" else "" is PtNodeGroup -> "<group>"
is PtNop -> "nop" is PtNop -> "nop"
is PtPostIncrDecr -> "<post> ${node.operator}"
is PtProgram -> "PROGRAM ${node.name}" is PtProgram -> "PROGRAM ${node.name}"
is PtRepeatLoop -> "repeat" is PtRepeatLoop -> "repeat"
is PtReturn -> "return" is PtReturn -> "return"
@@ -144,6 +125,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else else
"->" "->"
} }
else -> throw InternalCompilerException("unrecognised ast node $node")
} }
} }

View File

@@ -23,13 +23,13 @@ class PtSub(
val parameters: List<PtSubroutineParameter>, val parameters: List<PtSubroutineParameter>,
val returntype: DataType?, val returntype: DataType?,
position: Position position: Position
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer { ) : PtNamedNode(name, position), IPtSubroutine {
init { init {
// params and return value should not be str // params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL }) if(parameters.any{ it.type !in NumericDatatypes })
throw AssemblyError("non-numeric/non-bool parameter") throw AssemblyError("non-numeric parameter")
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL) if(returntype!=null && returntype !in NumericDatatypes)
throw AssemblyError("non-numeric/non-bool returntype $returntype") throw AssemblyError("non-numeric returntype $returntype")
parameters.forEach { it.parent=this } parameters.forEach { it.parent=this }
} }
} }
@@ -98,13 +98,12 @@ class PtIfElse(position: Position) : PtNode(position) {
get() = children[1] as PtNodeGroup get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup val elseScope: PtNodeGroup
get() = children[2] as PtNodeGroup get() = children[2] as PtNodeGroup
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
} }
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position. class PtJump(val identifier: PtIdentifier?,
val address: UInt?, val address: UInt?,
val generatedLabel: String?,
position: Position) : PtNode(position) { position: Position) : PtNode(position) {
init { init {
identifier?.let {it.parent = this } identifier?.let {it.parent = this }
@@ -112,6 +111,12 @@ class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are
} }
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
val target: PtAssignTarget
get() = children.single() as PtAssignTarget
}
class PtRepeatLoop(position: Position) : PtNode(position) { class PtRepeatLoop(position: Position) : PtNode(position) {
val count: PtExpression val count: PtExpression
get() = children[0] as PtExpression get() = children[0] as PtExpression
@@ -148,11 +153,7 @@ class PtVariable(name: String, override val type: DataType, val zeropage: Zeropa
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable { class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable
init {
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
}
}
class PtWhen(position: Position) : PtNode(position) { class PtWhen(position: Position) : PtNode(position) {

View File

@@ -68,10 +68,9 @@ 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:
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"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),
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
@@ -79,58 +78,36 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), "sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), "reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
// 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", IntegerDatatypes), FParam("value2", NumericDatatypes)), 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),
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null), "abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
"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", NumericDatatypes)), DataType.BYTE), "sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), 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), "any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"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), "all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"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.BOOL),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
"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),
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT),
"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),
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), "pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null), "pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), "popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
"rsave" to FSignature(false, emptyList(), null), "rsave" to FSignature(false, emptyList(), null),
"rsavex" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null), "rrestore" to FSignature(false, emptyList(), null),
"rrestorex" to FSignature(false, emptyList(), null),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), "memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD), "callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
) )
val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse") val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse")

View File

@@ -8,34 +8,24 @@ class CompilationOptions(val output: OutputType,
val launcher: CbmPrgLauncherType, val launcher: CbmPrgLauncherType,
val zeropage: ZeropageType, val zeropage: ZeropageType,
val zpReserved: List<UIntRange>, val zpReserved: List<UIntRange>,
val zpAllowed: List<UIntRange>,
val floats: Boolean, val floats: Boolean,
val noSysInit: Boolean, val noSysInit: Boolean,
val compTarget: ICompilationTarget, val compTarget: ICompilationTarget,
// these are set later, based on command line arguments or options in the source code: // these are set later, based on command line arguments or options in the source code:
var loadAddress: UInt, var loadAddress: UInt,
var warnSymbolShadowing: Boolean = false, var slowCodegenWarnings: Boolean = false,
var optimize: Boolean = false, var optimize: Boolean = false,
var optimizeFloatExpressions: Boolean = false,
var asmQuiet: Boolean = false, var asmQuiet: Boolean = false,
var asmListfile: Boolean = false, var asmListfile: Boolean = false,
var includeSourcelines: Boolean = false,
var dumpVariables: Boolean = false,
var experimentalCodegen: Boolean = false, var experimentalCodegen: Boolean = false,
var varsHighBank: Int? = null, var varsHigh: Boolean = false,
var varsGolden: Boolean = false, var useNewExprCode: Boolean = false,
var slabsHighBank: Int? = null, var evalStackBaseAddress: UInt? = null,
var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false,
var strictBool: Boolean = true,
var breakpointCpuInstruction: String? = null,
var outputDir: Path = Path(""), var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap() var symbolDefs: Map<String, String> = emptyMap()
) { ) {
init { init {
compTarget.machine.initializeMemoryAreas(this) compTarget.machine.initializeMemoryAreas(this)
} }
companion object {
val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u)
}
} }

View File

@@ -58,7 +58,6 @@ fun String.unescape(): String {
'\\' -> '\\' '\\' -> '\\'
'n' -> '\n' 'n' -> '\n'
'r' -> '\r' 'r' -> '\r'
't' -> '\t'
'"' -> '"' '"' -> '"'
'\'' -> '\'' '\'' -> '\''
'u' -> { 'u' -> {

View File

@@ -1,20 +1,17 @@
package prog8.code.core package prog8.code.core
enum class DataType { enum class DataType {
UBYTE, // pass by value 8 bits unsigned UBYTE, // pass by value
BYTE, // pass by value 8 bits signed BYTE, // pass by value
UWORD, // pass by value 16 bits unsigned UWORD, // pass by value
WORD, // pass by value 16 bits signed WORD, // pass by value
LONG, // pass by value 32 bits signed FLOAT, // pass by value
FLOAT, // pass by value machine dependent BOOL, // pass by value
BOOL, // pass by value bit 0 of a 8 bit byte
STR, // pass by reference STR, // pass by reference
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;
@@ -24,12 +21,11 @@ enum class DataType {
*/ */
infix fun isAssignableTo(targetType: DataType) = infix fun isAssignableTo(targetType: DataType) =
when(this) { when(this) {
BOOL -> targetType == BOOL BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT) UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT) BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT) UWORD -> targetType.oneOf(UWORD, FLOAT)
WORD -> targetType.oneOf(WORD, LONG, FLOAT) WORD -> targetType.oneOf(WORD, FLOAT)
LONG -> targetType.oneOf(LONG, FLOAT)
FLOAT -> targetType.oneOf(FLOAT) FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD) STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this in ArrayDatatypes -> targetType == this
@@ -41,17 +37,16 @@ enum class DataType {
infix fun largerThan(other: DataType) = infix fun largerThan(other: DataType) =
when { when {
this == other -> false this == other -> false
this in ByteDatatypesWithBoolean -> false this in ByteDatatypes -> false
this in WordDatatypes -> other in ByteDatatypesWithBoolean this in WordDatatypes -> other in ByteDatatypes
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes this== STR && other== UWORD || this== UWORD && other== STR -> false
this == STR && other == UWORD || this == UWORD && other == STR -> false
else -> true else -> true
} }
infix fun equalsSize(other: DataType) = infix fun equalsSize(other: DataType) =
when { when {
this == other -> true this == other -> true
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean this in ByteDatatypes -> other in ByteDatatypes
this in WordDatatypes -> other in WordDatatypes this in WordDatatypes -> other in WordDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> true this== STR && other== UWORD || this== UWORD && other== STR -> true
else -> false else -> false
@@ -79,13 +74,6 @@ enum class RegisterOrPair {
companion object { companion object {
val names by lazy { values().map { it.toString()} } val names by lazy { values().map { it.toString()} }
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
return when(cpu) {
CpuRegister.A -> A
CpuRegister.X -> X
CpuRegister.Y -> Y
}
}
} }
fun asCpuRegister(): CpuRegister = when(this) { fun asCpuRegister(): CpuRegister = when(this) {
@@ -120,29 +108,26 @@ enum class BranchCondition {
PL, // PL == POS PL, // PL == POS
POS, POS,
VS, VS,
VC VC,
} }
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE) val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD) val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG) val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT) val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, 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 = NumericDatatypesWithBoolean val PassByValueDatatypes = NumericDatatypes
val PassByReferenceDatatypes = IterableDatatypes val PassByReferenceDatatypes = IterableDatatypes
val ArrayToElementTypes = mapOf( val ArrayToElementTypes = mapOf(
DataType.STR to DataType.UBYTE, DataType.STR to DataType.UBYTE,
@@ -150,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
) )
@@ -161,10 +144,8 @@ val ElementToArrayTypes = mapOf(
DataType.WORD to DataType.ARRAY_W, DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW, DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F, DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL, DataType.BOOL to DataType.ARRAY_BOOL
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
) )
val Cx16VirtualRegisters = arrayOf( val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3, RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7, RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
@@ -172,10 +153,6 @@ val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15 RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
) )
val CpuRegisters = setOf(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
)
enum class OutputType { enum class OutputType {

View File

@@ -3,6 +3,8 @@ package prog8.code.core
interface ICompilationTarget: IStringEncoding, IMemSizer { interface ICompilationTarget: IStringEncoding, IMemSizer {
val name: String val name: String
val machine: IMachineDefinition val machine: IMachineDefinition
val supportedEncodings: Set<Encoding>
val defaultEncoding: Encoding
override fun encodeString(str: String, encoding: Encoding): List<UByte> override fun encodeString(str: String, encoding: Encoding): List<UByte>
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String

View File

@@ -3,14 +3,10 @@ package prog8.code.core
interface IErrorReporter { interface IErrorReporter {
fun err(msg: String, position: Position) fun err(msg: String, position: Position)
fun warn(msg: String, position: Position) fun warn(msg: String, position: Position)
fun info(msg: String, position: Position)
fun undefined(symbol: List<String>, position: Position)
fun noErrors(): Boolean fun noErrors(): Boolean
fun report() fun report()
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) { fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
if(numErrors>0) if(numErrors>0)
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.") throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.")
} }
fun noErrorForLine(position: Position): Boolean
} }

View File

@@ -13,11 +13,11 @@ interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int val FLOAT_MEM_SIZE: Int
var ESTACK_LO: UInt
var ESTACK_HI: UInt
val PROGRAM_LOAD_ADDRESS : UInt val PROGRAM_LOAD_ADDRESS : UInt
val BSSHIGHRAM_START: UInt val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt val BSSHIGHRAM_END: UInt
val BSSGOLDENRAM_START: UInt
val BSSGOLDENRAM_END: UInt
val cpu: CpuType val cpu: CpuType
var zeropage: Zeropage var zeropage: Zeropage
@@ -29,4 +29,11 @@ interface IMachineDefinition {
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean fun isIOAddress(address: UInt): Boolean
fun overrideEvalStack(evalStackBaseAddress: UInt) {
require(evalStackBaseAddress and 255u == 0u)
ESTACK_LO = evalStackBaseAddress
ESTACK_HI = evalStackBaseAddress + 256u
require(ESTACK_LO !in golden.region && ESTACK_HI !in golden.region) { "user-set ESTACK can't be in GOLDEN ram" }
}
} }

View File

@@ -9,8 +9,6 @@ enum class Encoding(val prefix: String) {
} }
interface IStringEncoding { interface IStringEncoding {
val defaultEncoding: Encoding
fun encodeString(str: String, encoding: Encoding): List<UByte> fun encodeString(str: String, encoding: Encoding): List<UByte>
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
} }

View File

@@ -42,13 +42,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
} }
} }
fun retainAllowed() {
synchronized(this) {
for(allowed in options.zpAllowed)
free.retainAll { it in allowed }
}
}
fun availableBytes() = if(options.zeropage== ZeropageType.DONTUSE) 0 else free.size fun availableBytes() = if(options.zeropage== ZeropageType.DONTUSE) 0 else free.size
fun hasByteAvailable() = if(options.zeropage== ZeropageType.DONTUSE) false else free.isNotEmpty() fun hasByteAvailable() = if(options.zeropage== ZeropageType.DONTUSE) false else free.isNotEmpty()
fun hasWordAvailable(): Boolean { fun hasWordAvailable(): Boolean {
@@ -71,7 +64,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
val size: Int = val size: Int =
when (datatype) { when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype) in IntegerDatatypes -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> { DataType.STR, in ArrayDatatypes -> {
val memsize = options.compTarget.memorySize(datatype, numElements!!) val memsize = options.compTarget.memorySize(datatype, numElements!!)
if(position!=null) if(position!=null)
@@ -119,7 +112,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
free.removeAll(address until address+size.toUInt()) free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) { if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) { allocatedVariables[name] = when(datatype) {
in NumericDatatypes, DataType.BOOL -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments in NumericDatatypes -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
DataType.STR -> VarAllocation(address, datatype, size) DataType.STR -> VarAllocation(address, datatype, size)
in ArrayDatatypes -> VarAllocation(address, datatype, size) in ArrayDatatypes -> VarAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt") else -> throw AssemblyError("invalid dt")
@@ -151,7 +144,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
val size: Int = val size: Int =
when (datatype) { when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype) in IntegerDatatypes -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> { DataType.STR, in ArrayDatatypes -> {
options.compTarget.memorySize(datatype, numElements!!) options.compTarget.memorySize(datatype, numElements!!)
} }

View File

@@ -1,10 +1,12 @@
package prog8.code.core package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor") val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not", "in") val LogicalOperators = setOf("and", "or", "xor", "not")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val BitwiseOperators = setOf("&", "|", "^", "~") val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not") val PrefixOperators = setOf("+", "-", "~", "not")
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
fun invertedComparisonOperator(operator: String) = fun invertedComparisonOperator(operator: String) =
when (operator) { when (operator) {

View File

@@ -1,6 +1,6 @@
package prog8.code.core package prog8.code.core
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import java.nio.file.InvalidPathException import java.nio.file.InvalidPathException
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.absolute import kotlin.io.path.absolute
@@ -10,7 +10,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
fun toClickableStr(): String { fun toClickableStr(): String {
if(this===DUMMY) if(this===DUMMY)
return "" return ""
if(file.startsWith(LIBRARYFILEPREFIX)) if(file.startsWith(libraryFilePrefix))
return "$file:$line:$startCol:" return "$file:$line:$startCol:"
return try { return try {
val path = Path(file).absolute().normalize().toString() val path = Path(file).absolute().normalize().toString()

View File

@@ -3,7 +3,6 @@ package prog8.code.core
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.file.Path import java.nio.file.Path
import java.text.Normalizer
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.readText import kotlin.io.path.readText
@@ -55,25 +54,22 @@ sealed class SourceCode {
/** /**
* filename prefix to designate library files that will be retreived from internal resources rather than disk * filename prefix to designate library files that will be retreived from internal resources rather than disk
*/ */
const val LIBRARYFILEPREFIX = "library:" const val libraryFilePrefix = "library:"
const val STRINGSOURCEPREFIX = "string:" const val stringSourcePrefix = "string:"
val curdir: Path = Path(".").toAbsolutePath() val curdir: Path = Path(".").toAbsolutePath()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath()) fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
fun isRegularFilesystemPath(pathString: String) = fun isRegularFilesystemPath(pathString: String) =
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX)) !(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix))
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
} }
/** /**
* Turn a plain String into a [SourceCode] object. * Turn a plain String into a [SourceCode] object.
* [origin] will be something like `string:44c56085`. * [origin] will be something like `string:44c56085`.
*/ */
class Text(origText: String): SourceCode() { class Text(override val text: String): SourceCode() {
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
override val isFromResources = false override val isFromResources = false
override val isFromFilesystem = false override val isFromFilesystem = false
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}" override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}"
override val name = "<unnamed-text>" override val name = "<unnamed-text>"
} }
@@ -96,8 +92,7 @@ sealed class SourceCode {
val normalized = path.normalize() val normalized = path.normalize()
origin = relative(normalized).toString() origin = relative(normalized).toString()
try { try {
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC) text = normalized.readText()
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
name = normalized.toFile().nameWithoutExtension name = normalized.toFile().nameWithoutExtension
} catch (nfx: java.nio.file.NoSuchFileException) { } catch (nfx: java.nio.file.NoSuchFileException) {
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) } throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
@@ -115,7 +110,7 @@ sealed class SourceCode {
override val isFromResources = true override val isFromResources = true
override val isFromFilesystem = false override val isFromFilesystem = false
override val origin = "$LIBRARYFILEPREFIX$normalized" override val origin = "$libraryFilePrefix$normalized"
override val text: String override val text: String
override val name: String override val name: String
@@ -129,8 +124,7 @@ sealed class SourceCode {
) )
} }
val stream = object {}.javaClass.getResourceAsStream(normalized) val stream = object {}.javaClass.getResourceAsStream(normalized)
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) } text = stream!!.reader().use { it.readText() }
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
name = Path(pathString).toFile().nameWithoutExtension name = Path(pathString).toFile().nameWithoutExtension
} }
} }
@@ -145,33 +139,3 @@ sealed class SourceCode {
override val text: String = "<generated code node, no text representation>" override val text: String = "<generated code node, no text representation>"
} }
} }
object SourceLineCache {
private val cache = mutableMapOf<String, List<String>>()
private fun getCachedFile(file: String): List<String> {
val existing = cache[file]
if(existing!=null)
return existing
if (SourceCode.isRegularFilesystemPath(file)) {
val source = SourceCode.File(Path(file))
cache[file] = source.text.split('\n', '\r').map { it.trim() }
return cache.getValue(file)
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
cache[file] = source.text.split('\n', '\r').map { it.trim()}
return cache.getValue(file)
}
return emptyList()
}
fun retrieveLine(position: Position): String? {
if (position.line>0) {
val lines = getCachedFile(position.file)
if(lines.isNotEmpty())
return lines[position.line-1]
}
return null
}
}

View File

@@ -1,130 +0,0 @@
package prog8.code.optimize
import prog8.code.ast.*
import prog8.code.core.*
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, errors: IErrorReporter) {
if (!options.optimize)
return
while(errors.noErrors() && optimizeCommonSubExpressions(program, errors)>0) {
// keep rolling
}
}
private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
fun recurse(node: PtNode, depth: Int) {
if(act(node, depth))
node.children.forEach { recurse(it, depth+1) }
}
recurse(root, 0)
}
private var tempVarCounter = 0
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
fun extractableSubExpr(expr: PtExpression): Boolean {
val result = if(expr is PtBinaryExpression)
expr.type !in ByteDatatypes ||
!expr.left.isSimple() ||
!expr.right.isSimple() ||
(expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes)
true
else
!expr.isSimple()
return result
}
// for each Binaryexpression, recurse to find a common subexpression pair therein.
val commons = mutableMapOf<PtBinaryExpression, Pair<PtExpression, PtExpression>>()
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtBinaryExpression) {
val subExpressions = mutableListOf<PtExpression>()
walkAst(node.left) { subNode: PtNode, subDepth: Int ->
if (subNode is PtExpression) {
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
true
} else false
}
walkAst(node.right) { subNode: PtNode, subDepth: Int ->
if (subNode is PtExpression) {
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
true
} else false
}
outer@for (first in subExpressions) {
for (second in subExpressions) {
if (first!==second && first isSameAs second) {
commons[node] = first to second
break@outer // do only 1 replacement at a time per binaryexpression
}
}
}
false
} else true
}
// replace common subexpressions by a temp variable that is assigned only once.
// TODO: check for commonalities across multiple separate expressions in the current scope, not only inside a single line
commons.forEach { binexpr, (occurrence1, occurrence2) ->
val (stmtContainer, stmt) = findContainingStatements(binexpr)
val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1)
val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2)
val containerScopedName = findScopeName(stmtContainer)
tempVarCounter++
val tempvarName = "prog8_subexprvar_$tempVarCounter"
// TODO: some tempvars could be reused, if they are from different lines
val datatype = occurrence1.type
val singleReplacement1 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence1.position)
val singleReplacement2 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence2.position)
occurrence1.parent.children[occurrence1idx] = singleReplacement1
singleReplacement1.parent = occurrence1.parent
occurrence2.parent.children[occurrence2idx] = singleReplacement2
singleReplacement2.parent = occurrence2.parent
val tempassign = PtAssignment(binexpr.position).also { assign ->
assign.add(PtAssignTarget(binexpr.position).also { tgt->
tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
})
assign.add(occurrence1)
occurrence1.parent = assign
}
stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign)
tempassign.parent = stmtContainer
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.NOT_IN_ZEROPAGE, null, null, binexpr.position)
stmtContainer.add(0, tempvar)
tempvar.parent = stmtContainer
// errors.info("common subexpressions replaced by a tempvar, maybe simplify the expression manually", binexpr.position)
}
return commons.size
}
internal fun findScopeName(node: PtNode): String {
var parent=node
while(parent !is PtNamedNode)
parent = parent.parent
return parent.scopedName
}
internal fun findContainingStatements(node: PtNode): Pair<PtNode, PtNode> { // returns (parentstatementcontainer, childstatement)
var parent = node.parent
var child = node
while(true) {
if(parent is IPtStatementContainer) {
return parent to child
}
child=parent
parent=parent.parent
}
}

View File

@@ -7,6 +7,7 @@ import prog8.code.target.atari.AtariMachineDefinition
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer { class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME override val name = NAME
override val machine = AtariMachineDefinition() override val machine = AtariMachineDefinition()
override val supportedEncodings = setOf(Encoding.ATASCII)
override val defaultEncoding = Encoding.ATASCII override val defaultEncoding = Encoding.ATASCII
companion object { companion object {
@@ -15,17 +16,13 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override fun memorySize(dt: DataType): Int { override fun memorySize(dt: DataType): Int {
return when(dt) { return when(dt) {
in ByteDatatypesWithBoolean -> 1 in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2 in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype") else -> Int.MIN_VALUE
} }
} }
override fun memorySize(arrayDt: DataType, numElements: Int) = override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
} }

View File

@@ -11,6 +11,7 @@ import prog8.code.target.cbm.CbmMemorySizer
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME override val name = NAME
override val machine = C128MachineDefinition() override val machine = C128MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
companion object { companion object {

View File

@@ -11,6 +11,7 @@ import prog8.code.target.cbm.CbmMemorySizer
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME override val name = NAME
override val machine = C64MachineDefinition() override val machine = C64MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
companion object { companion object {

View File

@@ -11,6 +11,7 @@ import prog8.code.target.cx16.CX16MachineDefinition
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME override val name = NAME
override val machine = CX16MachineDefinition() override val machine = CX16MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
companion object { companion object {

View File

@@ -10,8 +10,6 @@ import prog8.code.target.cbm.PetsciiEncoding
object Encoder: IStringEncoding { object Encoder: IStringEncoding {
override val defaultEncoding: Encoding = Encoding.ISO
override fun encodeString(str: String, encoding: Encoding): List<UByte> { override fun encodeString(str: String, encoding: Encoding): List<UByte> {
val coded = when(encoding) { val coded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true) Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)

View File

@@ -1,19 +0,0 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.target.pet.PETMachineDefinition
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = PETMachineDefinition()
override val defaultEncoding = Encoding.PETSCII
companion object {
const val NAME = "pet32"
}
}

View File

@@ -6,6 +6,7 @@ import prog8.code.target.virtual.VirtualMachineDefinition
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer { class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME override val name = NAME
override val machine = VirtualMachineDefinition() override val machine = VirtualMachineDefinition()
override val supportedEncodings = setOf(Encoding.ISO)
override val defaultEncoding = Encoding.ISO override val defaultEncoding = Encoding.ISO
companion object { companion object {
@@ -14,16 +15,13 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override fun memorySize(dt: DataType): Int { override fun memorySize(dt: DataType): Int {
return when(dt) { return when(dt) {
in ByteDatatypesWithBoolean -> 1 in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2 in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype") else -> Int.MIN_VALUE
} }
} }
override fun memorySize(arrayDt: DataType, numElements: Int) = override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
} }

View File

@@ -13,10 +13,12 @@ class AtariMachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = 6 override val FLOAT_MEM_SIZE = 6
override val PROGRAM_LOAD_ADDRESS = 0x2000u override val PROGRAM_LOAD_ADDRESS = 0x2000u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive // TODO
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive // TODO
override val BSSHIGHRAM_START = 0u // TODO override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam override lateinit var golden: GoldenRam

View File

@@ -48,7 +48,6 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
free.addAll(distinctFree) free.addAll(distinctFree)
removeReservedFromFreePool() removeReservedFromFreePool()
retainAllowed()
} }
override fun allocateCx16VirtualRegisters() { override fun allocateCx16VirtualRegisters() {

View File

@@ -15,10 +15,12 @@ class C128MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x1c01u override val PROGRAM_LOAD_ADDRESS = 0x1c01u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive
override val BSSHIGHRAM_START = 0u // TODO override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam override lateinit var golden: GoldenRam

View File

@@ -69,7 +69,6 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
free.addAll(distinctFree) free.addAll(distinctFree)
removeReservedFromFreePool() removeReservedFromFreePool()
retainAllowed()
} }
override fun allocateCx16VirtualRegisters() { override fun allocateCx16VirtualRegisters() {

View File

@@ -16,10 +16,12 @@ class C64MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0xcf00u // $cf00-$cf7f inclusive
override var ESTACK_HI = 0xcf80u // $cf80-$cfff inclusive
override val BSSHIGHRAM_START = 0xc000u override val BSSHIGHRAM_START = 0xc000u
override val BSSHIGHRAM_END = 0xcfffu override val BSSHIGHRAM_END = ESTACK_LO
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam override lateinit var golden: GoldenRam
@@ -60,7 +62,7 @@ class C64MachineDefinition: IMachineDefinition {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions) zeropage = C64Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u) golden = GoldenRam(compilerOptions, 0xc000u until ESTACK_LO)
} }
} }

View File

@@ -53,11 +53,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
).map{it.toUInt()}) ).map{it.toUInt()})
} }
if(options.zeropage != ZeropageType.DONTUSE) { if(options.zeropage!= ZeropageType.DONTUSE) {
// add the free Zp addresses // add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O* // these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e, free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6, 0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()}) 0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
} else { } else {
// don't use the zeropage at all // don't use the zeropage at all
@@ -75,8 +75,6 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well. // in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
allocateCx16VirtualRegisters() allocateCx16VirtualRegisters()
} }
retainAllowed()
} }
override fun allocateCx16VirtualRegisters() { override fun allocateCx16VirtualRegisters() {

View File

@@ -6,16 +6,13 @@ import prog8.code.core.*
internal object CbmMemorySizer: IMemSizer { internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int { override fun memorySize(dt: DataType): Int {
return when(dt) { return when(dt) {
in ByteDatatypesWithBoolean -> 1 in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2 in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype") else -> Int.MIN_VALUE
} }
} }
override fun memorySize(arrayDt: DataType, numElements: Int) = override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
} }

View File

@@ -24,7 +24,7 @@ object PetsciiEncoding {
'\ufffe', // 0x0A -> UNDEFINED '\ufffe', // 0x0A -> UNDEFINED
'\ufffe', // 0x0B -> UNDEFINED '\ufffe', // 0x0B -> UNDEFINED
'\ufffe', // 0x0C -> UNDEFINED '\ufffe', // 0x0C -> UNDEFINED
'\n' , // 0x0D -> LINE FEED (RETURN) '\r' , // 0x0D -> CARRIAGE RETURN
'\u000e', // 0x0E -> SHIFT OUT '\u000e', // 0x0E -> SHIFT OUT
'\ufffe', // 0x0F -> UNDEFINED '\ufffe', // 0x0F -> UNDEFINED
'\ufffe', // 0x10 -> UNDEFINED '\ufffe', // 0x10 -> UNDEFINED
@@ -152,7 +152,7 @@ object PetsciiEncoding {
'\uf113', //  0x8A -> FUNCTION KEY 4 (CUS) '\uf113', //  0x8A -> FUNCTION KEY 4 (CUS)
'\uf115', //  0x8B -> FUNCTION KEY 6 (CUS) '\uf115', //  0x8B -> FUNCTION KEY 6 (CUS)
'\uf117', //  0x8C -> FUNCTION KEY 8 (CUS) '\uf117', //  0x8C -> FUNCTION KEY 8 (CUS)
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN) '\n' , // 0x8D -> LINE FEED
'\u000f', //  0x8E -> SHIFT IN '\u000f', //  0x8E -> SHIFT IN
'\ufffe', // 0x8F -> UNDEFINED '\ufffe', // 0x8F -> UNDEFINED
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS) '\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
@@ -283,7 +283,7 @@ object PetsciiEncoding {
'\ufffe', // 0x0A -> UNDEFINED '\ufffe', // 0x0A -> UNDEFINED
'\ufffe', // 0x0B -> UNDEFINED '\ufffe', // 0x0B -> UNDEFINED
'\ufffe', // 0x0C -> UNDEFINED '\ufffe', // 0x0C -> UNDEFINED
'\n' , // 0x0D -> LINE FEED (RETURN) '\r' , // 0x0D -> CARRIAGE RETURN
'\u000e', // 0x0E -> SHIFT OUT '\u000e', // 0x0E -> SHIFT OUT
'\ufffe', // 0x0F -> UNDEFINED '\ufffe', // 0x0F -> UNDEFINED
'\ufffe', // 0x10 -> UNDEFINED '\ufffe', // 0x10 -> UNDEFINED
@@ -411,7 +411,7 @@ object PetsciiEncoding {
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS) '\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS) '\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS) '\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN) '\n' , // 0x8D -> LINE FEED
'\u000f', // 0x8E -> SHIFT IN '\u000f', // 0x8E -> SHIFT IN
'\ufffe', // 0x8F -> UNDEFINED '\ufffe', // 0x8F -> UNDEFINED
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS) '\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
@@ -1061,7 +1061,6 @@ object PetsciiEncoding {
'}' -> '├' '}' -> '├'
'|' -> '│' '|' -> '│'
'\\' -> '╲' '\\' -> '╲'
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
else -> chr else -> chr
} }
@@ -1077,10 +1076,7 @@ object PetsciiEncoding {
} }
else -> { else -> {
val case = if (lowercase) "lower" else "upper" val case = if (lowercase) "lower" else "upper"
if(chr.isISOControl()) throw CharConversionException("no ${case}Petscii character for '${chr}' (${chr.code})")
throw CharConversionException("no ${case}Petscii character for char #${chr.code}")
else
throw CharConversionException("no ${case}Petscii character for char #${chr.code} '${chr}'")
} }
} }
} }
@@ -1123,10 +1119,7 @@ object PetsciiEncoding {
} }
else -> { else -> {
val case = if (lowercase) "lower" else "upper" val case = if (lowercase) "lower" else "upper"
if(chr.isISOControl()) throw CharConversionException("no ${case}Screencode character for '${chr}' (${chr.code})")
throw CharConversionException("no ${case}Screencode character for char #${chr.code}")
else
throw CharConversionException("no ${case}Screencode character for char #${chr.code} '${chr}'")
} }
} }
} }

View File

@@ -15,10 +15,12 @@ class CX16MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x0700u // $0700-$077f inclusive
override var ESTACK_HI = 0x0780u // $0780-$07ff inclusive
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000 override val BSSHIGHRAM_END = 0xc000u // rom starts here.
override val BSSGOLDENRAM_START = 0x0400u
override val BSSGOLDENRAM_END = 0x07ffu
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam override lateinit var golden: GoldenRam
@@ -38,7 +40,7 @@ class CX16MachineDefinition: IMachineDefinition {
when(selectedEmulator) { when(selectedEmulator) {
1 -> { 1 -> {
emulator = "x16emu" emulator = "x16emu"
extraArgs = listOf("-debug") extraArgs = emptyList()
} }
2 -> { 2 -> {
emulator = "box16" emulator = "box16"
@@ -51,7 +53,7 @@ class CX16MachineDefinition: IMachineDefinition {
} }
println("\nStarting Commander X16 emulator $emulator...") println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline).inheritIO() val processb = ProcessBuilder(cmdline).inheritIO()
processb.environment()["PULSE_LATENCY_MSEC"] = "10" processb.environment()["PULSE_LATENCY_MSEC"] = "10"
val process: Process = processb.start() val process: Process = processb.start()
@@ -62,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions) zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u) golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
} }
} }

View File

@@ -40,6 +40,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.DONTUSE -> { ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all free.clear() // don't use zeropage at all
} }
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
} }
val distinctFree = free.distinct() val distinctFree = free.distinct()
@@ -47,8 +48,8 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
free.addAll(distinctFree) free.addAll(distinctFree)
removeReservedFromFreePool() removeReservedFromFreePool()
allocateCx16VirtualRegisters() allocateCx16VirtualRegisters()
retainAllowed()
} }
} }

View File

@@ -1,57 +0,0 @@
package prog8.code.target.pet
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class PETMachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0401u
override val BSSHIGHRAM_START = 0u
override val BSSHIGHRAM_END = 0u
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The pet target only supports the main emulator (Vice).")
return
}
println("\nStarting PET emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process=processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = PETZeropage(compilerOptions)
// there's no golden ram.
}
}

View File

@@ -1,59 +0,0 @@
package prog8.code.target.pet
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
// reference: http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
init {
if (options.floats) {
throw InternalCompilerException("PET target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
ZeropageType.DONTUSE
))
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x00u..0xffu)
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x00u..0xffu)
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
}
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> {
free.addAll(0xb3u..0xbau) // TODO more?
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {
TODO("Not known if PET can put the virtual regs in ZP")
}
}

View File

@@ -12,20 +12,20 @@ class VirtualMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble() override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble() override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 8 // 64-bits double override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override var ESTACK_LO = 0u // not actually used
override var ESTACK_HI = 0u // not actually used
override val BSSHIGHRAM_START = 0u // not actually used override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used override val BSSHIGHRAM_END = 0u // not actually used
override val BSSGOLDENRAM_START = 0u // not actually used
override val BSSGOLDENRAM_END = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used override lateinit var zeropage: Zeropage // not actually used
override lateinit var golden: GoldenRam // not actually used override lateinit var golden: GoldenRam // not actually used
override fun getFloatAsmBytes(num: Number): String { override fun getFloatAsmBytes(num: Number): String {
// little endian binary representation // little endian binary representation
val bits = num.toDouble().toBits().toULong() val bits = num.toFloat().toBits().toUInt()
val hexStr = bits.toString(16).padStart(16, '0') val hexStr = bits.toString(16).padStart(8, '0')
val parts = hexStr.chunked(2).map { "\$" + it } val parts = hexStr.chunked(2).map { "\$" + it }
return parts.joinToString(", ") return parts.joinToString(", ")
} }

View File

@@ -1,7 +1,9 @@
plugins { plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
} }
java { java {
@@ -26,20 +28,18 @@ 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.20" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
} }
sourceSets { sourceSets {
main { main {
java { java {
srcDir "${project.projectDir}/src" srcDirs = ["${project.projectDir}/src"]
} }
resources { resources {
srcDir "${project.projectDir}/res" srcDirs = ["${project.projectDir}/res"]
} }
} }
test { test {

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,21 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
var linesByFour = getLinesBy(lines, 4) var linesByFour = getLinesBy(lines, 4)
var mods = optimizeIncDec(linesByFour) var mods = optimizeUselessStackByteWrites(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeIncDec(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeCmpSequence(linesByFour)
if(mods.isNotEmpty()) { if(mods.isNotEmpty()) {
apply(mods, lines) apply(mods, lines)
linesByFour = getLinesBy(lines, 4) linesByFour = getLinesBy(lines, 4)
@@ -29,21 +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()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeUnneededTempvarInAdd(linesByFour)
if(mods.isNotEmpty()) { if(mods.isNotEmpty()) {
apply(mods, lines) apply(mods, lines)
linesByFour = getLinesBy(lines, 4) linesByFour = getLinesBy(lines, 4)
@@ -58,7 +58,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++ numberOfOptimizations++
} }
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen) mods = optimizeSamePointerIndexing(linesByFourteen)
if(mods.isNotEmpty()) { if(mods.isNotEmpty()) {
apply(mods, lines) apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14) linesByFourteen = getLinesBy(lines, 14)
@@ -75,48 +75,61 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz") private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx") private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false) private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
private fun apply(modifications: List<Modification>, lines: MutableList<String>) { private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) { for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
if(modification.remove) { if(modification.remove)
if(modification.removeLabel)
lines.removeAt(modification.lineIndex) lines.removeAt(modification.lineIndex)
else {
val line = lines[modification.lineIndex]
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
lines.removeAt(modification.lineIndex)
else if (haslabel(line)) {
val label = keeplabel(line)
if (label.isNotEmpty())
lines[modification.lineIndex] = label
else
lines.removeAt(modification.lineIndex)
} else lines.removeAt(modification.lineIndex)
}
}
else else
lines[modification.lineIndex] = modification.replacement!! lines[modification.lineIndex] = modification.replacement!!
} }
} }
private fun haslabel(line: String): Boolean {
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
}
private fun keeplabel(line: String): String {
if(':' in line)
return line.substringBefore(':') + ':'
val splits = line.split('\t', ' ', limit=2)
return if(splits.size>1) splits[0] + ':' else ""
}
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) = private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
// all lines (that aren't empty or comments) in sliding windows of certain size // all lines (that aren't empty or comments) in sliding windows of certain size
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false) lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// when statement (on bytes) generates a sequence of:
// lda $ce01,x
// cmp #$20
// beq check_prog8_s72choice_32
// lda $ce01,x
// cmp #$21
// beq check_prog8_s73choice_33
// the repeated lda can be removed
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
lines[1].value.trim().startsWith("cmp ") &&
lines[2].value.trim().startsWith("beq ") &&
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
}
}
return mods
}
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
// this is a lot harder for word values because the instruction sequence varies.
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
lines[1].value.trim()=="dex" &&
lines[2].value.trim()=="inx" &&
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
return mods
}
private fun optimizeSameAssignments( private fun optimizeSameAssignments(
linesByFourteen: Sequence<List<IndexedValue<String>>>, linesByFourteen: List<List<IndexedValue<String>>>,
machine: IMachineDefinition, machine: IMachineDefinition,
symbolTable: SymbolTable symbolTable: SymbolTable
): List<Modification> { ): List<Modification> {
@@ -306,7 +319,7 @@ private fun optimizeSameAssignments(
return mods return mods
} }
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> { private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y // Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
// if Y isn't modified in between we can omit the second LDY: // if Y isn't modified in between we can omit the second LDY:
@@ -343,57 +356,31 @@ private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<L
mods.add(Modification(lines[4].index, true, null)) mods.add(Modification(lines[4].index, true, null))
} }
} }
/*
beq +
lda #1
+
[ optional: label_xxxx_shortcut line here]
beq label_xxxx_shortcut / bne label_xxxx_shortcut
or *_afterif labels.
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
*/
if(first=="beq +" && second=="lda #1" && third=="+") {
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
else if(fourth.startsWith("label_") && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
if((fifth.startsWith("beq label_") || fifth.startsWith("bne label_")) &&
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
}
}
} }
return mods return mods
} }
private fun optimizeStoreLoadSame( private fun optimizeStoreLoadSame(
linesByFour: Sequence<List<IndexedValue<String>>>, linesByFour: List<List<IndexedValue<String>>>,
machine: IMachineDefinition, machine: IMachineDefinition,
symbolTable: SymbolTable symbolTable: SymbolTable
): List<Modification> { ): List<Modification> {
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
for (lines in linesByFour) { for (lines in linesByFour) {
val first = lines[1].value.trimStart() val first = lines[1].value.trimStart()
val second = lines[2].value.trimStart() val second = lines[2].value.trimStart()
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
if ((first.startsWith("sta ") && second.startsWith("lda ")) || if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("stx ") && second.startsWith("ldx ")) || (first.startsWith("stx ") && second.startsWith("ldx ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) || (first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("lda ") && second.startsWith("lda ")) || (first.startsWith("lda ") && second.startsWith("lda ")) ||
(first.startsWith("ldy ") && second.startsWith("ldy ")) || (first.startsWith("ldy ") && second.startsWith("ldy ")) ||
(first.startsWith("ldx ") && second.startsWith("ldx ")) (first.startsWith("ldx ") && second.startsWith("ldx ")) ||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("stx ") && second.startsWith("ldx "))
) { ) {
val third = lines[3].value.trimStart() val third = lines[3].value.trimStart()
val attemptRemove = val attemptRemove =
@@ -432,21 +419,15 @@ private fun optimizeStoreLoadSame(
} else if(first=="phx" && second=="pla") { } else if(first=="phx" && second=="pla") {
mods.add(Modification(lines[1].index, true, null)) mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " txa")) mods.add(Modification(lines[2].index, false, " txa"))
} else if(first=="phx" && second=="ply") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " txy"))
} else if(first=="phy" && second=="pla") { } else if(first=="phy" && second=="pla") {
mods.add(Modification(lines[1].index, true, null)) mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tya")) mods.add(Modification(lines[2].index, false, " tya"))
} } else if(first=="phy" && second=="plx") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tyx"))
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
(first.startsWith("ldy ") && second.startsWith("sty "))
) {
val firstLoc = first.substring(4).trimStart()
val secondLoc = second.substring(4).trimStart()
if (firstLoc == secondLoc)
mods.add(Modification(lines[2].index, true, null))
} }
} }
return mods return mods
@@ -477,7 +458,7 @@ private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
} }
} }
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> { private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated. // sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
for (lines in linesByFour) { for (lines in linesByFour) {
@@ -494,17 +475,14 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
mods.add(Modification(lines[0].index, true, null)) mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null)) mods.add(Modification(lines[1].index, true, null))
} }
} }
return mods return mods
} }
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> { private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// jsr Sub + rts -> jmp Sub // jsr Sub + rts -> jmp Sub
// rts + jmp -> remove jmp // rts + jmp -> remove jmp
// rts + bxx -> remove bxx // rts + bxx -> remove bxx
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
// and some other optimizations.
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
for (lines in linesByFour) { for (lines in linesByFour) {
@@ -536,109 +514,6 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
else if (" bvc" in second || "\tbvc" in second) else if (" bvc" in second || "\tbvc" in second)
mods += Modification(lines[1].index, true, null) mods += Modification(lines[1].index, true, null)
} }
if (!haslabel(second)) {
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
) {
mods.add(Modification(lines[1].index, true, null))
}
else if(" cmp #0" in second || "\tcmp #0" in second) {
// there are many instructions that modify A and set the bits...
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
if(" $instr" in first || "\t$instr" in first) {
mods.add(Modification(lines[1].index, true, null))
}
}
}
}
/*
LDA NUM1
CMP NUM2
BCC LABEL
BEQ LABEL
(or something similar) which branches to LABEL when NUM1 <= NUM2. (In this case NUM1 and NUM2 are unsigned numbers.) However, consider the following sequence:
LDA NUM2
CMP NUM1
BCS LABEL
*/
val tfirst = first.trimStart()
val tsecond = second.trimStart()
val tthird = lines[2].value.trimStart()
val tfourth = lines[3].value.trimStart()
if(tfirst.startsWith("lda") && tsecond.startsWith("cmp") && tthird.startsWith("bcc") && tfourth.startsWith("beq")) {
val label = tthird.substring(4)
if(label==tfourth.substring(4)) {
mods += Modification(lines[0].index, false, " lda ${tsecond.substring(4)}")
mods += Modification(lines[1].index, false, " cmp ${tfirst.substring(4)}")
mods += Modification(lines[2].index, false, " bcs $label")
mods += Modification(lines[3].index, true, null)
}
}
} }
return mods return mods
} }
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
val mods = mutableListOf<Modification>()
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
if(lines[0].value.trimStart().startsWith("ph$register")) {
if(lines[2].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
if(register!in second
&& !second.startsWith("jsr")
&& !second.startsWith("pl")
&& !second.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
}
else if (lines[3].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
val third = lines[2].value.trimStart().take(6).lowercase()
if(register !in second && register !in third
&& !second.startsWith("jsr") && !third.startsWith("jsr")
&& !second.startsWith("pl") && !third.startsWith("pl")
&& !second.startsWith("ph") && !third.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
}
for (lines in linesByFour) {
optimize('a', lines)
optimize('x', lines)
optimize('y', lines)
}
return mods
}
private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
// this can be performed without the scratch variable: clc / adc something
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
val first = lines[0].value.trimStart()
val second = lines[1].value.trimStart()
val third = lines[2].value.trimStart()
val fourth = lines[3].value.trimStart()
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
if(fourth.substring(4)==first.substring(4)) {
mods.add(Modification(lines[0].index, false, " clc"))
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
return mods
}

View File

@@ -1,8 +1,9 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.ast.PtAsmSub import prog8.code.ast.*
import prog8.code.core.Cx16VirtualRegisters import prog8.code.core.Cx16VirtualRegisters
import prog8.code.core.RegisterOrPair import prog8.code.core.RegisterOrPair
import prog8.code.core.RegisterOrStatusflag
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> { fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
@@ -10,19 +11,50 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
// order is: // order is:
// 1) cx16 virtual word registers, // 1) cx16 virtual word registers,
// 2) paired CPU registers, // 2) paired CPU registers,
// 3) single CPU registers (order Y,X,A), // 3) single CPU registers (X last), except A,
// 4) CPU Carry status flag // 4) CPU Carry status flag
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
val args = sub.parameters.withIndex() val args = sub.parameters.withIndex()
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters } val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY) val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters } val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
val (singleRegs, rest) = args3.partition { it.value.first.registerOrPair != null } val (regsWithoutA, args4) = args3.partition { it.value.first.registerOrPair != RegisterOrPair.A }
val (regA, rest) = args4.partition { it.value.first.registerOrPair != null }
cx16regs.forEach { order += it.index } cx16regs.forEach { order += it.index }
pairedRegs.forEach { order += it.index } pairedRegs.forEach { order += it.index }
singleRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index } regsWithoutA.forEach {
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null}) if(it.value.first.registerOrPair != RegisterOrPair.X)
order += it.index
}
regsWithoutA.firstOrNull { it.value.first.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
rest.forEach { order += it.index } rest.forEach { order += it.index }
regA.forEach { order += it.index }
require(order.size==sub.parameters.size) require(order.size==sub.parameters.size)
return order return order
} }
fun asmsub6502ArgsHaveRegisterClobberRisk(
args: List<PtExpression>,
params: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>
): Boolean {
fun isClobberRisk(expr: PtExpression): Boolean {
when (expr) {
is PtArrayIndexer -> {
return params.any {
it.first.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
}
}
is PtBuiltinFunctionCall -> {
if (expr.name == "lsb" || expr.name == "msb")
return isClobberRisk(expr.args[0])
if (expr.name == "mkword")
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
return !expr.isSimple()
}
else -> return !expr.isSimple()
}
}
return args.size>1 && args.any { isClobberRisk(it) }
}

View File

@@ -1,8 +1,14 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.C64Target import prog8.code.target.C64Target
import java.io.File
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
internal class AssemblyProgram( internal class AssemblyProgram(
@@ -22,20 +28,15 @@ internal class AssemblyProgram(
val assemblerCommand: List<String> val assemblerCommand: List<String>
when (compTarget.name) { when (compTarget.name) {
in setOf("c64", "c128", "cx16", "pet32") -> { in setOf("c64", "c128", "cx16") -> {
// CBM machines .prg generation. // CBM machines .prg generation.
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently) // add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", // "-Wno-strict-bool", "-Werror", "-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor" "--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
) )
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet) if(options.asmQuiet)
command.add("--quiet") command.add("--quiet")
@@ -64,15 +65,10 @@ internal class AssemblyProgram(
// TODO are these options okay for atari? // TODO are these options okay for atari?
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", // "-Werror", "-Wno-strict-bool" "-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
"--no-monitor" "--no-monitor"
) )
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet) if(options.asmQuiet)
command.add("--quiet") command.add("--quiet")
@@ -134,3 +130,18 @@ internal class AssemblyProgram(
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n") viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
} }
} }
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
return com.github.michaelbull.result.runCatching {
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").text
}.mapError { NoSuchFileException(File(filename)) }
} else {
val sib = Path(source.origin).resolveSibling(filename)
if (sib.isRegularFile())
Ok(SourceCode.File(sib).text)
else
Ok(SourceCode.File(Path(filename)).text)
}
}

View File

@@ -0,0 +1,929 @@
package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.core.*
import kotlin.math.absoluteValue
internal class ExpressionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val allocator: VariableAllocator) {
@Deprecated("avoid calling this as it generates slow evalstack based code")
internal fun translateExpression(expression: PtExpression) {
if (this.asmgen.options.slowCodegenWarnings) {
asmgen.errors.warn("slow stack evaluation used for expression", expression.position)
}
translateExpressionInternal(expression)
}
// the rest of the methods are all PRIVATE
private fun translateExpressionInternal(expression: PtExpression) {
when(expression) {
is PtPrefix -> translateExpression(expression)
is PtBinaryExpression -> translateExpression(expression)
is PtArrayIndexer -> translateExpression(expression)
is PtTypeCast -> translateExpression(expression)
is PtAddressOf -> translateExpression(expression)
is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
is PtNumber -> translateExpression(expression)
is PtIdentifier -> translateExpression(expression)
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PtContainmentCheck -> translateContainmentCheck(expression)
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
else -> TODO("missing expression asmgen for $expression")
}
}
private fun translateContainmentCheck(check: PtContainmentCheck) {
asmgen.assignExpressionToRegister(check, RegisterOrPair.A)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation
val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(call)
asmgen.translateFunctionCall(call)
if(sub.regXasResult()) {
// store the return value in X somewhere that we can access again below
asmgen.out(" stx P8ZP_SCRATCH_REG")
}
asmgen.restoreXafterCall(call)
val returns: List<Pair<RegisterOrStatusflag, DataType>> = sub.returnsWhatWhere()
for ((reg, _) in returns) {
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
if (reg.registerOrPair != null) {
when (reg.registerOrPair!!) {
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
RegisterOrPair.R0,
RegisterOrPair.R1,
RegisterOrPair.R2,
RegisterOrPair.R3,
RegisterOrPair.R4,
RegisterOrPair.R5,
RegisterOrPair.R6,
RegisterOrPair.R7,
RegisterOrPair.R8,
RegisterOrPair.R9,
RegisterOrPair.R10,
RegisterOrPair.R11,
RegisterOrPair.R12,
RegisterOrPair.R13,
RegisterOrPair.R14,
RegisterOrPair.R15 -> {
asmgen.out(
"""
lda cx16.${reg.registerOrPair.toString().lowercase()}
sta P8ESTACK_LO,x
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
sta P8ESTACK_HI,x
dex
""")
}
}
} else when(reg.statusflag) {
Statusflag.Pc -> {
asmgen.out("""
lda #0
rol a
sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pz -> {
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pv -> {
asmgen.out("""
bvs +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pn -> {
asmgen.out("""
bmi +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
null -> {}
}
}
}
private fun translateExpression(typecast: PtTypeCast) {
translateExpressionInternal(typecast.value)
when(typecast.value.type) {
DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
when(typecast.type) {
DataType.BYTE, DataType.UBYTE -> {}
DataType.WORD, DataType.UWORD -> {}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.WORD -> {
when(typecast.type) {
DataType.BYTE, DataType.UBYTE -> {}
DataType.WORD, DataType.UWORD -> {}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.FLOAT -> {
when(typecast.type) {
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
DataType.FLOAT -> {}
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.STR -> {
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
}
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
else -> throw AssemblyError("weird type")
}
}
private fun translateExpression(expr: PtAddressOf) {
val name = asmgen.asmVariableName(expr.identifier)
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: PtNumber) {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
DataType.UWORD, DataType.WORD -> asmgen.out("""
lda #<${expr.number.toHex()}
sta P8ESTACK_LO,x
lda #>${expr.number.toHex()}
sta P8ESTACK_HI,x
dex
""")
DataType.FLOAT -> {
val floatConst = allocator.getFloatAsmConst(expr.number)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
}
else -> throw AssemblyError("weird type")
}
}
private fun translateExpression(expr: PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
}
in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("stack push weird variable type $expr")
}
}
private fun translateExpression(expr: PtBinaryExpression) {
require(!asmgen.options.useNewExprCode)
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
return
val leftDt = expr.left.type
val rightDt = expr.right.type
// compare with zero
if(expr.operator in ComparisonOperators) {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.asConstInteger()
if(rightVal==0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators)
return translateCompareStrings(expr.left, expr.operator, expr.right)
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
// the general, non-optimized cases
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
translateExpressionInternal(expr.left)
translateExpressionInternal(expr.right)
when (leftDt) {
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
else -> throw AssemblyError("non-numerical datatype")
}
}
private fun translateSomewhatOptimized(left: PtExpression, operator: String, right: PtExpression): Boolean {
val leftDt = left.type
val rightDt = right.type
when(operator) {
"+" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVal = left.asConstInteger()
val rightVal = right.asConstInteger()
if (leftVal!=null && leftVal in -4..4) {
translateExpressionInternal(right)
if(rightDt in ByteDatatypes) {
val incdec = if(leftVal<0) "dec" else "inc"
repeat(leftVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(leftVal<0) {
repeat(leftVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(leftVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return true
}
else if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "dec" else "inc"
repeat(rightVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(rightVal<0) {
repeat(rightVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(rightVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return true
}
}
}
"-" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = right.asConstInteger()
if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "inc" else "dec"
repeat(rightVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(rightVal>0) {
repeat(rightVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(rightVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return true
}
}
}
">>" -> {
val amount = right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(left)
when (leftDt) {
DataType.UBYTE -> {
if (amount <= 2)
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x")
repeat(amount) { asmgen.out(" lsr a") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
}
DataType.BYTE -> {
if (amount <= 2)
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
}
DataType.UWORD -> {
if(amount>=16) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
return true
}
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_right_uw_7")
amountLeft -= 7
}
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_uw_$amountLeft")
}
DataType.WORD -> {
if(amount>=16) {
asmgen.out("""
lda P8ESTACK_HI+1,x
bmi +
lda #0
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
beq ++
+ lda #255
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
+""")
return true
}
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_right_w_7")
amountLeft -= 7
}
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_w_$amountLeft")
}
else -> throw AssemblyError("weird type")
}
return true
}
}
"<<" -> {
val amount = right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(left)
if (leftDt in ByteDatatypes) {
if (amount <= 2)
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x")
repeat(amount) { asmgen.out(" asl a") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
} else {
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_left_w_7")
amountLeft -= 7
}
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
else
asmgen.out(" jsr math.shift_left_w_$amountLeft")
}
return true
}
}
"*" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVar = left as? PtIdentifier
val rightVar = right as? PtIdentifier
if(leftVar!=null && rightVar!=null && leftVar==rightVar) {
translateSquared(leftVar, leftDt)
return true
}
}
val value = right as? PtNumber
if(value!=null) {
if(rightDt in IntegerDatatypes) {
val amount = value.number.toInt()
if(amount==2) {
// optimize x*2 common case
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
asmgen.out(" asl P8ESTACK_LO+1,x")
} else {
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
}
return true
}
when(rightDt) {
DataType.UBYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return true
}
}
DataType.BYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return true
}
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
return true
}
}
DataType.UWORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return true
}
}
DataType.WORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return true
}
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
return true
}
}
else -> {}
}
}
}
}
"/" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = right.asConstInteger()
if(rightVal!=null && rightVal==2) {
translateExpressionInternal(left)
when (leftDt) {
DataType.UBYTE -> {
asmgen.out(" lsr P8ESTACK_LO+1,x")
}
DataType.UWORD -> {
asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
}
DataType.BYTE -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_LO+1,x
bpl +
inc P8ESTACK_LO+1,x
lda P8ESTACK_LO+1,x
+ asl a
ror P8ESTACK_LO+1,x""")
}
DataType.WORD -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_HI+1,x
bpl ++
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+ lda P8ESTACK_HI+1,x
+ asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x""")
}
else -> throw AssemblyError("weird dt")
}
return true
}
}
}
}
return false
}
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
if(expr.isSimple()) {
if(operator=="!=") {
when (dt) {
in ByteDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
asmgen.out("""
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
in WordDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
DataType.FLOAT -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
asmgen.out("""
jsr floats.SIGN
sta P8ESTACK_LO,x
dex""")
return
}
else -> {}
}
}
/* operator == is not worth it to special case, the code is mostly larger */
}
translateExpressionInternal(expr)
when(operator) {
"==" -> {
when(dt) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.equal_zero")
else -> throw AssemblyError("wrong dt")
}
}
"!=" -> {
when(dt) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.notequalzero_b")
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.notequalzero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.notequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
"<" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.less_zero")
else -> throw AssemblyError("wrong dt")
}
}
">" -> {
when(dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.greaterzero_ub")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterzero_sb")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.greaterzero_uw")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.greater_zero")
else -> throw AssemblyError("wrong dt")
}
}
"<=" -> {
when(dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzero_sb")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
">=" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.greaterequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
else -> throw AssemblyError("invalid comparison operator")
}
}
private fun translateSquared(variable: PtIdentifier, dt: DataType) {
val asmVar = asmgen.asmVariableName(variable)
when(dt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" lda $asmVar")
asmgen.signExtendAYlsb(dt)
asmgen.out(" jsr math.square")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $asmVar | ldy $asmVar+1 | jsr math.square")
}
else -> throw AssemblyError("require integer dt for square")
}
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: PtPrefix) {
translateExpressionInternal(expr.value)
when(expr.operator) {
"+" -> {}
"-" -> {
when(expr.type) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
else -> throw AssemblyError("weird type")
}
}
"~" -> {
when(expr.type) {
in ByteDatatypes ->
asmgen.out("""
lda P8ESTACK_LO+1,x
eor #255
sta P8ESTACK_LO+1,x
""")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
}
}
private fun translateExpression(arrayExpr: PtArrayIndexer) {
val elementDt = arrayExpr.type
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
if(arrayExpr.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayExpr.index.type != DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(arrayExpr.variable)) {
asmgen.out(" lda ($arrayVarName),y")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
}
asmgen.out(" sta P8ESTACK_LO,x | dex")
return
}
val constIndexNum = arrayExpr.index.asConstInteger()
if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
}
in WordDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue) | jsr floats.push_float")
}
else -> throw AssemblyError("weird element type")
}
} else {
when(elementDt) {
in ByteDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
asmgen.out("""
ldy #>$arrayVarName
clc
adc #<$arrayVarName
bcc +
iny
+ jsr floats.push_float""")
}
else -> throw AssemblyError("weird dt")
}
}
}
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
when(operator) {
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
"%" -> {
if(types==DataType.BYTE)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_ub")
}
"+" -> asmgen.out("""
lda P8ESTACK_LO+2,x
clc
adc P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
""")
"-" -> asmgen.out("""
lda P8ESTACK_LO+2,x
sec
sbc P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
""")
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
when(operator) {
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"%" -> {
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_uw")
}
"+" -> asmgen.out(" jsr prog8_lib.add_w")
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
"<<" -> asmgen.out(" jsr math.shift_left_w")
">>" -> {
if(dt==DataType.UWORD)
asmgen.out(" jsr math.shift_right_uw")
else
asmgen.out(" jsr math.shift_right_w")
}
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateBinaryOperatorFloats(operator: String) {
when(operator) {
"*" -> asmgen.out(" jsr floats.mul_f")
"/" -> asmgen.out(" jsr floats.div_f")
"+" -> asmgen.out(" jsr floats.add_f")
"-" -> asmgen.out(" jsr floats.sub_f")
"<" -> asmgen.out(" jsr floats.less_f")
">" -> asmgen.out(" jsr floats.greater_f")
"<=" -> asmgen.out(" jsr floats.lesseq_f")
">=" -> asmgen.out(" jsr floats.greatereq_f")
"==" -> asmgen.out(" jsr floats.equal_f")
"!=" -> asmgen.out(" jsr floats.notequal_f")
"%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
compareStringsProcessResultInA(operator)
}
private fun compareStringsProcessResultInA(operator: String) {
when(operator) {
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
"<=" -> asmgen.out("""
bpl +
lda #1
bne ++
+ lda #0
+ sta P8ESTACK_LO,x""")
">=" -> asmgen.out("""
bmi +
lda #1
bne ++
+ lda #0
+ sta P8ESTACK_LO,x""")
"<" -> asmgen.out("""
bmi +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x""")
">" -> asmgen.out("""
bpl +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x""")
}
asmgen.out(" dex")
}
}

View File

@@ -6,6 +6,26 @@ import prog8.code.ast.PtSub
import prog8.code.core.* import prog8.code.core.*
internal fun IPtSubroutine.regXasResult(): Boolean =
(this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal fun IPtSubroutine.shouldSaveX(): Boolean =
this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam()))
internal fun PtAsmSub.regXasParam(): Boolean =
parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
internal fun PtAsmSub.shouldKeepA(): KeepAresult {
// determine if A's value should be kept when preparing for calling the subroutine, and when returning from it
// it seems that we never have to save A when calling? will be loaded correctly after setup.
// but on return it depends on wether the routine returns something in A.
val saveAonReturn = returns.any { it.first.registerOrPair==RegisterOrPair.A || it.first.registerOrPair==RegisterOrPair.AY || it.first.registerOrPair==RegisterOrPair.AX }
return KeepAresult(false, saveAonReturn)
}
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> { internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
when(this) { when(this) {
is PtAsmSub -> { is PtAsmSub -> {
@@ -17,7 +37,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
emptyList() emptyList()
else { else {
val register = when (returntype!!) { val register = when (returntype!!) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null) in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null) in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null) DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null) else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
@@ -31,7 +51,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
internal fun PtSub.returnRegister(): RegisterOrStatusflag? { internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
return when(returntype) { return when(returntype) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null) in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null) in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null) DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
null -> null null -> null

View File

@@ -1,18 +1,13 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import prog8.code.StMemVar import prog8.code.ast.*
import prog8.code.StStaticVariable
import prog8.code.ast.PtForLoop
import prog8.code.ast.PtIdentifier
import prog8.code.ast.PtRange
import prog8.code.core.* import prog8.code.core.*
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
internal class ForLoopsAsmGen( internal class ForLoopsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal, private val asmgen: AsmGen6502Internal,
private val zeropage: Zeropage private val zeropage: Zeropage) {
) {
internal fun translate(stmt: PtForLoop) { internal fun translate(stmt: PtForLoop) {
val iterableDt = stmt.iterable.type val iterableDt = stmt.iterable.type
@@ -56,35 +51,7 @@ internal class ForLoopsAsmGen(
// 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("""
@@ -102,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) {
@@ -161,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
@@ -196,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
@@ -245,25 +182,41 @@ $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(""" asmgen.out("""
lda $varname lda $varname
sec sec
sbc #<${stepsize.absoluteValue} sbc #<${stepsize.absoluteValue}
sta $varname sta $varname
tax
lda $varname+1 lda $varname+1
sbc #>${stepsize.absoluteValue} sbc #>${stepsize.absoluteValue}
sta $varname+1 sta $varname+1
txa $modifiedLabel cmp #0 ; modified
bcc $endLabel
bne $loopLabel
lda $varname
$modifiedLabel2 cmp #0 ; modified
bcs $loopLabel
$endLabel""")
} else {
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
sta $varname
pha
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
pla
$modifiedLabel2 cmp #0 ; modified $modifiedLabel2 cmp #0 ; modified
lda $varname+1 lda $varname+1
$modifiedLabel sbc #0 ; modified $modifiedLabel sbc #0 ; modified
@@ -274,72 +227,24 @@ $endLabel""")
} }
} }
} }
}
else -> throw AssemblyError("range expression can only be byte or word") else -> throw AssemblyError("range expression can only be byte or word")
} }
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")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident) val iterableName = asmgen.asmVariableName(ident)
val symbol = asmgen.symbolTable.lookup(ident.name) val symbol = asmgen.symbolTable.lookup(ident.name)
val numElements = when(symbol) { val decl = symbol!!.astNode as IPtVariable
is StStaticVariable -> symbol.length!! val numElements = when(decl) {
is StMemVar -> symbol.length!! is PtConstant -> throw AssemblyError("length of non-array requested")
else -> 0 is PtMemMapped -> decl.arraySize
is PtVariable -> decl.arraySize
} }
when(iterableDt) { when(iterableDt) {
DataType.STR -> { DataType.STR -> {
@@ -359,7 +264,7 @@ $loopLabel lda ${65535.toHex()} ; modified
bne $loopLabel bne $loopLabel
$endLabel""") $endLabel""")
} }
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
val indexVar = asmgen.makeLabel("for_index") val indexVar = asmgen.makeLabel("for_index")
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
@@ -367,7 +272,7 @@ $loopLabel sty $indexVar
lda $iterableName,y lda $iterableName,y
sta ${asmgen.asmVariableName(stmt.variable)}""") sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(numElements<=255) { if(numElements!!<=255u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
@@ -382,11 +287,11 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(numElements>=16) { if(numElements>=16u) {
// 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") } failure = { asmgen.out("$indexVar .byte 0") }
) )
} else { } else {
@@ -395,7 +300,7 @@ $loopLabel sty $indexVar
asmgen.out(endLabel) asmgen.out(endLabel)
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
val length = numElements * 2 val length = numElements!! * 2u
val indexVar = asmgen.makeLabel("for_index") val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable) val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
@@ -406,7 +311,7 @@ $loopLabel sty $indexVar
lda $iterableName+1,y lda $iterableName+1,y
sta $loopvarName+1""") sta $loopvarName+1""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(length<=127) { if(length<=127u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
@@ -423,49 +328,11 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(length>=16) { if(length>=16u) {
// 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 -> {
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<=255) {
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>=16) {
// 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 {
@@ -723,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

@@ -11,13 +11,45 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) { internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) { internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
saveXbeforeCall(stmt)
translateFunctionCall(stmt) translateFunctionCall(stmt)
restoreXafterCall(stmt)
// just ignore any result values from the function call. // just ignore any result values from the function call.
} }
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
} else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
}
}
internal fun restoreXafterCall(stmt: PtFunctionCall) {
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
} else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
internal fun optimizeIntArgsViaRegisters(sub: PtSub) = internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean) (sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean) || (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
internal fun translateFunctionCall(call: PtFunctionCall) { internal fun translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call // Output only the code to set up the parameters and perform the actual call
@@ -26,13 +58,13 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this) // (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val symbol = asmgen.symbolTable.lookup(call.name) val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol?.astNode as IPtSubroutine val sub = symbol!!.astNode as IPtSubroutine
val subAsmName = asmgen.asmSymbolName(call.name) val subAsmName = asmgen.asmSymbolName(call.name)
if(sub is PtAsmSub) { if(sub is PtAsmSub) {
argumentsViaRegisters(sub, call) argumentsViaRegisters(sub, call)
if (sub.inline) { if (sub.inline && asmgen.options.optimize) {
// inline the subroutine. (regardless of optimization settings!) // inline the subroutine.
// we do this by copying the subroutine's statements at the call site. // we do this by copying the subroutine's statements at the call site.
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine // NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
// (this condition has been enforced by an ast check earlier) // (this condition has been enforced by an ast check earlier)
@@ -46,7 +78,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
else if(sub is PtSub) { else if(sub is PtSub) {
if(optimizeIntArgsViaRegisters(sub)) { if(optimizeIntArgsViaRegisters(sub)) {
if(sub.parameters.size==1) { if(sub.parameters.size==1) {
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register) argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
} else { } else {
// 2 byte params, second in Y, first in A // 2 byte params, second in Y, first in A
@@ -69,63 +101,40 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller // remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
} }
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
return when(arg) {
is PtBuiltinFunctionCall -> {
if (arg.name == "lsb" || arg.name == "msb")
return usesOtherRegistersWhileEvaluating(arg.args[0])
if (arg.name == "mkword")
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
return !arg.isSimple()
}
is PtAddressOf -> false
is PtIdentifier -> false
is PtMachineRegister -> false
is PtMemoryByte -> return usesOtherRegistersWhileEvaluating(arg.address)
is PtNumber -> false
is PtBool -> false
else -> true
}
}
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) { private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
val registersUsed = mutableListOf<RegisterOrStatusflag>();
fun usedA() = registersUsed.any {it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY}
fun usedX() = registersUsed.any {it.registerOrPair==RegisterOrPair.X || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.XY}
fun usedY() = registersUsed.any {it.registerOrPair==RegisterOrPair.Y || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.XY}
if(sub.parameters.size==1) { if(sub.parameters.size==1) {
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0]) argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0])
} else { } else {
val optimalEvalOrder = asmsub6502ArgsEvalOrder(sub) if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) {
optimalEvalOrder.forEach { registerArgsViaCpuStackEvaluation(call, sub)
} else {
asmsub6502ArgsEvalOrder(sub).forEach {
val param = sub.parameters[it] val param = sub.parameters[it]
val arg = call.args[it] val arg = call.args[it]
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
else if(registersUsed.any {it.statusflag!=null}) {
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
}
else {
if(usedX()) asmgen.saveRegisterStack(CpuRegister.X, false)
if(usedY()) asmgen.saveRegisterStack(CpuRegister.Y, false)
if(usedA()) asmgen.saveRegisterStack(CpuRegister.A, false)
val used = argumentViaRegister(sub, IndexedValue(it, param.second), arg)
if(usedA()) asmgen.restoreRegisterStack(CpuRegister.A, false)
if(usedY()) asmgen.restoreRegisterStack(CpuRegister.Y, true)
if(usedX()) asmgen.restoreRegisterStack(CpuRegister.X, true)
used
}
} else {
argumentViaRegister(sub, IndexedValue(it, param.second), arg) argumentViaRegister(sub, IndexedValue(it, param.second), arg)
} }
} }
} }
} }
private fun registerArgsViaCpuStackEvaluation(call: PtFunctionCall, callee: PtAsmSub) {
// this is called when one or more of the arguments are 'complex' and
// cannot be assigned to a register easily or risk clobbering other registers.
if(callee.parameters.isEmpty())
return
// use the cpu hardware stack as intermediate storage for the arguments.
val argOrder = asmsub6502ArgsEvalOrder(callee)
argOrder.reversed().forEach {
asmgen.pushCpuStack(callee.parameters[it].second.type, call.args[it])
}
argOrder.forEach {
val param = callee.parameters[it]
asmgen.popCpuStack(callee, param.second, param.first)
}
}
private fun argumentViaVariable(sub: PtSub, parameter: PtSubroutineParameter, value: PtExpression) { private fun argumentViaVariable(sub: PtSub, parameter: PtSubroutineParameter, value: PtExpression) {
// pass parameter via a regular variable (not via registers) // pass parameter via a regular variable (not via registers)
if(!isArgumentTypeCompatible(value.type, parameter.type)) if(!isArgumentTypeCompatible(value.type, parameter.type))
@@ -135,7 +144,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
asmgen.assignExpressionToVariable(value, varName, parameter.type) asmgen.assignExpressionToVariable(value, varName, parameter.type)
} }
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag { private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
// pass argument via a register parameter // pass argument via a register parameter
if(!isArgumentTypeCompatible(value.type, parameter.value.type)) if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible") throw AssemblyError("argument type incompatible")
@@ -153,21 +162,17 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
if (statusflag!=null) { if (statusflag!=null) {
if(requiredDt!=value.type) if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte or bool 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 -> {
val carrySet = value.number.toInt() != 0 val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc") asmgen.out(if(carrySet) " sec" else " clc")
} }
is PtBool -> {
asmgen.out(if(value.value) " sec" else " clc")
}
is PtIdentifier -> { is PtIdentifier -> {
val sourceName = asmgen.asmVariableName(value) val sourceName = asmgen.asmVariableName(value)
// note: cannot use X register here to store A because it might be used for other arguments
asmgen.out(""" asmgen.out("""
pha pha
clc clc
@@ -178,11 +183,15 @@ 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")
return RegisterOrStatusflag(null, statusflag)
} }
else { else {
// via register or register pair // via register or register pair
@@ -215,15 +224,12 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope) asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
} }
return RegisterOrStatusflag(register, null)
} }
} }
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType) if(argType isAssignableTo paramType)
return true return true
if(argType==DataType.BOOL && paramType==DataType.BOOL)
return true
if(argType in ByteDatatypes && paramType in ByteDatatypes) if(argType in ByteDatatypes && paramType in ByteDatatypes)
return true return true
if(argType in WordDatatypes && paramType in WordDatatypes) if(argType in WordDatatypes && paramType in WordDatatypes)
@@ -239,5 +245,3 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
return false return false
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
package prog8.codegen.cpu6502
import prog8.code.ast.PtIdentifier
import prog8.code.ast.PtNumber
import prog8.code.ast.PtPostIncrDecr
import prog8.code.ast.PtProgram
import prog8.code.core.*
internal class PostIncrDecrAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
internal fun translate(stmt: PtPostIncrDecr) {
val incr = stmt.operator=="++"
val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.array
val scope = stmt.definingISub()
when {
targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent)
when (stmt.target.type) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
in WordDatatypes -> {
if(incr)
asmgen.out(" inc $what | bne + | inc $what+1 |+")
else
asmgen.out("""
lda $what
bne +
dec $what+1
+ dec $what
""")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$what | ldy #>$what")
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
}
else -> throw AssemblyError("need numeric type")
}
}
targetMemory!=null -> {
when (val addressExpr = targetMemory.address) {
is PtNumber -> {
val what = addressExpr.number.toHex()
asmgen.out(if(incr) " inc $what" else " dec $what")
}
is PtIdentifier -> {
val what = asmgen.asmVariableName(addressExpr)
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
else -> {
asmgen.assignExpressionToRegister(addressExpr, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
}
}
targetArrayIdx!=null -> {
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
val elementDt = targetArrayIdx.type
val constIndex = targetArrayIdx.index.asConstInteger()
if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
in WordDatatypes -> {
if(incr)
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
else
asmgen.out("""
lda $asmArrayvarname+$indexValue
bne +
dec $asmArrayvarname+$indexValue+1
+ dec $asmArrayvarname+$indexValue
""")
}
DataType.FLOAT -> {
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
}
else -> throw AssemblyError("need numeric type")
}
}
else
{
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.out(" tax")
when(elementDt) {
in ByteDatatypes -> {
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
}
in WordDatatypes -> {
if(incr)
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
else
asmgen.out("""
lda $asmArrayvarname,x
bne +
dec $asmArrayvarname+1,x
+ dec $asmArrayvarname,x
""")
}
DataType.FLOAT -> {
asmgen.out("""
ldy #>$asmArrayvarname
clc
adc #<$asmArrayvarname
bcc +
iny
+ jsr floats.inc_var_f""")
}
else -> throw AssemblyError("weird array elt dt")
}
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
}
}
}

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 != "p8b_main" && allBlocks.first().name != "main") throw AssemblyError("first block should be 'main'")
throw AssemblyError("first block should be 'main' or 'p8b_main'")
if(errors.noErrors()) { if(errors.noErrors()) {
program.allBlocks().forEach { block2asm(it) } program.allBlocks().forEach { block2asm(it) }
@@ -49,7 +48,6 @@ internal class ProgramAndVarsGen(
} }
memorySlabs() memorySlabs()
tempVars()
footer() footer()
} }
} }
@@ -76,6 +74,8 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word") asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word") asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!) asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
asmgen.out(".endweak") asmgen.out(".endweak")
if(options.symbolDefs.isNotEmpty()) { if(options.symbolDefs.isNotEmpty()) {
@@ -104,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")
} }
} }
} }
@@ -120,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")
} }
} }
@@ -134,22 +134,29 @@ internal class ProgramAndVarsGen(
pha""") pha""")
} }
// make sure that on the cx16 and c64, basic rom is banked in again when we exit the program
when(compTarget.name) { when(compTarget.name) {
"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 p8b_main.p8s_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 p8b_main.p8s_start | lda #31 | sta $01") asmgen.out(" jsr main.start | lda #31 | sta $01")
asmgen.out(" jmp sys.cleanup_at_exit") if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
} }
"c128" -> { "c128" -> {
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00") asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00")
asmgen.out(" jmp sys.cleanup_at_exit") if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
} }
else -> asmgen.jmp("p8b_main.p8s_start") else -> asmgen.jmp("main.start")
} }
} }
@@ -166,120 +173,47 @@ internal class ProgramAndVarsGen(
} }
} }
private fun tempVars() {
asmgen.out("; expression temp vars\n .section BSS")
for((dt, count) in asmgen.tempVarsCounters) {
if(count>0) {
for(num in 1..count) {
val name = asmgen.buildTempVarName(dt, num)
when (dt) {
DataType.BOOL -> asmgen.out("$name .byte ?")
DataType.BYTE -> asmgen.out("$name .char ?")
DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.WORD -> asmgen.out("$name .sint ?")
DataType.UWORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
}
}
asmgen.out(" .send BSS")
}
private fun footer() { private fun footer() {
var relocateBssVars = false
var relocateBssSlabs = false
var relocatedBssStart = 0u
var relocatedBssEnd = 0u
if(options.varsGolden) {
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
}
relocateBssVars = true
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
}
else if(options.varsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
}
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
throw AssemblyError("slabs and vars high bank must be the same")
relocateBssVars = true
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
}
if(options.slabsGolden) {
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
}
relocateBssSlabs = true
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
}
else if(options.slabsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
}
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
throw AssemblyError("slabs and vars high bank must be the same")
relocateBssSlabs = true
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
}
asmgen.out("; bss sections") asmgen.out("; bss sections")
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}") if(options.varsHigh) {
if(relocateBssVars) { if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
if(!relocateBssSlabs) throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
}
// BSS vars in high ram area, memory() slabs just concatenated at the end of the program.
if(symboltable.allMemorySlabs.isNotEmpty()) {
asmgen.out(" .dsection slabs_BSS") asmgen.out(" .dsection slabs_BSS")
}
asmgen.out("prog8_program_end\t; end of program label for progend()") asmgen.out("prog8_program_end\t; end of program label for progend()")
asmgen.out(" * = ${relocatedBssStart.toHex()}") asmgen.out(" * = ${options.compTarget.machine.BSSHIGHRAM_START.toHex()}")
asmgen.out("prog8_bss_section_start") asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS") asmgen.out(" .dsection BSS")
if(relocateBssSlabs) asmgen.out(" .cerror * >= ${options.compTarget.machine.BSSHIGHRAM_END.toHex()}, \"too many variables for BSS section\"")
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
} else { } else {
// BSS vars followed by memory() slabs, concatenated at the end of the program.
asmgen.out("prog8_bss_section_start") asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS") asmgen.out(" .dsection BSS")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
if(!relocateBssSlabs) if(symboltable.allMemorySlabs.isNotEmpty()) {
asmgen.out(" .dsection slabs_BSS") asmgen.out(" .dsection slabs_BSS")
asmgen.out("prog8_program_end\t; end of program label for progend()")
if(relocateBssSlabs) {
asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
} }
asmgen.out("prog8_program_end\t; end of program label for progend()")
} }
} }
private fun block2asm(block: PtBlock) { private fun block2asm(block: PtBlock) {
asmgen.out("") asmgen.out("")
asmgen.out("; ---- block: '${block.name}' ----") asmgen.out("; ---- block: '${block.name}' ----")
if(block.options.address!=null) if(block.address!=null)
asmgen.out("* = ${block.options.address!!.toHex()}") asmgen.out("* = ${block.address!!.toHex()}")
else { else {
if(block.options.alignment==PtBlock.BlockAlignment.WORD) if(block.alignment==PtBlock.BlockAlignment.WORD)
asmgen.out("\t.align 2") asmgen.out("\t.align 2")
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE) else if(block.alignment==PtBlock.BlockAlignment.PAGE)
asmgen.out("\t.align $100") asmgen.out("\t.align $100")
} }
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc")) asmgen.out("${block.name}\t" + (if(block.forceOutput) ".block" else ".proc"))
asmgen.outputSourceLine(block) asmgen.outputSourceLine(block)
createBlockVariables(block) createBlockVariables(block)
@@ -302,7 +236,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" rts\n .bend") asmgen.out(" rts\n .bend")
} }
asmgen.out(if(block.options.forceOutput) "\n\t.bend" else "\n\t.pend") asmgen.out(if(block.forceOutput) "\n\t.bend" else "\n\t.pend")
} }
private fun getVars(scope: StNode): Map<String, StNode> = private fun getVars(scope: StNode): Map<String, StNode> =
@@ -335,14 +269,16 @@ internal class ProgramAndVarsGen(
internal fun translateAsmSubroutine(sub: PtAsmSub) { internal fun translateAsmSubroutine(sub: PtAsmSub) {
if(sub.inline) { if(sub.inline) {
return // subroutine gets inlined at call site. if(options.optimize) {
return
}
} }
asmgen.out("") asmgen.out("")
val asmStartScope: String val asmStartScope: String
val asmEndScope: String val asmEndScope: String
if(sub.definingBlock()!!.options.forceOutput) { if(sub.definingBlock()!!.forceOutput) {
asmStartScope = ".block" asmStartScope = ".block"
asmEndScope = ".bend" asmEndScope = ".bend"
} else { } else {
@@ -365,7 +301,7 @@ internal class ProgramAndVarsGen(
val asmStartScope: String val asmStartScope: String
val asmEndScope: String val asmEndScope: String
if(sub.definingBlock()!!.options.forceOutput) { if(sub.definingBlock()!!.forceOutput) {
asmStartScope = ".block" asmStartScope = ".block"
asmEndScope = ".bend" asmEndScope = ".bend"
} else { } else {
@@ -375,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)
@@ -397,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=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main")) if(sub.name=="start" && sub.definingBlock()!!.name=="main")
entrypointInitialization() entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) { if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
@@ -405,7 +339,7 @@ internal class ProgramAndVarsGen(
if(sub.parameters.size==1) { if(sub.parameters.size==1) {
val dt = sub.parameters[0].type val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name) val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypesWithBoolean) if(dt in ByteDatatypes)
asmgen.assignRegister(RegisterOrPair.A, target) asmgen.assignRegister(RegisterOrPair.A, target)
else else
asmgen.assignRegister(RegisterOrPair.AY, target) asmgen.assignRegister(RegisterOrPair.AY, target)
@@ -435,6 +369,12 @@ internal class ProgramAndVarsGen(
else -> throw AssemblyError("weird dt for extravar $dt") else -> throw AssemblyError("weird dt for extravar $dt")
} }
} }
if(asmGenInfo.usedRegsaveA) // will probably never occur
asmgen.out("prog8_regsaveA .byte ?")
if(asmGenInfo.usedRegsaveX)
asmgen.out("prog8_regsaveX .byte ?")
if(asmGenInfo.usedRegsaveY)
asmgen.out("prog8_regsaveY .byte ?")
if(asmGenInfo.usedFloatEvalResultVar1) if(asmGenInfo.usedFloatEvalResultVar1)
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}") asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2) if(asmGenInfo.usedFloatEvalResultVar2)
@@ -494,12 +434,12 @@ internal class ProgramAndVarsGen(
asmgen.out(""" asmgen.out("""
lda #<${name}_init_value lda #<${name}_init_value
ldy #>${name}_init_value ldy #>${name}_init_value
sta cx16.r0 sta cx16.r0L
sty cx16.r0+1 sty cx16.r0H
lda #<${name} lda #<${name}
ldy #>${name} ldy #>${name}
sta cx16.r1 sta cx16.r1L
sty cx16.r1+1 sty cx16.r1H
lda #<$size lda #<$size
ldy #>$size ldy #>$size
jsr sys.memcopy""") jsr sys.memcopy""")
@@ -518,6 +458,7 @@ internal class ProgramAndVarsGen(
} }
asmgen.out("""+ asmgen.out("""+
ldx #127 ; init estack ptr (half page)
clv clv
clc""") clc""")
} }
@@ -539,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
@@ -551,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
@@ -563,17 +504,9 @@ internal class ProgramAndVarsGen(
for ((scopedName, zpvar) in zpVariables) { for ((scopedName, zpvar) in zpVariables) {
if (scopedName.startsWith("cx16.r")) if (scopedName.startsWith("cx16.r"))
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
if(variable.dt in SplitWordArrayTypes) {
val lsbAddr = zpvar.address
val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
asmgen.out("${scopedName.substringAfterLast('.')}_msb \t= $msbAddr \t; zp ${zpvar.dt} (msbs)")
} else {
asmgen.out("${scopedName.substringAfterLast('.')} \t= ${zpvar.address} \t; zp ${zpvar.dt}") asmgen.out("${scopedName.substringAfterLast('.')} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
} }
} }
}
private fun nonZpVariables2asm(variables: List<StStaticVariable>) { private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
asmgen.out("") asmgen.out("")
@@ -605,16 +538,11 @@ internal class ProgramAndVarsGen(
private fun uninitializedVariable2asm(variable: StStaticVariable) { private fun uninitializedVariable2asm(variable: StStaticVariable) {
when (variable.dt) { when (variable.dt) {
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?") DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?") DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
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")
@@ -635,7 +563,6 @@ internal class ProgramAndVarsGen(
} else 0 } else 0
when (variable.dt) { when (variable.dt) {
DataType.BOOL -> TODO("bool var to asm")
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}") DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue") DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}") DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
@@ -660,7 +587,7 @@ internal class ProgramAndVarsGen(
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) { private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
when(dt) { when(dt) {
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> { DataType.ARRAY_UB -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros) val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16) if (data.size <= 16)
asmgen.out("$varname\t.byte ${data.joinToString()}") asmgen.out("$varname\t.byte ${data.joinToString()}")
@@ -700,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 {
@@ -728,7 +643,7 @@ internal class ProgramAndVarsGen(
private fun zeroFilledArray(numElts: Int): StArray { private fun zeroFilledArray(numElts: Int): StArray {
val values = mutableListOf<StArrayElement>() val values = mutableListOf<StArrayElement>()
repeat(numElts) { repeat(numElts) {
values.add(StArrayElement(0.0, null, null)) values.add(StArrayElement(0.0, null))
} }
return values return values
} }
@@ -765,23 +680,13 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> { private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) { return when (dt) {
DataType.ARRAY_BOOL ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
if(it.boolean!=null)
if(it.boolean==true) "1" else "0"
else {
val number = it.number!!
if(number==0.0) "0" else "1"
}
}
DataType.ARRAY_UB -> DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers // byte array can never contain pointer-to types, so treat values as all integers
array.map { array.map {
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')
} }
@@ -813,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

@@ -91,7 +91,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
if(errors.noErrors()) { if(errors.noErrors()) {
val sortedList = varsDontCare.sortedByDescending { it.scopedName } val sortedList = varsDontCare.sortedByDescending { it.scopedName }
for (variable in sortedList) { for (variable in sortedList) {
if(variable.dt in IntegerDatatypesWithBoolean) { if(variable.dt in IntegerDatatypes) {
if(zeropage.free.isEmpty()) { if(zeropage.free.isEmpty()) {
break break
} else { } else {

View File

@@ -1,253 +0,0 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.ast.PtBinaryExpression
import prog8.code.ast.PtExpression
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.AsmGen6502Internal
//
// This contains codegen for stack-based evaluation of binary expressions.
// It uses the CPU stack so depth is limited.
// It is called "as a last resort" if the optimized codegen path is unable
// to come up with a special case of the expression.
//
internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal
) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) {
in ByteDatatypesWithBoolean -> {
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
require(expr.operator in ComparisonOperators)
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
}
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign)
}
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
}
in WordDatatypes -> {
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
"both operands must be words"
}
return assignWordBinExpr(expr)
}
DataType.FLOAT -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
"both operands must be floats"
}
return assignFloatBinExpr(expr, assign)
}
else -> throw AssemblyError("weird expression type in assignment")
}
}
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
when(expr.operator) {
"+" -> TODO("word + at ${expr.position}")
"-" -> TODO("word - at ${expr.position}")
"*" -> TODO("word * at ${expr.position}")
"/" -> TODO("word / at ${expr.position}")
"<<" -> TODO("word << at ${expr.position}")
">>" -> TODO("word >> at ${expr.position}")
"%" -> TODO("word % at ${expr.position}")
"and" -> TODO("word logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("word logical or (with optional shortcircuit) ${expr.position}")
"&" -> TODO("word and at ${expr.position}")
"|" -> TODO("word or at ${expr.position}")
"^", "xor" -> TODO("word xor at ${expr.position}")
"==" -> TODO("word == at ${expr.position}")
"!=" -> TODO("word != at ${expr.position}")
"<" -> TODO("word < at ${expr.position}")
"<=" -> TODO("word <= at ${expr.position}")
">" -> TODO("word > at ${expr.position}")
">=" -> TODO("word >= at ${expr.position}")
else -> return false
}
}
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"*" -> {
TODO("byte * at ${expr.position}")
}
"/" -> {
TODO("byte / at ${expr.position}")
}
"<<" -> {
TODO("byte << at ${expr.position}")
}
">>" -> {
TODO("byte >> at ${expr.position}")
}
"%" -> {
TODO("byte % at ${expr.position}")
}
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
"&" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"|" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"==" -> {
TODO("byte == at ${expr.position}")
}
"!=" -> {
TODO("byte != at ${expr.position}")
}
"<" -> {
TODO("byte < at ${expr.position}")
}
"<=" -> {
TODO("byte <= at ${expr.position}")
}
">" -> {
TODO("byte > at ${expr.position}")
}
">=" -> {
TODO("byte >= at ${expr.position}")
}
else -> return false
}
}
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
assignFloatOperandsToFACandARG(expr.left, expr.right)
asmgen.out(" jsr floats.FADDT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"-" -> {
assignFloatOperandsToFACandARG(expr.right, expr.left)
asmgen.out(" jsr floats.FSUBT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"*" -> {
assignFloatOperandsToFACandARG(expr.left, expr.right)
asmgen.out(" jsr floats.FMULTT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"/" -> {
assignFloatOperandsToFACandARG(expr.right, expr.left)
asmgen.out(" jsr floats.FDIVT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"==" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_equal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"!=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_notequal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_less_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greater_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_lesseq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greatereq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
else -> TODO("float expression operator ${expr.operator}")
}
}
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
when(asmgen.options.compTarget.name) {
C64Target.NAME -> {
// c64 has a quirk: always make sure FAC2 is loaded last (done using CONUPK) otherwise the result will be corrupt on C64
// this requires some more forced copying around of float values in certain cases
if (right.isSimple()) {
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
} else {
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
asmgen.pushFAC1()
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.popFAC2()
}
}
Cx16Target.NAME -> {
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
if (!right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
if (!right.isSimple()) asmgen.popFAC1()
}
else -> TODO("don't know how to evaluate float expression for selected compilation target")
}
}
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
}
}

View File

@@ -10,16 +10,17 @@ internal enum class TargetStorageKind {
VARIABLE, VARIABLE,
ARRAY, ARRAY,
MEMORY, MEMORY,
REGISTER REGISTER,
STACK
} }
internal enum class SourceStorageKind { internal enum class SourceStorageKind {
LITERALBOOLEAN,
LITERALNUMBER, LITERALNUMBER,
VARIABLE, VARIABLE,
ARRAY, ARRAY,
MEMORY, MEMORY,
REGISTER, REGISTER,
STACK, // value is already present on stack
EXPRESSION, // expression in ast-form, still to be evaluated EXPRESSION, // expression in ast-form, still to be evaluated
} }
@@ -44,8 +45,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
} }
init { init {
if(register!=null && datatype !in NumericDatatypesWithBoolean) if(register!=null && datatype !in NumericDatatypes)
throw AssemblyError("must be numeric type") throw AssemblyError("register must be integer or float type")
} }
companion object { companion object {
@@ -114,12 +115,12 @@ 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!!
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER, TargetStorageKind.STACK -> {
false false
} }
} }
@@ -134,7 +135,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val memory: PtMemoryByte? = null, val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null, val register: RegisterOrPair? = null,
val number: PtNumber? = null, val number: PtNumber? = null,
val boolean: PtBool? = null,
val expression: PtExpression? = null val expression: PtExpression? = null
) )
{ {
@@ -149,9 +149,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val cv = value as? PtNumber val cv = value as? PtNumber
if(cv!=null) if(cv!=null)
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv) return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
val bv = value as? PtBool
if(bv!=null)
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
return when(value) { return when(value) {
// checked above: is PtNumber -> throw AssemblyError("should have been constant value") // checked above: is PtNumber -> throw AssemblyError("should have been constant value")
@@ -181,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")
@@ -199,7 +196,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
// allow some signed/unsigned relaxations // allow some signed/unsigned relaxations
fun withAdjustedDt(newType: DataType) = fun withAdjustedDt(newType: DataType) =
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression) AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
if(target.datatype!=datatype) { if(target.datatype!=datatype) {
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) { if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {

View File

@@ -5,7 +5,7 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) { override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypesWithBoolean -> 1 in ByteDatatypes -> 1
DataType.FLOAT -> 5 DataType.FLOAT -> 5
else -> 2 else -> 2
} }
@@ -18,8 +18,6 @@ internal object DummyMemsizer : IMemSizer {
} }
internal object DummyStringEncoder : IStringEncoding { internal object DummyStringEncoder : IStringEncoding {
override val defaultEncoding: Encoding = Encoding.ISO
override fun encodeString(str: String, encoding: Encoding): List<UByte> { override fun encodeString(str: String, encoding: Encoding): List<UByte> {
return emptyList() return emptyList()
} }
@@ -29,11 +27,11 @@ internal object DummyStringEncoder : IStringEncoding {
} }
} }
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter { internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
IErrorReporter {
val errors = mutableListOf<String>() val errors = mutableListOf<String>()
val warnings = mutableListOf<String>() val warnings = mutableListOf<String>()
val infos = mutableListOf<String>()
override fun err(msg: String, position: Position) { override fun err(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg" val text = "${position.toClickableStr()} $msg"
@@ -47,25 +45,13 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text) warnings.add(text)
} }
override fun info(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg"
if(text !in infos)
infos.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 noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun report() { override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") } warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") } errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
if(throwExceptionAtReportIfErrors) if(throwExceptionAtReportIfErrors)
finalizeNumErrors(errors.size, warnings.size, infos.size) finalizeNumErrors(errors.size, warnings.size)
if(!keepMessagesAfterReporting) { if(!keepMessagesAfterReporting) {
clear() clear()
} }
@@ -74,6 +60,5 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
fun clear() { fun clear() {
errors.clear() errors.clear()
warnings.clear() warnings.clear()
infos.clear()
} }
} }

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.*
@@ -22,7 +20,6 @@ class TestCodegen: FunSpec({
CbmPrgLauncherType.NONE, CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE, ZeropageType.DONTUSE,
zpReserved = emptyList(), zpReserved = emptyList(),
zpAllowed = CompilationOptions.AllZeropageAllowed,
floats = true, floats = true,
noSysInit = false, noSysInit = false,
compTarget = target, compTarget = target,
@@ -43,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",false, SourceCode.Generated("test"), PtBlock.Options(), 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))
@@ -95,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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
@@ -107,22 +104,12 @@ class TestCodegen: FunSpec({
Files.deleteIfExists(Path("${result.name}.asm")) Files.deleteIfExists(Path("${result.name}.asm"))
} }
test("64tass assembler available? - if this fails you need to install 64tass version 1.58 or newer 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,17 +29,17 @@ 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.20" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
} }
sourceSets { sourceSets {
main { main {
java { java {
srcDir "${project.projectDir}/src" srcDirs = ["${project.projectDir}/src"]
} }
resources { resources {
srcDir "${project.projectDir}/res" srcDirs = ["${project.projectDir}/res"]
} }
} }
} }

View File

@@ -15,7 +15,7 @@ class ExperiCodeGen: ICodeGeneratorBackend {
symbolTable: SymbolTable, symbolTable: SymbolTable,
options: CompilationOptions, options: CompilationOptions,
errors: IErrorReporter errors: IErrorReporter
): IAssemblyProgram { ): IAssemblyProgram? {
// you could write a code generator directly on the PtProgram AST, // you could write a code generator directly on the PtProgram AST,
// but you can also use the Intermediate Representation to build a codegen on: // but you can also use the Intermediate Representation to build a codegen on:
@@ -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,21 +28,18 @@ 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.20" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
} }
sourceSets { sourceSets {
main { main {
java { java {
srcDir "${project.projectDir}/src" srcDirs = ["${project.projectDir}/src"]
} }
resources { resources {
srcDir "${project.projectDir}/res" srcDirs = ["${project.projectDir}/res"]
} }
} }
test { test {

View File

@@ -14,6 +14,5 @@
<orderEntry type="module" module-name="intermediate" /> <orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" /> <orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" /> <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component> </component>
</module> </module>

View File

@@ -1,7 +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.* import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.intermediate.* import prog8.intermediate.*
@@ -11,146 +13,41 @@ 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)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore "pop" -> funcPop(call)
"callfar" -> funcCallfar(call) "popw" -> funcPopw(call)
"call" -> funcCall(call) "push" -> funcPush(call)
"pushw" -> funcPushw(call)
"rsave",
"rsavex",
"rrestore",
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"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)
"peek" -> funcPeek(call, IRDataType.BYTE) "peek" -> funcPeek(call)
"peekw" -> funcPeek(call, IRDataType.WORD) "peekw" -> funcPeekW(call)
"peekf" -> funcPeek(call, IRDataType.FLOAT) "poke" -> funcPoke(call)
"poke" -> funcPoke(call, IRDataType.BYTE) "pokew" -> funcPokeW(call)
"pokew" -> funcPoke(call, IRDataType.WORD) "pokemon" -> ExpressionCodeResult.EMPTY // easter egg function
"pokef" -> funcPoke(call, IRDataType.FLOAT)
"pokemon" -> funcPokemon(call)
"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)
"setlsb" -> funcSetLsbMsb(call, false) "rol" -> funcRolRor(Opcode.ROXL, call)
"setmsb" -> funcSetLsbMsb(call, true) "ror" -> funcRolRor(Opcode.ROXR, call)
"rol" -> funcRolRor(call) "rol2" -> funcRolRor(Opcode.ROL, call)
"ror" -> funcRolRor(call) "ror2" -> funcRolRor(Opcode.ROR, call)
"rol2" -> funcRolRor(call)
"ror2" -> funcRolRor(call)
"prog8_lib_stringcompare" -> funcStringCompare(call) "prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
"prog8_lib_arraycopy" -> funcArrayCopy(call)
else -> throw AssemblyError("missing builtinfunc for ${call.name}") else -> throw AssemblyError("missing builtinfunc for ${call.name}")
} }
} }
private fun funcArrayCopy(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val source = call.args[0] as PtIdentifier
val target = call.args[1] as PtIdentifier
val sourceLength = codeGen.symbolTable.getLength(source.name)!!
val targetLength = codeGen.symbolTable.getLength(target.name)!!
require(sourceLength==targetLength)
val result = mutableListOf<IRCodeChunkBase>()
val fromReg = codeGen.registers.nextFree()
val toReg = codeGen.registers.nextFree()
val countReg = codeGen.registers.nextFree()
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
// split words -> split words, copy lsb and msb arrays separately
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
}
else if(source.type in SplitWordArrayTypes) {
// split -> normal words
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
val fromRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromRegMsb, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_SPLITW_TO_NORMAL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to fromRegMsb, IRDataType.WORD to toReg, IRDataType.BYTE to countReg), returns = null)
}
else if(target.type in SplitWordArrayTypes) {
// normal -> split words
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
val toRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toRegMsb, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_NORMAL_TO_SPLITW, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to toRegMsb, IRDataType.BYTE to countReg), returns = null)
}
else {
// normal array to array copy (various element types)
val eltsize = codeGen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength * eltsize)
}
result += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val valueTr = exprGen.translateExpression(call.args[0])
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
return if(resultType==IRDataType.FLOAT) {
val resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null)
ExpressionCodeResult(result, resultType, -1, resultFpReg)
}
else {
val resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null)
ExpressionCodeResult(result, resultType, resultReg, -1)
}
}
private fun funcCall(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val addressTr = exprGen.translateExpression(call.args[0])
addToResult(result, addressTr, addressTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.CALLI, reg1 = addressTr.resultReg), null)
if(call.void)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else
return ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
}
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
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]
@@ -182,7 +79,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val left = exprGen.translateExpression(call.args[0]) val left = exprGen.translateExpression(call.args[0])
val right = exprGen.translateExpression(call.args[1]) val right = exprGen.translateExpression(call.args[1])
addToResult(result, left, left.resultReg, -1) addToResult(result, left, left.resultReg, -1)
@@ -206,34 +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 result = mutableListOf<IRCodeChunkBase>()
val lengthReg = codeGen.registers.nextFree()
if(arrayName.type in SplitWordArrayTypes) {
// any(lsb-array) or any(msb-array)
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
addToResult(result, trLsb, trLsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trLsb.resultReg)
val shortcircuitLabel = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = trLsb.resultReg, immediate = 0)
it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcircuitLabel)
it += IRInstruction(Opcode.PREPARECALL, immediate = 2)
}
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
addToResult(result, trMsb, trMsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trMsb.resultReg)
addInstr(result, IRInstruction(Opcode.ORR, IRDataType.BYTE, reg1=trLsb.resultReg, reg2=trMsb.resultReg), null)
result += IRCodeChunk(shortcircuitLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, trLsb.resultReg, -1)
}
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,
@@ -241,25 +112,20 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(arrayName) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength!! and 255), null) val lengthReg = codeGen.registers.nextFree()
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
if(arrayName.type in SplitWordArrayTypes) {
// this is a bit complicated to calculate.... have to check all recombined (lsb,msb) words for $0000
TODO("all(split words $arrayName)")
}
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,
@@ -268,11 +134,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val tr = exprGen.translateExpression(call.args[0])
val tr = exprGen.translateExpression(arrayName)
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!! and 255), 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)
} }
@@ -286,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)
@@ -302,54 +175,32 @@ 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")
} }
} }
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val vmDt = irType(call.type)
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
val resultReg = codeGen.registers.nextFree()
if(tr.dt==IRDataType.FLOAT) {
addToResult(result, tr, -1, tr.resultFpReg)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, fpReg1 = tr.resultFpReg)
}
} else {
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, reg2 = tr.resultReg) it += IRInstruction(Opcode.SGN, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
} }
} return ExpressionCodeResult(result, vmDt, resultReg, -1)
return ExpressionCodeResult(result, IRDataType.BYTE, 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
when(dt) {
DataType.UBYTE -> {
addToResult(result, tr, tr.resultReg, -1)
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) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
@@ -357,312 +208,210 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1) return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
} }
DataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg) private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val resultFpReg = codeGen.registers.nextFreeFloat() val code = IRCodeChunk(null, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return ExpressionCodeResult(result, IRDataType.BYTE, reg, -1)
}
private fun funcPopw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val code = IRCodeChunk(null, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return ExpressionCodeResult(result, IRDataType.WORD, reg, -1)
}
private fun funcPush(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg) it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=tr.resultReg)
} }
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
else -> throw AssemblyError("invalid dt")
private fun funcPushw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = tr.resultReg)
} }
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
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 lengthReg = codeGen.registers.nextFree()
val result = mutableListOf<IRCodeChunkBase>()
if(arrayName.type in SplitWordArrayTypes) {
// reverse the lsb and msb arrays both, independently
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
addToResult(result, trLsb, trLsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), null)
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
addToResult(result, trMsb, trMsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
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
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(arrayName) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null) val lengthReg = codeGen.registers.nextFree()
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>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val tr = exprGen.translateExpression(call.args[0])
val tr = exprGen.translateExpression(arrayName)
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 funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val resultReg = codeGen.registers.nextFree()
if((call.args[0] as? PtNumber)?.number == 0.0) {
// msb is 0, use EXT
val lsbTr = exprGen.translateExpression(call.args[1])
addToResult(result, lsbTr, lsbTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultReg, reg2 = lsbTr.resultReg), null)
} else {
val msbTr = exprGen.translateExpression(call.args[0]) val msbTr = exprGen.translateExpression(call.args[0])
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)
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultReg, reg2 = msbTr.resultReg, reg3 = lsbTr.resultReg), null)
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
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 { result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after) it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = lsbTr.resultReg, reg2 = msbTr.resultReg)
// 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, IRDataType.WORD, lsbTr.resultReg, -1)
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
} }
private fun funcMax(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcPokeW(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 funcPoke(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) { if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, dt, address = address) it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, address = address)
} }
} else { } else {
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)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, dt, reg1 = tr.resultReg) it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = tr.resultReg)
} }
} }
} else { } else {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
val tr = exprGen.translateExpression(call.args[1]) val tr = exprGen.translateExpression(call.args[1])
if(dt==IRDataType.FLOAT) {
addToResult(result, tr, -1, tr.resultFpReg)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, dt, fpReg1 = tr.resultFpReg, address = address)
}
} else {
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, dt, reg1 = tr.resultReg, address = address) it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, address = address)
}
} }
} else { } else {
val addressTr = exprGen.translateExpression(call.args[0]) val addressTr = exprGen.translateExpression(call.args[0])
addToResult(result, addressTr, addressTr.resultReg, -1) addToResult(result, addressTr, addressTr.resultReg, -1)
val valueTr = exprGen.translateExpression(call.args[1]) val valueTr = exprGen.translateExpression(call.args[1])
if(dt==IRDataType.FLOAT) {
addToResult(result, valueTr, -1, valueTr.resultFpReg)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREI, IRDataType.FLOAT, reg1 = addressTr.resultReg, fpReg1 = valueTr.resultFpReg)
}
} else {
addToResult(result, valueTr, valueTr.resultReg, -1) addToResult(result, valueTr, valueTr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREI, dt, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg) it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
}
} }
} }
} }
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
private fun funcPeek(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult { private fun funcPoke(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
return if(dt==IRDataType.FLOAT) { if(codeGen.isZero(call.args[1])) {
if(call.args[0] is PtNumber) {
val resultFpRegister = codeGen.registers.nextFreeFloat()
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address)
}
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpRegister)
} else {
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultFpReg = codeGen.registers.nextFreeFloat()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg)
}
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
} else {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val resultRegister = codeGen.registers.nextFree()
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address)
}
ExpressionCodeResult(result, dt, resultRegister, -1)
} else {
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg)
}
ExpressionCodeResult(result, dt, resultReg, -1)
}
}
}
private fun funcPokemon(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val address = call.args[0]
fun pokeM(result: MutableList<IRCodeChunkBase>, address: Int, value: PtExpression) {
if(codeGen.isZero(value)) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address) it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address)
} }
} else { } else {
val tr = exprGen.translateExpression(value) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = tr.resultReg)
}
}
} else {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
val tr = exprGen.translateExpression(call.args[1])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address) it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address)
} }
}
}
fun pokeI(result: MutableList<IRCodeChunkBase>, register: Int, value: PtExpression) {
if(codeGen.isZero(value)) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = register)
}
} else { } else {
val valueTr = exprGen.translateExpression(value) val addressTr = exprGen.translateExpression(call.args[0])
addToResult(result, addressTr, addressTr.resultReg, -1)
val valueTr = exprGen.translateExpression(call.args[1])
addToResult(result, valueTr, valueTr.resultReg, -1) addToResult(result, valueTr, valueTr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = register) it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
} }
} }
} }
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcPeekW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
return if(call.args[0] is PtNumber) {
val resultRegister = codeGen.registers.nextFree()
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, address = address)
}
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg)
}
ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
}
return if(address is PtNumber) { private fun funcPeek(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
return if(call.args[0] is PtNumber) {
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.nextFree()
val addressNum = address.number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = addressNum) it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = address)
} }
pokeM(result, addressNum, call.args[1])
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
val addressTr = exprGen.translateExpression(address) val tr = exprGen.translateExpression(call.args.single())
addToResult(result, addressTr, addressTr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = addressTr.resultReg) it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
} }
pokeI(result, addressTr.resultReg, call.args[1])
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
} }
private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val name = (call.args[0] as PtString).value val name = (call.args[0] as PtString).value
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
@@ -674,7 +423,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
return exprGen.translateExpression(call.args.single()) return exprGen.translateExpression(call.args.single())
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
// ....To be more strict, maybe we should introduce a new result register that is of type .b?
} }
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@@ -682,176 +430,25 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
}
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
val vmDt = irType(call.args[0].type)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val arg = call.args[0] val tr = exprGen.translateExpression(call.args[0])
val vmDt = irType(arg.type)
val opcodeMemAndReg = when(call.name) {
"rol" -> Opcode.ROXLM to Opcode.ROXL
"ror" -> Opcode.ROXRM to Opcode.ROXR
"rol2" -> Opcode.ROLM to Opcode.ROL
"ror2" -> Opcode.RORM to Opcode.ROR
else -> throw AssemblyError("wrong func")
}
val ident = arg as? PtIdentifier
if(ident!=null) {
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = ident.name), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
val memAddr = (arg as? PtMemoryByte)?.address?.asConstInteger()
if(memAddr!=null) {
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, address = memAddr), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
val arr = (arg as? PtArrayIndexer)
val index = arr?.index?.asConstInteger()
if(arr!=null && index!=null) {
val variable = arr.variable.name
val itemsize = codeGen.program.memsizer.memorySize(arr.type)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
val opcode = opcodeMemAndReg.second
val saveCarry = opcode in OpcodesThatDependOnCarry && !arg.isSimple()
if(saveCarry)
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
val tr = exprGen.translateExpression(arg)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
if(saveCarry) result += IRCodeChunk(null, null).also {
addInstr(result, IRInstruction(Opcode.POPST), null) it += IRInstruction(opcode, vmDt, reg1 = tr.resultReg)
addInstr(result, IRInstruction(opcode, vmDt, reg1 = tr.resultReg), null) }
if(saveCarry) result += assignRegisterTo(call.args[0], tr.resultReg)
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
result += assignRegisterTo(arg, tr.resultReg)
if(saveCarry)
addInstr(result, IRInstruction(Opcode.POPST), null)
return ExpressionCodeResult(result, vmDt, -1, -1) return ExpressionCodeResult(result, vmDt, -1, -1)
} }
private fun funcSetLsbMsb(call: PtBuiltinFunctionCall, msb: Boolean): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val target = call.args[0]
val isConstZeroValue = call.args[1].asConstInteger()==0
when(target) {
is PtIdentifier -> {
if(isConstZeroValue) {
result += IRCodeChunk(null, null).also {
val pointerReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
if (msb)
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = pointerReg)
}
} else {
val valueTr = exprGen.translateExpression(call.args[1])
addToResult(result, valueTr, valueTr.resultReg, -1)
result += IRCodeChunk(null, null).also {
val pointerReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
if (msb)
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = pointerReg)
}
}
}
is PtArrayIndexer -> {
if(target.splitWords) {
// lsb/msb in split arrays, element index 'size' is always 1
val constIndex = target.index.asConstInteger()
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
if(isConstZeroValue) {
if(constIndex!=null) {
val offsetReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName)
}
} else {
val indexTr = exprGen.translateExpression(target.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = varName)
}
}
} else {
val valueTr = exprGen.translateExpression(call.args[1])
addToResult(result, valueTr, valueTr.resultReg, -1)
if(constIndex!=null) {
val offsetReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName)
}
} else {
val indexTr = exprGen.translateExpression(target.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = varName)
}
}
}
}
else {
val eltSize = codeGen.program.memsizer.memorySize(target.type)
val constIndex = target.index.asConstInteger()
if(isConstZeroValue) {
if(constIndex!=null) {
val offsetReg = codeGen.registers.nextFree()
val offset = eltSize*constIndex + if(msb) 1 else 0
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
}
} else {
val indexTr = exprGen.translateExpression(target.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
result += IRCodeChunk(null, null).also {
if(eltSize>1)
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
if(msb)
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
}
}
} else {
val valueTr = exprGen.translateExpression(call.args[1])
addToResult(result, valueTr, valueTr.resultReg, -1)
if(constIndex!=null) {
val offsetReg = codeGen.registers.nextFree()
val offset = eltSize*constIndex + if(msb) 1 else 0
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
}
} else {
val indexTr = exprGen.translateExpression(target.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
result += IRCodeChunk(null, null).also {
if(eltSize>1)
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
if(msb)
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
}
}
}
}
}
else -> throw AssemblyError("weird target for setlsb/setmsb: $target")
}
return ExpressionCodeResult(result, IRDataType.WORD, -1, -1)
}
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks { private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val assignment = PtAssignment(target.position) val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position) val assignTarget = PtAssignTarget(target.position)

File diff suppressed because it is too large Load Diff

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
@@ -45,41 +44,22 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
val indexedInstructions = chunk1.instructions.withIndex() val indexedInstructions = chunk1.instructions.withIndex()
.map { IndexedValue(it.index, it.value) } .map { IndexedValue(it.index, it.value) }
val changed = removeNops(chunk1, indexedInstructions) val changed = removeNops(chunk1, indexedInstructions)
|| replaceConcatZeroMsbWithExt(chunk1, indexedInstructions) || removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions)
|| 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
} }
private fun replaceConcatZeroMsbWithExt(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.CONCAT) {
// if the previous instruction loads a zero in the msb, this can be turned into EXT.B instead
val prev = indexedInstructions[idx-1].value
if(prev.opcode==Opcode.LOAD && prev.immediate==0 && prev.reg1==ins.reg2) {
chunk.instructions[idx] = IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = ins.reg1, reg2 = ins.reg3)
chunk.instructions.removeAt(idx-1)
changed = true
}
}
}
return changed
}
private fun removeEmptyChunks(sub: IRSubroutine) { private fun removeEmptyChunks(sub: IRSubroutine) {
if(sub.chunks.isEmpty()) if(sub.chunks.isEmpty())
return return
@@ -87,7 +67,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
/* /*
Empty Code chunk with label -> Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1. If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: merge both labels into 1)
If is last chunk -> keep chunk in place because of the label. If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label -> Empty Code chunk without label ->
should not have been generated! ERROR. should not have been generated! ERROR.
@@ -96,7 +76,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
val relabelChunks = mutableListOf<Pair<Int, String>>() val relabelChunks = mutableListOf<Pair<Int, String>>()
val removeChunks = mutableListOf<Int>() val removeChunks = mutableListOf<Int>()
val replaceLabels = mutableMapOf<String, String>()
sub.chunks.withIndex().forEach { (index, chunk) -> sub.chunks.withIndex().forEach { (index, chunk) ->
if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) { if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) {
@@ -110,18 +89,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
relabelChunks += Pair(index + 1, chunk.label!!) relabelChunks += Pair(index + 1, chunk.label!!)
removeChunks += index removeChunks += index
} else { } else {
// merge both labels into 1 except if this is the label chunk at the start of the subroutine
if(index>0) {
if (chunk.label == nextchunk.label) if (chunk.label == nextchunk.label)
removeChunks += index removeChunks += index
else { else {
removeChunks += index // TODO: merge labels on same chunk
replaceLabels[chunk.label!!] = nextchunk.label!!
replaceLabels.entries.forEach { (key, value) ->
if (value == chunk.label)
replaceLabels[key] = nextchunk.label!!
}
}
} }
} }
} }
@@ -131,32 +102,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
relabelChunks.forEach { (index, label) -> relabelChunks.forEach { (index, label) ->
val chunk = IRCodeChunk(label, null) val chunk = IRCodeChunk(label, null)
val subChunk = sub.chunks[index] chunk.instructions += sub.chunks[index].instructions
chunk.instructions += subChunk.instructions
if(subChunk is IRCodeChunk)
chunk.appendSrcPositions(subChunk.sourceLinesPositions)
sub.chunks[index] = chunk sub.chunks[index] = chunk
} }
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) } removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
sub.chunks.forEach { chunk ->
chunk.instructions.withIndex().forEach { (idx, instr) ->
instr.labelSymbol?.let {
if(instr.opcode in OpcodesThatBranch) {
replaceLabels.forEach { (from, to) ->
if (it == from) {
chunk.instructions[idx] = instr.copy(labelSymbol = to)
}
else {
val actualPrefix = "$from."
if (it.startsWith(actualPrefix))
chunk.instructions[idx] = instr.copy(labelSymbol = "$to.")
}
}
}
}
}
}
} }
private fun joinChunks(sub: IRSubroutine) { private fun joinChunks(sub: IRSubroutine) {
@@ -188,8 +137,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(mayJoinCodeChunks(lastChunk, candidate)) { if(mayJoinCodeChunks(lastChunk, candidate)) {
lastChunk.instructions += candidate.instructions lastChunk.instructions += candidate.instructions
lastChunk.next = candidate.next lastChunk.next = candidate.next
if(lastChunk is IRCodeChunk)
lastChunk.appendSrcPositions(candidate.sourceLinesPositions)
} }
else else
chunks += candidate chunks += candidate
@@ -317,53 +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.reg1==ins.reg1) {
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 OpcodesThatDependOnCarry) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
// a SNZ etc. whose target register is not used can be removed altogether
if(ins.opcode in OpcodesThatSetRegFromStatusbits) {
val usages = regUsages(ins.reg1!!)
if(usages.toList().sumOf { it.second } <= 1) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
return changed
}
private fun regUsages(register: Int): Map<IRCodeChunkBase, Int> {
val chunks = mutableMapOf<IRCodeChunkBase, Int>()
irprog.foreachSub { sub ->
sub.chunks.forEach { chunk ->
val used = chunk.usedRegisters()
val numUsages = used.readRegs.getOrDefault(register, 0) + used.writeRegs.getOrDefault(register, 0)
if(numUsages>0) {
chunks[chunk] = numUsages
}
}
}
return chunks
}
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
@@ -433,21 +333,18 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean { private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
return false
/*
var changed = false var changed = false
indexedInstructions.forEach { (idx, ins) -> indexedInstructions.forEach { (idx, ins) ->
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory) // TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory) // TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
// TODO: detect multiple float ffrom/fto to the same target, only keep first // TODO: detect multiple float ffrom/fto to the same target, only keep first
// TODO: detect multiple sequential rnd with same reg1, only keep one
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out // TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
// TODO: detect multiple same ands, ors; only keep first // TODO: detect multiple same ands, ors; only keep first
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them // TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them
// ... // ...
} }
return changed return changed
*/
} }
} }

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

@@ -1,6 +1,7 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import prog8.intermediate.* import prog8.intermediate.*
@@ -15,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++
} }
} }
@@ -23,45 +24,20 @@ 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 or variable initializer value
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 // symbol occurs in an instruction
}
}
}
irprog.st.allVariables().forEach { variable ->
val initValue = variable.onetimeInitializationArrayValue
if(!initValue.isNullOrEmpty()) {
if(initValue.any {
it.addressOfSymbol?.startsWith(blockLabel)==true
})
return // symbol occurs in an initializer value (address-of this symbol)_
}
}
}
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 ->
sub.chunks.forEach { chunk ->
chunk.label?.let { allLabeledChunks[it] = chunk } chunk.label?.let { allLabeledChunks[it] = chunk }
} }
}
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks) var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
irprog.blocks.forEach { block -> irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub -> block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) { if(sub.isEmpty()) {
if(!block.options.ignoreUnused) { if(!sub.position.file.startsWith(libraryFilePrefix)) {
errors.info("unused subroutine '${sub.label}'", sub.position) errors.warn("unused subroutine ${sub.label}", sub.position)
} }
block.children.remove(sub) block.children.remove(sub)
irprog.st.removeTree(sub.label) irprog.st.removeTree(sub.label)
@@ -81,8 +57,8 @@ class IRUnusedCodeRemover(
irprog.blocks.forEach { block -> irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub -> block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) { if(sub.isEmpty()) {
if(!block.options.ignoreUnused) { if(!sub.position.file.startsWith(libraryFilePrefix)) {
errors.info("unused subroutine '${sub.label}'", sub.position) errors.warn("unused asmsubroutine ${sub.label}", sub.position)
} }
block.children.remove(sub) block.children.remove(sub)
irprog.st.removeTree(sub.label) irprog.st.removeTree(sub.label)
@@ -102,7 +78,7 @@ class IRUnusedCodeRemover(
// check if asmsub is called from another asmsub // check if asmsub is called from another asmsub
irprog.blocks.asSequence().forEach { block -> irprog.blocks.asSequence().forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub -> block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
if (block.options.forceOutput || block.library) if (block.forceOutput || block.library)
linkedAsmSubs += sub linkedAsmSubs += sub
if (sub.asmChunk.isNotEmpty()) { if (sub.asmChunk.isNotEmpty()) {
allSubs.forEach { (label, asmsub) -> allSubs.forEach { (label, asmsub) ->
@@ -121,12 +97,14 @@ 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 ->
sub.chunks.forEach { chunk ->
chunk.instructions.forEach { chunk.instructions.forEach {
it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } } 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 // note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
} }
} }
}
return removeUnlinkedAsmsubs(linkedAsmSubs) return removeUnlinkedAsmsubs(linkedAsmSubs)
} }
@@ -145,22 +123,8 @@ 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())
reachable.add(irprog.globalInits)
// 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>()
@@ -168,13 +132,7 @@ class IRUnusedCodeRemover(
it.next?.let { next -> new += next } it.next?.let { next -> new += next }
it.instructions.forEach { instr -> it.instructions.forEach { instr ->
if (instr.branchTarget == null) if (instr.branchTarget == null)
instr.labelSymbol?.let { label -> instr.labelSymbol?.let { label -> allLabeledChunks[label]?.let { chunk -> new += chunk } }
val chunk = allLabeledChunks[label] ?: allLabeledChunks[label.substringBeforeLast('.')]
if(chunk!=null)
new+=chunk
else
allLabeledChunks[label]?.let { new += it }
}
else else
new += instr.branchTarget!! new += instr.branchTarget!!
} }
@@ -196,19 +154,8 @@ 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 }
.forEach {
it.onetimeInitializationArrayValue?.let { array ->
array.forEach {elt ->
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
linkedChunks += irprog.getChunkWithLabel(elt.addressOfSymbol!!)
}
}
}
irprog.foreachCodeChunk { chunk ->
chunk.next?.let { next -> linkedChunks += next } chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach { chunk.instructions.forEach {
if(it.branchTarget==null) { if(it.branchTarget==null) {
@@ -220,24 +167,16 @@ class IRUnusedCodeRemover(
if (chunk.label == "main.start") if (chunk.label == "main.start")
linkedChunks += chunk linkedChunks += chunk
} }
// make sure that chunks that are only used as a prefix of a label, are also marked as linked
linkedChunks.toList().forEach { chunk ->
chunk.instructions.forEach {
if(it.labelSymbol!=null) {
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
}
}
} }
linkedChunks.add(irprog.globalInits)
return removeUnlinkedChunks(linkedChunks) return removeUnlinkedChunks(linkedChunks)
} }
private fun removeUnlinkedChunks(linkedChunks: Set<IRCodeChunkBase>): Int { private fun removeUnlinkedChunks(
linkedChunks: Set<IRCodeChunkBase>
): 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

@@ -1,47 +0,0 @@
package prog8.codegen.intermediate
import prog8.code.ast.*
internal fun makeAllNodenamesScoped(program: PtProgram) {
val renames = mutableListOf<Pair<PtNamedNode, String>>()
fun recurse(node: PtNode) {
node.children.forEach {
if(it is PtNamedNode)
renames.add(it to it.scopedName)
recurse(it)
}
}
recurse(program)
renames.forEach { it.first.name = it.second }
}
internal fun moveAllNestedSubroutinesToBlockScope(program: PtProgram) {
val movedSubs = mutableListOf<Pair<PtBlock, PtSub>>()
val removedSubs = mutableListOf<Pair<PtSub, PtSub>>()
fun moveToBlock(block: PtBlock, parent: PtSub, asmsub: PtAsmSub) {
block.add(asmsub)
parent.children.remove(asmsub)
}
fun moveToBlock(block: PtBlock, parent: PtSub, sub: PtSub) {
sub.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, sub, subsub) }
sub.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub -> moveToBlock(block, sub, asmsubsub) }
movedSubs += Pair(block, sub)
removedSubs += Pair(parent, sub)
}
program.allBlocks().forEach { block ->
block.children.toList().forEach {
if (it is PtSub) {
// Only regular subroutines can have nested subroutines.
it.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, it, subsub) }
it.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub -> moveToBlock(block, it, asmsubsub) }
}
}
}
removedSubs.forEach { (parent, sub) -> parent.children.remove(sub) }
movedSubs.forEach { (block, sub) -> block.add(sub) }
}

View File

@@ -16,7 +16,7 @@ class VmCodeGen: ICodeGeneratorBackend {
symbolTable: SymbolTable, symbolTable: SymbolTable,
options: CompilationOptions, options: CompilationOptions,
errors: IErrorReporter errors: IErrorReporter
): IAssemblyProgram { ): IAssemblyProgram? {
val irCodeGen = IRCodeGen(program, symbolTable, options, errors) val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate() val irProgram = irCodeGen.generate()
return VmAssemblyProgram(irProgram.name, irProgram) return VmAssemblyProgram(irProgram.name, irProgram)

View File

@@ -3,7 +3,7 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) { override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypesWithBoolean -> 1 in ByteDatatypes -> 1
DataType.FLOAT -> 5 DataType.FLOAT -> 5
else -> 2 else -> 2
} }
@@ -16,8 +16,6 @@ internal object DummyMemsizer : IMemSizer {
} }
internal object DummyStringEncoder : IStringEncoding { internal object DummyStringEncoder : IStringEncoding {
override val defaultEncoding: Encoding = Encoding.ISO
override fun encodeString(str: String, encoding: Encoding): List<UByte> { override fun encodeString(str: String, encoding: Encoding): List<UByte> {
return emptyList() return emptyList()
} }
@@ -27,11 +25,11 @@ internal object DummyStringEncoder : IStringEncoding {
} }
} }
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter { internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
IErrorReporter {
val errors = mutableListOf<String>() val errors = mutableListOf<String>()
val warnings = mutableListOf<String>() val warnings = mutableListOf<String>()
val infos = mutableListOf<String>()
override fun err(msg: String, position: Position) { override fun err(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg" val text = "${position.toClickableStr()} $msg"
@@ -45,25 +43,13 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text) warnings.add(text)
} }
override fun info(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg"
if(text !in infos)
infos.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 noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun report() { override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") } warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") } errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
if(throwExceptionAtReportIfErrors) if(throwExceptionAtReportIfErrors)
finalizeNumErrors(errors.size, warnings.size, infos.size) finalizeNumErrors(errors.size, warnings.size)
if(!keepMessagesAfterReporting) { if(!keepMessagesAfterReporting) {
clear() clear()
} }
@@ -72,6 +58,5 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
fun clear() { fun clear() {
errors.clear() errors.clear()
warnings.clear() warnings.clear()
infos.clear()
} }
} }

View File

@@ -8,7 +8,7 @@ import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({ class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram { fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start") require(chunks.first().label=="main.start")
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY) val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY) val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
chunks.forEach { sub += it } chunks.forEach { sub += it }
block += sub block += sub
@@ -18,13 +18,12 @@ class TestIRPeepholeOpt: FunSpec({
CbmPrgLauncherType.NONE, CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE, ZeropageType.DONTUSE,
emptyList(), emptyList(),
CompilationOptions.AllZeropageAllowed,
floats = false, floats = false,
noSysInit = true, noSysInit = true,
compTarget = target, compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
) )
val prog = IRProgram("test", IRSymbolTable(), options, target) val prog = IRProgram("test", IRSymbolTable(null), options, target)
prog.addBlock(block) prog.addBlock(block)
prog.linkChunks() prog.linkChunks()
prog.validate() prog.validate()
@@ -37,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(
@@ -53,9 +52,9 @@ class TestIRPeepholeOpt: FunSpec({
test("remove jmp to label below") { test("remove jmp to label below") {
val c1 = IRCodeChunk("main.start", null) val c1 = IRCodeChunk("main.start", null)
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed, but chunk stays because of label
val c2 = IRCodeChunk("label", null) val c2 = IRCodeChunk("label", null)
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed, but chunk stays because of label
c2 += IRInstruction(Opcode.NOP) // removed c2 += IRInstruction(Opcode.NOP) // removed
val c3 = IRCodeChunk("label2", null) val c3 = IRCodeChunk("label2", null)
c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3") c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3")
@@ -67,13 +66,15 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().flatMap { it.instructions }.size shouldBe 5 irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize(true, ErrorReporterForTests()) opt.optimize(true, ErrorReporterForTests())
irProg.chunks().size shouldBe 3 irProg.chunks().size shouldBe 4
irProg.chunks()[0].label shouldBe "main.start" irProg.chunks()[0].label shouldBe "main.start"
irProg.chunks()[1].label shouldBe "label2" irProg.chunks()[1].label shouldBe "label"
irProg.chunks()[2].label shouldBe "label3" irProg.chunks()[2].label shouldBe "label2"
irProg.chunks()[3].label shouldBe "label3"
irProg.chunks()[0].isEmpty() shouldBe true irProg.chunks()[0].isEmpty() shouldBe true
irProg.chunks()[1].isEmpty() shouldBe false irProg.chunks()[1].isEmpty() shouldBe true
irProg.chunks()[2].isEmpty() shouldBe true irProg.chunks()[2].isEmpty() shouldBe false
irProg.chunks()[3].isEmpty() shouldBe true
val instr = irProg.chunks().flatMap { it.instructions } val instr = irProg.chunks().flatMap { it.instructions }
instr.size shouldBe 2 instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.JUMP instr[0].opcode shouldBe Opcode.JUMP

View File

@@ -19,7 +19,6 @@ class TestVmCodeGen: FunSpec({
CbmPrgLauncherType.NONE, CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE, ZeropageType.DONTUSE,
zpReserved = emptyList(), zpReserved = emptyList(),
zpAllowed = CompilationOptions.AllZeropageAllowed,
floats = true, floats = true,
noSysInit = false, noSysInit = false,
compTarget = target, compTarget = target,
@@ -42,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", false, SourceCode.Generated("test"), PtBlock.Options(), 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))
@@ -92,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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
@@ -121,11 +120,11 @@ 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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@@ -133,7 +132,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@@ -141,7 +140,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@@ -149,7 +148,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@@ -184,11 +183,11 @@ 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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@@ -196,7 +195,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@@ -204,7 +203,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@@ -212,7 +211,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@@ -243,23 +242,23 @@ 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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if1.add(PtNodeGroup()) if1.add(PtNodeGroup())
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY) val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if2.add(PtNodeGroup()) if2.add(PtNodeGroup())
sub.add(if2) sub.add(if2)
block.add(sub) block.add(sub)
@@ -290,11 +289,11 @@ 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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@@ -302,7 +301,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@@ -310,7 +309,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@@ -318,7 +317,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@@ -353,11 +352,11 @@ 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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@@ -365,7 +364,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@@ -373,7 +372,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@@ -381,7 +380,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@@ -412,23 +411,23 @@ 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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if1.add(PtNodeGroup()) if1.add(PtNodeGroup())
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY) val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if2.add(PtNodeGroup()) if2.add(PtNodeGroup())
sub.add(if2) sub.add(if2)
block.add(sub) block.add(sub)
@@ -452,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", false, SourceCode.Generated("test"), PtBlock.Options(), 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)
@@ -467,11 +466,7 @@ class TestVmCodeGen: FunSpec({
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
irChunks.size shouldBe 1 irChunks.size shouldBe 1
irChunks[0].instructions.size shouldBe 2 val callInstr = irChunks.single().instructions.single()
val preparecallInstr = irChunks[0].instructions[0]
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
preparecallInstr.immediate shouldBe 0
val callInstr = irChunks[0].instructions[1]
callInstr.opcode shouldBe Opcode.CALL callInstr.opcode shouldBe Opcode.CALL
callInstr.address shouldBe 0x5000 callInstr.address shouldBe 0x5000
} }

View File

@@ -27,17 +27,16 @@ dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':compilerAst') implementation project(':compilerAst')
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.20"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
} }
sourceSets { sourceSets {
main { main {
java { java {
srcDir "${project.projectDir}/src" srcDirs = ["${project.projectDir}/src"]
} }
resources { resources {
srcDir "${project.projectDir}/res" srcDirs = ["${project.projectDir}/res"]
} }
} }
} }

View File

@@ -11,6 +11,5 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="compilerAst" /> <orderEntry type="module" module-name="compilerAst" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component> </component>
</module> </module>

View File

@@ -0,0 +1,71 @@
package prog8.optimizer
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.BinaryExpression
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AugmentAssignmentOperators
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.target.VMTarget
class BinExprSplitter(private val program: Program, private val options: CompilationOptions) : AstWalker() {
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(options.compTarget.name == VMTarget.NAME)
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
return noModifications
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {
if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
if(assignment.target isSameAs binExpr.right)
return noModifications
if(assignment.target isSameAs binExpr.left) {
if(binExpr.right.isSimple)
return noModifications
val leftBx = binExpr.left as? BinaryExpression
if(leftBx!=null && (!leftBx.left.isSimple || !leftBx.right.isSimple))
return noModifications
val rightBx = binExpr.right as? BinaryExpression
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
return noModifications
}
if(binExpr.right.isSimple) {
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, AssignmentOrigin.OPTIMIZER, binExpr.left.position)
val targetExpr = assignment.target.toExpression()
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
return listOf(
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer)
)
}
}
// Further unraveling of binary expressions is really complicated here and
// often results in much bigger code, thereby defeating the purpose a bit.
// All in all this should probably be fixed in a better code generation backend
// that doesn't require this at all.
}
return noModifications
}
private fun isSimpleTarget(target: AssignTarget) =
if (target.identifier!=null || target.memoryAddress!=null)
!target.isIOAddress(options.compTarget.machine)
else
false
}

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