mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ee162d98b | ||
|
|
380f557c45 | ||
|
|
1bdae53f4e | ||
|
|
9314c346da | ||
|
|
bfaad1388c | ||
|
|
0b580ad05d | ||
|
|
bb35a80177 | ||
|
|
24fc95ac81 | ||
|
|
8f864417c4 | ||
|
|
bb9d29b061 | ||
|
|
b9d8ec1463 | ||
|
|
1842a7660d | ||
|
|
5caa2f5536 | ||
|
|
d6078be8b7 | ||
|
|
cf60723f14 | ||
|
|
f7ff0a2b1d | ||
|
|
cc49664b2f | ||
|
|
99fe74f026 | ||
|
|
b021869eeb | ||
|
|
b8806d163b | ||
|
|
1116aae1de | ||
|
|
5e5f60253b | ||
|
|
bbc02752c9 | ||
|
|
9896bc110e | ||
|
|
ca60f8ecdd | ||
|
|
544acd1e35 | ||
|
|
6e07602d77 | ||
|
|
82898f7bba | ||
|
|
d61283a8bc | ||
|
|
1ee3f826cc | ||
|
|
4a00a5ba9e | ||
|
|
39eda67867 | ||
|
|
a99d38fdaa | ||
|
|
0eb2d437e2 | ||
|
|
3ac9036c79 | ||
|
|
c94e292176 | ||
|
|
91d87c2d9b | ||
|
|
ff472f69c0 | ||
|
|
e18119e24c | ||
|
|
4a592dc64c | ||
|
|
d9e13201dd | ||
|
|
5c75b19c5d | ||
|
|
52a77db60f | ||
|
|
0513c250fb | ||
|
|
48864ad6cf | ||
|
|
cdbccad21e | ||
|
|
e15bc68c9b | ||
|
|
8bffd7672d | ||
|
|
61df5b3060 | ||
|
|
b5255444cd | ||
|
|
0c94e377fc | ||
|
|
8e5c67b4b2 | ||
|
|
b24f2f1756 | ||
|
|
c69c17de42 | ||
|
|
061617122a | ||
|
|
125ce3240f | ||
|
|
7215efe167 | ||
|
|
06d1570142 | ||
|
|
093c370faa | ||
|
|
aec9574737 | ||
|
|
7ceb76cff5 | ||
|
|
300e2fe9f8 | ||
|
|
91e1643627 | ||
|
|
91421b0c62 | ||
|
|
40f611664f | ||
|
|
dcba4f4098 | ||
|
|
c098ad2b3b | ||
|
|
b43223cb7a | ||
|
|
e243531dab | ||
|
|
1af38e62bc | ||
|
|
f37f062cdc | ||
|
|
7e734214dc | ||
|
|
05d152746f | ||
|
|
dea7f37553 | ||
|
|
415c599310 | ||
|
|
70cd4fedbe | ||
|
|
1e6d7673bc | ||
|
|
b4963b725b | ||
|
|
0371ffa4ce | ||
|
|
6a664a7e15 | ||
|
|
88ce9300bc | ||
|
|
85cf0e311c | ||
|
|
0e3d75cfeb | ||
|
|
630c8a5faa | ||
|
|
905921a684 | ||
|
|
1e469b3b0f | ||
|
|
bff3c4f95c | ||
|
|
bd2bcb6994 | ||
|
|
4c8898a639 | ||
|
|
97df33ab1a | ||
|
|
ef46fb2685 | ||
|
|
d5d6dd3614 | ||
|
|
6c233c6a0a | ||
|
|
6db715d879 | ||
|
|
ab02e8a546 | ||
|
|
8cbfe64f19 | ||
|
|
fd1e9971e4 | ||
|
|
68336a76c5 | ||
|
|
393e914a86 | ||
|
|
ffb54110e9 | ||
|
|
533d825f1a | ||
|
|
c65279b672 | ||
|
|
f9926beeef | ||
|
|
add8a777d8 | ||
|
|
21bc505d85 | ||
|
|
3fc49c001e | ||
|
|
3d69a95c49 | ||
|
|
d81fdf6d6b | ||
|
|
87d3109ffb | ||
|
|
180dbbb521 | ||
|
|
24aac7cee5 | ||
|
|
53e18a5387 | ||
|
|
92062d056d | ||
|
|
06368ab0a1 | ||
|
|
38efe25c68 | ||
|
|
319079de7a | ||
|
|
025bf900a5 | ||
|
|
2885f4f7b1 | ||
|
|
c07eda15b1 | ||
|
|
4274296cf3 | ||
|
|
76a203d4df | ||
|
|
24f37e2062 | ||
|
|
f465b2e2a0 |
No files matched your search
@@ -12,8 +12,13 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install 64tass
|
||||
run: sudo apt-get update -y && sudo apt-get install -y 64tass
|
||||
- name: build and install recent 64tass
|
||||
run: |
|
||||
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
|
||||
uses: actions/setup-java@v2
|
||||
|
||||
Generated
+1
-1
@@ -4,6 +4,6 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.20-release-327" />
|
||||
<option name="version" value="1.8.21-release-380" />
|
||||
</component>
|
||||
</project>
|
||||
+13
-13
@@ -1,21 +1,21 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.5.5" />
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.6.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.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/1.8.10/kotlin-stdlib-1.8.10.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$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" />
|
||||
<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/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.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$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
+14
-14
@@ -1,22 +1,22 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.property.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:5.5.5" />
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:5.6.2" />
|
||||
<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$/io/kotest/kotest-property-jvm/5.6.2/kotest-property-jvm-5.6.2.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$/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/1.8.10/kotlin-stdlib-1.8.10.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$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
+27
-40
@@ -1,55 +1,42 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.5.5" />
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.6.2" />
|
||||
<CLASSES>
|
||||
<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.5.5/kotest-framework-api-jvm-5.5.5.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/kotest/kotest-runner-junit5-jvm/5.6.2/kotest-runner-junit5-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.6.2/kotest-framework-api-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-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.154/classgraph-4.8.154.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$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.6.2/kotest-framework-engine-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.157/classgraph-4.8.157.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$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.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$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.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/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$/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.5.5/kotest-assertions-core-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$/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.5.5/kotest-extensions-jvm-5.5.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-jvm/1.13.1/mockk-jvm-1.13.1.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$/io/mockk/mockk-agent-jvm/1.13.1/mockk-agent-jvm-1.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.2/objenesis-3.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$/io/mockk/mockk-core-jvm/1.13.1/mockk-core-jvm-1.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.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$/io/kotest/kotest-framework-discovery-jvm/5.6.2/kotest-framework-discovery-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.6.2/kotest-extensions-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.6.2/kotest-framework-concurrency-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||
<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/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.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/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.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.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/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.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-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.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-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
+6
-6
@@ -1,13 +1,13 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17" />
|
||||
<CLASSES>
|
||||
<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-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$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.17/kotlin-result-jvm-1.1.17.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/1.8.10/kotlin-stdlib-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-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
@@ -26,7 +26,7 @@ compileTestKotlin {
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -235,7 +235,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
|
||||
|
||||
|
||||
class StRomSub(name: String,
|
||||
val address: UInt,
|
||||
val address: UInt?, // null in case of asmsub, specified in case of romsub
|
||||
val parameters: List<StRomSubParameter>,
|
||||
val returns: List<StRomSubParameter>,
|
||||
astNode: PtNode) :
|
||||
|
||||
@@ -40,14 +40,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
|
||||
val stNode = when(node) {
|
||||
is PtAsmSub -> {
|
||||
if(node.address==null) {
|
||||
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
|
||||
StSub(node.name, params, node.returns.singleOrNull()?.second, node)
|
||||
} else {
|
||||
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
||||
StRomSub(node.name, node.address, parameters, returns, node)
|
||||
}
|
||||
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
||||
StRomSub(node.name, node.address, parameters, returns, node)
|
||||
}
|
||||
is PtBlock -> {
|
||||
StNode(node.name, StNodeType.BLOCK, node)
|
||||
|
||||
@@ -63,7 +63,7 @@ class PtProgram(
|
||||
children.asSequence().filterIsInstance<PtBlock>()
|
||||
|
||||
fun entrypoint(): PtSub? =
|
||||
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
|
||||
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub?
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.NumericDatatypes
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.*
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.round
|
||||
@@ -28,7 +25,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
infix fun isSameAs(other: PtExpression): Boolean {
|
||||
return when(this) {
|
||||
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
|
||||
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||
@@ -51,7 +48,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
this.name == target.identifier!!.name
|
||||
}
|
||||
target.array != null && this is PtArrayIndexer -> {
|
||||
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index
|
||||
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
@@ -118,6 +115,9 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
||||
val index: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
val splitWords: Boolean
|
||||
get() = variable.type in SplitWordArrayTypes
|
||||
|
||||
init {
|
||||
require(elementType in NumericDatatypes)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||
is PtAddressOf -> "&"
|
||||
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
|
||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}"
|
||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
|
||||
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||
is PtBuiltinFunctionCall -> {
|
||||
val str = if(node.void) "void " else ""
|
||||
@@ -96,13 +96,14 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
str
|
||||
}
|
||||
is PtVariable -> {
|
||||
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
|
||||
val str = if(node.arraySize!=null) {
|
||||
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||
"${eltType.name.lowercase()}[${node.arraySize}] ${node.name}"
|
||||
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
|
||||
}
|
||||
else if(node.type in ArrayDatatypes) {
|
||||
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||
"${eltType.name.lowercase()}[] ${node.name}"
|
||||
"${eltType.name.lowercase()}[] $split ${node.name}"
|
||||
}
|
||||
else
|
||||
"${node.type.name.lowercase()} ${node.name}"
|
||||
|
||||
@@ -68,7 +68,6 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
// this set of function have no return value and operate in-place:
|
||||
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
@@ -80,19 +79,41 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), 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),
|
||||
// normal functions follow:
|
||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), 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),
|
||||
"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" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
|
||||
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
|
||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), 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),
|
||||
"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),
|
||||
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
|
||||
@@ -21,6 +21,7 @@ class CompilationOptions(val output: OutputType,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHigh: Boolean = false,
|
||||
var useNewExprCode: Boolean = false,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var evalStackBaseAddress: UInt? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
|
||||
@@ -11,7 +11,9 @@ enum class DataType {
|
||||
ARRAY_UB, // pass by reference
|
||||
ARRAY_B, // 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_SPLIT, // pass by reference, lo/hi byte split
|
||||
ARRAY_F, // pass by reference
|
||||
ARRAY_BOOL, // pass by reference
|
||||
UNDEFINED;
|
||||
@@ -119,12 +121,14 @@ val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWO
|
||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
|
||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||
val 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 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(
|
||||
DataType.STR,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
|
||||
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
||||
)
|
||||
val PassByValueDatatypes = NumericDatatypes
|
||||
@@ -135,6 +139,8 @@ val ArrayToElementTypes = mapOf(
|
||||
DataType.ARRAY_UB to DataType.UBYTE,
|
||||
DataType.ARRAY_W to DataType.WORD,
|
||||
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_BOOL to DataType.BOOL
|
||||
)
|
||||
|
||||
@@ -28,9 +28,9 @@ dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -362,13 +362,15 @@ class AsmGen6502Internal (
|
||||
internal fun loadScaledArrayIndexIntoRegister(
|
||||
expr: PtArrayIndexer,
|
||||
elementDt: DataType,
|
||||
register: CpuRegister,
|
||||
addOneExtra: Boolean = false
|
||||
register: CpuRegister
|
||||
) {
|
||||
val reg = register.toString().lowercase()
|
||||
val indexnum = expr.index.asConstInteger()
|
||||
if (indexnum != null) {
|
||||
val indexValue = indexnum * options.compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0
|
||||
val indexValue = if(expr.splitWords)
|
||||
indexnum
|
||||
else
|
||||
indexnum * options.compTarget.memorySize(elementDt)
|
||||
out(" ld$reg #$indexValue")
|
||||
return
|
||||
}
|
||||
@@ -377,77 +379,42 @@ class AsmGen6502Internal (
|
||||
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}")
|
||||
|
||||
val indexName = asmVariableName(indexVar)
|
||||
if (addOneExtra) {
|
||||
// add 1 to the result
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
out(" ldy $indexName | iny")
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" tya")
|
||||
CpuRegister.X -> out(" tyx")
|
||||
CpuRegister.Y -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
out(" lda $indexName | sec | rol a")
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
|
||||
out(
|
||||
"""
|
||||
lda $indexName
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
adc $indexName"""
|
||||
)
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
|
||||
if(expr.splitWords) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" lda $indexName")
|
||||
CpuRegister.X -> out(" ldx $indexName")
|
||||
CpuRegister.Y -> out(" ldy $indexName")
|
||||
}
|
||||
} else {
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> out(" ld$reg $indexName")
|
||||
in WordDatatypes -> {
|
||||
out(" lda $indexName | asl a")
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> out(" ld$reg $indexName")
|
||||
in WordDatatypes -> {
|
||||
out(" lda $indexName | asl a")
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
|
||||
out(
|
||||
"""
|
||||
lda $indexName
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $indexName"""
|
||||
)
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
|
||||
out("""
|
||||
lda $indexName
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $indexName"""
|
||||
)
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1639,14 +1606,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -1680,14 +1640,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -1723,15 +1676,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_less_uw | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -1769,15 +1714,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -1816,14 +1753,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -1859,14 +1789,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.BYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -1908,15 +1831,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return code("P8ZP_SCRATCH_W2+1", "P8ZP_SCRATCH_W2")
|
||||
}
|
||||
|
||||
@@ -1959,15 +1874,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleLeftOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, right)
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -2007,14 +1914,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -2050,14 +1950,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.BYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -2101,15 +1994,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -2156,15 +2041,7 @@ $repeatLabel lda $counterVar
|
||||
return code(asmVariableName(left))
|
||||
}
|
||||
|
||||
if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, right)
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -2200,14 +2077,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -2240,15 +2110,31 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.BYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateUwordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
@@ -2282,15 +2168,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, right)
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -2328,15 +2206,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
|
||||
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
||||
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
||||
"abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultToStack, resultRegister)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultToStack, resultRegister)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultToStack, resultRegister)
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
|
||||
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
|
||||
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
|
||||
"sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
|
||||
"divmod" -> funcDivmod(fcall)
|
||||
"divmodw" -> funcDivmodW(fcall)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope)
|
||||
"divmod__ubyte" -> funcDivmod(fcall)
|
||||
"divmod__uword" -> funcDivmodW(fcall)
|
||||
"rol" -> funcRol(fcall)
|
||||
"rol2" -> funcRol2(fcall)
|
||||
"ror" -> funcRor(fcall)
|
||||
@@ -96,8 +99,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
|
||||
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
|
||||
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
|
||||
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
|
||||
@@ -114,8 +116,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, false)
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[0], fcall.args[1], "P8ZP_SCRATCH_W2")
|
||||
asmgen.out(" jsr prog8_lib.strcmp_mem")
|
||||
if(resultToStack)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
@@ -207,27 +208,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
|
||||
} else {
|
||||
if(arg1.isSimple()) {
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if(arg1.isSimple()) {
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
}
|
||||
} else
|
||||
@@ -253,25 +240,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
else -> {
|
||||
if(arg1.isSimple()) {
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
+""")
|
||||
} else {
|
||||
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
+""")
|
||||
}
|
||||
asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
+""")
|
||||
}
|
||||
}
|
||||
} else
|
||||
@@ -298,13 +272,34 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
if(resultToStack)
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||
else {
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
when(fcall.args[0].type) {
|
||||
DataType.UBYTE -> {
|
||||
if(resultToStack)
|
||||
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_stack")
|
||||
else {
|
||||
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(resultToStack)
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||
else {
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(resultToStack)
|
||||
throw AssemblyError("no support for sqrt float onto stack")
|
||||
else {
|
||||
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
lda #$numElements
|
||||
jsr floats.func_reverse_f""")
|
||||
}
|
||||
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -403,6 +399,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
jsr prog8_lib.func_sort_ub""")
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||
in SplitWordArrayTypes -> TODO("split word sort")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
} else
|
||||
@@ -437,6 +434,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.splitWords)
|
||||
TODO("ror2 split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
|
||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||
}
|
||||
@@ -496,6 +495,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.splitWords)
|
||||
TODO("ror split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
|
||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||
}
|
||||
@@ -538,6 +539,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.splitWords)
|
||||
TODO("rol2 split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
|
||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||
}
|
||||
@@ -597,6 +600,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is PtArrayIndexer -> {
|
||||
if(what.splitWords)
|
||||
TODO("rol split words ${what.position}")
|
||||
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
|
||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||
}
|
||||
@@ -612,6 +617,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
|
||||
if(indexer.splitWords)
|
||||
TODO("rol/ror split words access ${indexer.position}")
|
||||
if(arrayvar.type==DataType.UWORD) {
|
||||
if(dt!='b')
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
@@ -659,6 +666,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
|
||||
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
} else {
|
||||
@@ -666,6 +674,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
|
||||
@@ -677,21 +686,32 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val dt = fcall.args.single().type
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" ldy #0")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
||||
DataType.UWORD -> {}
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
|
||||
else -> throw AssemblyError("weird type")
|
||||
else -> throw AssemblyError("no support for abs onto stack for this dt")
|
||||
}
|
||||
} else {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" ldy #0")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY")
|
||||
DataType.UWORD -> {}
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||
DataType.BYTE -> {
|
||||
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false)
|
||||
}
|
||||
DataType.WORD -> {
|
||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,8 +773,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
// fall through method:
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||
}
|
||||
|
||||
@@ -826,6 +845,161 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
when(fcall.type) {
|
||||
in ByteDatatypes -> {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
|
||||
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed)
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value
|
||||
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMin(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
if(fcall.type in ByteDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
if(signed) asmgen.out(" bmi +") else asmgen.out(" bcc +")
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+""")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
}
|
||||
} else if(fcall.type in WordDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
if(signed) {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2+1
|
||||
bcc ++
|
||||
bne +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bcc ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
+""")
|
||||
}
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
} else {
|
||||
throw AssemblyError("min float not supported")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMax(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
if(fcall.type in ByteDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
if(signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+""")
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
}
|
||||
} else if(fcall.type in WordDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
if(signed) {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2+1
|
||||
bcc ++
|
||||
bne +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bcc ++
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
}
|
||||
if(resultToStack) {
|
||||
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
} else {
|
||||
throw AssemblyError("max float not supported")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(resultToStack) {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
|
||||
@@ -237,6 +237,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
||||
}
|
||||
in SplitWordArrayTypes -> {
|
||||
throw AssemblyError("can't push address of split-word array ${expr.position}")
|
||||
}
|
||||
in IterableDatatypes -> {
|
||||
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
@@ -761,6 +764,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
|
||||
if(arrayExpr.splitWords)
|
||||
TODO("split words expression ${arrayExpr.position}")
|
||||
|
||||
val constIndexNum = arrayExpr.index.asConstInteger()
|
||||
if(constIndexNum!=null) {
|
||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||
|
||||
@@ -51,7 +51,35 @@ internal class ForLoopsAsmGen(private val program: PtProgram,
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// 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.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
@@ -69,7 +97,35 @@ $modifiedLabel cmp #0 ; modified
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// 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.translate(stmt.statements)
|
||||
if(stepsize>0) {
|
||||
@@ -100,8 +156,9 @@ $modifiedLabel cmp #0 ; modified
|
||||
|
||||
stepsize == 1 || stepsize == -1 -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvar(stmt, range)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
@@ -134,8 +191,9 @@ $modifiedLabel2 cmp #0 ; modified
|
||||
|
||||
// (u)words, step >= 2
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvar(stmt, range)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
@@ -182,8 +240,9 @@ $endLabel""")
|
||||
|
||||
// (u)words, step <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvar(stmt, range)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
@@ -234,6 +293,56 @@ $endLabel""")
|
||||
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) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
@@ -332,7 +441,46 @@ $loopLabel sty $indexVar
|
||||
// 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""") },
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
|
||||
numElements!!
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda ${iterableName}_lsb,y
|
||||
sta $loopvarName
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$numElements
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
@@ -590,7 +738,7 @@ $loopLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun assignLoopvar(stmt: PtForLoop, range: PtRange) =
|
||||
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =
|
||||
asmgen.assignExpressionToVariable(
|
||||
range.from,
|
||||
asmgen.asmVariableName(stmt.variable),
|
||||
|
||||
@@ -164,7 +164,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
if(requiredDt!=value.type)
|
||||
throw AssemblyError("for statusflag, byte value is required")
|
||||
if (statusflag == Statusflag.Pc) {
|
||||
// this param needs to be set last, right before the jsr
|
||||
// this boolean param needs to be set last, right before the jsr
|
||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||
when(value) {
|
||||
is PtNumber -> {
|
||||
@@ -183,12 +183,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+""")
|
||||
asmgen.out(" ror a")
|
||||
}
|
||||
}
|
||||
} else throw AssemblyError("can only use Carry as status flag parameter")
|
||||
|
||||
@@ -65,6 +65,31 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
|
||||
val elementDt = targetArrayIdx.type
|
||||
val constIndex = targetArrayIdx.index.asConstInteger()
|
||||
if(targetArrayIdx.splitWords) {
|
||||
if(constIndex!=null) {
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb+$constIndex | bne + | inc ${asmArrayvarname}_msb+$constIndex |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda ${asmArrayvarname}_lsb+$constIndex
|
||||
bne +
|
||||
dec ${asmArrayvarname}_msb+$constIndex
|
||||
+ dec ${asmArrayvarname}_lsb+$constIndex""")
|
||||
} else {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
if(incr)
|
||||
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda ${asmArrayvarname}_lsb,x
|
||||
bne +
|
||||
dec ${asmArrayvarname}_msb,x
|
||||
+ dec ${asmArrayvarname}_lsb,x""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
return
|
||||
}
|
||||
if(constIndex!=null) {
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
@@ -74,11 +99,10 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname+$indexValue
|
||||
bne +
|
||||
dec $asmArrayvarname+$indexValue+1
|
||||
+ dec $asmArrayvarname+$indexValue
|
||||
""")
|
||||
lda $asmArrayvarname+$indexValue
|
||||
bne +
|
||||
dec $asmArrayvarname+$indexValue+1
|
||||
+ dec $asmArrayvarname+$indexValue""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
|
||||
@@ -89,9 +113,8 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
else
|
||||
{
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||
asmgen.out(" tax")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
@@ -101,20 +124,20 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname,x
|
||||
bne +
|
||||
dec $asmArrayvarname+1,x
|
||||
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""")
|
||||
ldy #>$asmArrayvarname
|
||||
clc
|
||||
adc #<$asmArrayvarname
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.inc_var_f""")
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
|
||||
@@ -104,15 +104,15 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("+\t.word 0")
|
||||
asmgen.out("prog8_entrypoint\t; assembly code starts here")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||
asmgen.out(" jsr sys.init_system")
|
||||
asmgen.out(" jsr sys.init_system_phase2")
|
||||
}
|
||||
CbmPrgLauncherType.NONE -> {
|
||||
asmgen.out("; ---- program without basic sys call ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||
asmgen.out(" jsr sys.init_system")
|
||||
asmgen.out(" jsr sys.init_system_phase2")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,8 +120,8 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("; ---- atari xex program ----")
|
||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||
asmgen.out(" jsr sys.init_system")
|
||||
asmgen.out(" jsr sys.init_system_phase2")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,19 +140,19 @@ internal class ProgramAndVarsGen(
|
||||
if(options.floats)
|
||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||
asmgen.out(" jsr main.start")
|
||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
}
|
||||
"c64" -> {
|
||||
asmgen.out(" jsr main.start | lda #31 | sta $01")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
else
|
||||
asmgen.out(" rts")
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||
else
|
||||
asmgen.out(" rts")
|
||||
}
|
||||
@@ -543,6 +543,11 @@ internal class ProgramAndVarsGen(
|
||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
||||
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 -> {
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||
@@ -627,6 +632,18 @@ internal class ProgramAndVarsGen(
|
||||
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 -> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
val floatFills = array.map {
|
||||
@@ -686,7 +703,7 @@ internal class ProgramAndVarsGen(
|
||||
val number = it.number!!.toInt()
|
||||
"$"+number.toString(16).padStart(2, '0')
|
||||
}
|
||||
DataType.ARRAY_UW -> array.map {
|
||||
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
||||
if(it.number!=null) {
|
||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||
}
|
||||
@@ -718,11 +735,11 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
DataType.ARRAY_UW -> array.map {
|
||||
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
DataType.ARRAY_W -> array.map {
|
||||
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||
if(number>=0)
|
||||
|
||||
@@ -115,7 +115,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
left is PtIdentifier && left.name==scopedName
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
left is PtArrayIndexer && left isSameAs array!!
|
||||
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+52
-53
@@ -195,7 +195,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
val indexVar = target.array.index as? PtIdentifier
|
||||
when {
|
||||
indexNum!=null -> {
|
||||
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||
val targetVarName = if(target.array.splitWords)
|
||||
"${target.asmVarname} + ${indexNum.number.toInt()}"
|
||||
else
|
||||
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
when(value.kind) {
|
||||
@@ -282,8 +285,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
|
||||
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1")
|
||||
if(target.array.splitWords) {
|
||||
asmgen.out(" lda ${target.array.variable.name}_lsb,y | sta P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" lda ${target.array.variable.name}_msb,y | sta P8ZP_SCRATCH_W1+1")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1")
|
||||
}
|
||||
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplaceModification_word_litval_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt())
|
||||
@@ -303,8 +311,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird source type ${value.kind}")
|
||||
}
|
||||
asmgen.restoreRegisterLocal(CpuRegister.Y)
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y")
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y")
|
||||
if(target.array.splitWords) {
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name}_lsb,y")
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}_msb,y")
|
||||
} else {
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y")
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
|
||||
@@ -772,13 +785,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"<" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
cmp $otherName
|
||||
bcc +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta $name""")
|
||||
ldy $name
|
||||
cpy $otherName
|
||||
rol a
|
||||
eor #1
|
||||
sta $name""")
|
||||
}
|
||||
else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
@@ -798,13 +810,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"<=" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
cmp $name
|
||||
bcs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta $name""")
|
||||
ldy $otherName
|
||||
cpy $name
|
||||
rol a
|
||||
sta $name""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
@@ -823,13 +833,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
">" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
cmp $otherName
|
||||
lda #0
|
||||
ldy $name
|
||||
cpy $otherName
|
||||
beq +
|
||||
bcs ++
|
||||
+ lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
rol a
|
||||
+ sta $name""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
@@ -849,13 +857,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
">=" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
cmp $otherName
|
||||
bcs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta $name""")
|
||||
ldy $name
|
||||
cpy $otherName
|
||||
rol a
|
||||
sta $name""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
@@ -963,13 +969,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"<" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
cmp #$value
|
||||
bcc +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta $name""")
|
||||
ldy $name
|
||||
cpy #$value
|
||||
rol a
|
||||
eor #1
|
||||
sta $name""")
|
||||
}
|
||||
else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
@@ -989,13 +994,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
"<=" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda #$value
|
||||
cmp $name
|
||||
bcs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta $name""")
|
||||
ldy #$value
|
||||
cpy $name
|
||||
rol a
|
||||
sta $name""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
@@ -1014,13 +1017,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
">" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
cmp #$value
|
||||
lda #0
|
||||
ldy $name
|
||||
cpy #$value
|
||||
beq +
|
||||
bcs ++
|
||||
+ lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
rol a
|
||||
+ sta $name""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
@@ -1040,13 +1041,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
">=" -> {
|
||||
if(dt==DataType.UBYTE) {
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
cmp #$value
|
||||
bcs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta $name""")
|
||||
ldy $name
|
||||
cpy #$value
|
||||
rol a
|
||||
sta $name""")
|
||||
} else {
|
||||
// see http://www.6502.org/tutorials/compare_beyond.html
|
||||
asmgen.out("""
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8tests.codegencpu6502
|
||||
|
||||
import io.kotest.assertions.throwables.shouldNotThrowAny
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.SymbolTableMaker
|
||||
@@ -102,5 +103,14 @@ class TestCodegen: FunSpec({
|
||||
result.name shouldBe "test"
|
||||
Files.deleteIfExists(Path("${result.name}.asm"))
|
||||
}
|
||||
|
||||
test("64tass assembler available? - if this fails you need to install 64tass in the path") {
|
||||
val command = mutableListOf("64tass", "--version")
|
||||
shouldNotThrowAny {
|
||||
val proc = ProcessBuilder(command).inheritIO().start()
|
||||
val result = proc.waitFor()
|
||||
result.shouldBe(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ dependencies {
|
||||
implementation project(':codeGenIntermediate')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ dependencies {
|
||||
implementation project(':intermediate')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
@@ -46,7 +47,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
assignment: PtAugmentedAssign
|
||||
): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val vmDt = codeGen.irType(value.type)
|
||||
val vmDt = irType(value.type)
|
||||
return when(assignment.operator) {
|
||||
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
||||
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
||||
@@ -72,7 +73,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val targetDt = codeGen.irType(assignment.target.type)
|
||||
val targetDt = irType(assignment.target.type)
|
||||
return when (assignment.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
|
||||
@@ -161,7 +162,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val targetIdent = assignment.target.identifier
|
||||
val targetMemory = assignment.target.memory
|
||||
val targetArray = assignment.target.array
|
||||
val vmDt = codeGen.irType(assignment.value.type)
|
||||
val valueDt = irType(assignment.value.type)
|
||||
val targetDt = irType(assignment.target.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
var valueRegister = -1
|
||||
@@ -169,30 +171,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val zero = codeGen.isZero(assignment.value)
|
||||
if(!zero) {
|
||||
// calculate the assignment value
|
||||
if (vmDt == IRDataType.FLOAT) {
|
||||
if (valueDt == IRDataType.FLOAT) {
|
||||
val tr = expressionEval.translateExpression(assignment.value)
|
||||
valueFpRegister = tr.resultFpReg
|
||||
addToResult(result, tr, -1, valueFpRegister)
|
||||
} else {
|
||||
val extendByteToWord = if(targetDt != valueDt) {
|
||||
// usually an error EXCEPT when a byte is assigned to a word.
|
||||
if(targetDt==IRDataType.WORD && valueDt==IRDataType.BYTE)
|
||||
true
|
||||
else
|
||||
throw AssemblyError("assignment value and target dt mismatch")
|
||||
} else false
|
||||
if (assignment.value is PtMachineRegister) {
|
||||
valueRegister = (assignment.value as PtMachineRegister).register
|
||||
if(extendByteToWord)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(assignment.value)
|
||||
valueRegister = tr.resultReg
|
||||
addToResult(result, tr, valueRegister, -1)
|
||||
if(extendByteToWord) {
|
||||
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
|
||||
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1 = valueRegister), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(targetIdent!=null) {
|
||||
val instruction = if(zero) {
|
||||
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
|
||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||
} else {
|
||||
if (vmDt == IRDataType.FLOAT) {
|
||||
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||
}
|
||||
if (targetDt == IRDataType.FLOAT)
|
||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||
else
|
||||
IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||
}
|
||||
result += IRCodeChunk(null, null).also { it += instruction }
|
||||
return result
|
||||
@@ -214,70 +228,106 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(zero) {
|
||||
// there's no STOREZIX instruction
|
||||
valueRegister = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, immediate = 0)
|
||||
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
|
||||
}
|
||||
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
||||
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
||||
result += code
|
||||
return result
|
||||
}
|
||||
|
||||
val fixedIndex = constIntValue(targetArray.index)
|
||||
val iterable = codeGen.symbolTable.flat.getValue(targetArray.variable.name) as StStaticVariable
|
||||
val arrayLength = iterable.length!!
|
||||
if(zero) {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(vmDt== IRDataType.FLOAT) {
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
|
||||
val chunk = IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
val msbReg = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
||||
}
|
||||
result += chunk
|
||||
} else {
|
||||
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||
result += code
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(targetArray.splitWords) {
|
||||
val msbReg = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
else if(targetMemory!=null) {
|
||||
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
||||
require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
||||
if(zero) {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, address = (targetMemory.address as PtNumber).number.toInt()) }
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
|
||||
}
|
||||
} else {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) }
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +338,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
|
||||
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
|
||||
// returns the code to load the Index into the register, which is also return\ed.
|
||||
// returns the code to load the Index into the register, which is also returned.
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(itemsize==1) {
|
||||
if(itemsize==1 || array.splitWords) {
|
||||
val tr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
return Pair(result, tr.resultReg)
|
||||
|
||||
@@ -4,6 +4,8 @@ import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.code.core.SplitWordArrayTypes
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@@ -13,12 +15,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return when(call.name) {
|
||||
"any" -> funcAny(call)
|
||||
"all" -> funcAll(call)
|
||||
"abs" -> funcAbs(call)
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(call)
|
||||
"cmp" -> funcCmp(call)
|
||||
"sgn" -> funcSgn(call)
|
||||
"sqrt16" -> funcSqrt16(call)
|
||||
"divmod" -> funcDivmod(call, IRDataType.BYTE)
|
||||
"divmodw" -> funcDivmod(call, IRDataType.WORD)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
|
||||
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
|
||||
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
|
||||
"pop" -> funcPop(call)
|
||||
"popw" -> funcPopw(call)
|
||||
"push" -> funcPush(call)
|
||||
@@ -37,6 +39,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"pokew" -> funcPokeW(call)
|
||||
"pokemon" -> ExpressionCodeResult.EMPTY // easter egg function
|
||||
"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)
|
||||
"reverse" -> funcReverse(call)
|
||||
"rol" -> funcRolRor(Opcode.ROXL, call)
|
||||
@@ -82,11 +87,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1=left.resultReg, immediate = 0), null)
|
||||
addToResult(result, right, right.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1=right.resultReg, immediate = 1), null)
|
||||
addInstr(result, IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.COMPARE_STRINGS.number), null)
|
||||
addInstr(result, IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=left.resultReg), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
|
||||
}
|
||||
|
||||
@@ -96,7 +98,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val dt = codeGen.irType(call.args[0].type)
|
||||
val dt = irType(call.args[0].type)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMP, dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
|
||||
}
|
||||
@@ -118,13 +120,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
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)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
|
||||
@@ -143,13 +141,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
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)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
|
||||
@@ -162,12 +156,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
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 -> {
|
||||
val notNegativeLabel = codeGen.createLabelName()
|
||||
val compareReg = codeGen.registers.nextFree()
|
||||
@@ -193,13 +181,18 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
result += IRCodeChunk(notNegativeLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
DataType.FLOAT -> {
|
||||
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 {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val vmDt = codeGen.irType(call.type)
|
||||
val vmDt = irType(call.type)
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
@@ -209,15 +202,37 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, vmDt, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcSqrt16(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
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.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||
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)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt for sqrt")
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -266,17 +281,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
|
||||
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
|
||||
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||
else -> throw IllegalArgumentException("weird type to reverse")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
}
|
||||
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)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
@@ -291,17 +304,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
DataType.ARRAY_W -> IMSyscall.SORT_WORD
|
||||
DataType.STR -> IMSyscall.SORT_UBYTE
|
||||
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")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
}
|
||||
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)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
@@ -317,6 +328,80 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, lsbTr.resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val type = irType(call.type)
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||
val maximumTr = exprGen.translateExpression(call.args[2])
|
||||
result += valueTr.chunks
|
||||
result += minimumTr.chunks
|
||||
result += maximumTr.chunks
|
||||
if(type==IRDataType.FLOAT) {
|
||||
result += codeGen.makeSyscall(
|
||||
IMSyscall.CLAMP_FLOAT, listOf(
|
||||
valueTr.dt to valueTr.resultFpReg,
|
||||
minimumTr.dt to minimumTr.resultFpReg,
|
||||
maximumTr.dt to maximumTr.resultFpReg,
|
||||
), type to valueTr.resultFpReg
|
||||
)
|
||||
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
|
||||
} else {
|
||||
val syscall = when(call.type) {
|
||||
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE
|
||||
DataType.BYTE -> IMSyscall.CLAMP_BYTE
|
||||
DataType.UWORD -> IMSyscall.CLAMP_UWORD
|
||||
DataType.WORD -> IMSyscall.CLAMP_WORD
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
result += codeGen.makeSyscall(syscall, listOf(
|
||||
valueTr.dt to valueTr.resultReg,
|
||||
minimumTr.dt to minimumTr.resultReg,
|
||||
maximumTr.dt to maximumTr.resultReg,
|
||||
), type to valueTr.resultReg
|
||||
)
|
||||
return ExpressionCodeResult(result, type, valueTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMin(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val type = irType(call.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val leftTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
|
||||
val after = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
|
||||
// right <= left, take right
|
||||
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
|
||||
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||
}
|
||||
result += IRCodeChunk(after, null)
|
||||
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcMax(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val type = irType(call.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val leftTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
|
||||
val after = codeGen.createLabelName()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
|
||||
// right >= left, take right
|
||||
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
|
||||
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||
}
|
||||
result += IRCodeChunk(after, null)
|
||||
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isZero(call.args[1])) {
|
||||
@@ -455,7 +540,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
|
||||
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val vmDt = codeGen.irType(call.args[0].type)
|
||||
val vmDt = irType(call.args[0].type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
|
||||
@@ -28,10 +28,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
|
||||
return when (expr) {
|
||||
is PtMachineRegister -> {
|
||||
ExpressionCodeResult(emptyList(), codeGen.irType(expr.type), expr.register, -1)
|
||||
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
|
||||
}
|
||||
is PtNumber -> {
|
||||
val vmDt = codeGen.irType(expr.type)
|
||||
val vmDt = irType(expr.type)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
val resultFpRegister = codeGen.registers.nextFreeFloat()
|
||||
@@ -45,7 +45,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val vmDt = codeGen.irType(expr.type)
|
||||
val vmDt = irType(expr.type)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if (expr.type in PassByValueDatatypes) {
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
@@ -66,7 +66,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
val vmDt = codeGen.irType(expr.type)
|
||||
val vmDt = irType(expr.type)
|
||||
val symbol = expr.identifier.name
|
||||
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
|
||||
val code = IRCodeChunk(null, null)
|
||||
@@ -105,53 +105,35 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
var tr = translateExpression(check.element)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable
|
||||
when(iterable.dt) {
|
||||
DataType.STR -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.STRING_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 2)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.BYTEARRAY_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
// SysCall call convention: return value in register r0
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 2)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.WORDARRAY_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
|
||||
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}")
|
||||
@@ -160,7 +142,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
|
||||
val vmDt = codeGen.irType(arrayIx.type)
|
||||
val vmDt = irType(arrayIx.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arrayVarSymbol = arrayIx.variable.name
|
||||
|
||||
@@ -178,6 +160,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
var resultRegister = -1
|
||||
|
||||
if(arrayIx.splitWords) {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
val iterable = codeGen.symbolTable.flat.getValue(arrayIx.variable.name) as StStaticVariable
|
||||
val arrayLength = iterable.length!!
|
||||
resultRegister = codeGen.registers.nextFree()
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = (arrayIx.index as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val tmpRegMsb = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb+$memOffset")
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb+$memOffset")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb)
|
||||
}
|
||||
} else {
|
||||
val tr = translateExpression(arrayIx.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val tmpRegMsb = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb)
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
}
|
||||
|
||||
var resultFpRegister = -1
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()
|
||||
@@ -210,7 +219,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = translateExpression(expr.value)
|
||||
addToResult(result, tr, tr.resultReg, tr.resultFpReg)
|
||||
val vmDt = codeGen.irType(expr.type)
|
||||
val vmDt = irType(expr.type)
|
||||
when(expr.operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
@@ -326,12 +335,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else -> throw AssemblyError("weird cast type")
|
||||
}
|
||||
|
||||
return ExpressionCodeResult(result, codeGen.irType(cast.type), actualResultReg2, actualResultFpReg2)
|
||||
return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)
|
||||
}
|
||||
|
||||
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
|
||||
require(!codeGen.options.useNewExprCode)
|
||||
val vmDt = codeGen.irType(binExpr.left.type)
|
||||
val vmDt = irType(binExpr.left.type)
|
||||
val signed = binExpr.left.type in SignedDatatypes
|
||||
return when(binExpr.operator) {
|
||||
"+" -> operatorPlus(binExpr, vmDt)
|
||||
@@ -358,97 +367,82 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
for ((index, argspec) in fcall.args.zip(callTarget.parameters).withIndex()) {
|
||||
val (arg, param) = argspec
|
||||
val paramDt = codeGen.irType(param.type)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
val paramDt = irType(parameter.type)
|
||||
val tr = translateExpression(arg)
|
||||
result += tr.chunks
|
||||
if(paramDt==IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, paramDt, fpReg1 = tr.resultFpReg, immediate = index), null)
|
||||
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, paramDt, reg1 = tr.resultReg, immediate = index), null)
|
||||
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
|
||||
result += tr.chunks
|
||||
}
|
||||
// for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
// val paramDt = codeGen.irType(parameter.type)
|
||||
// val symbol = "${fcall.name}.${parameter.name}"
|
||||
// if(codeGen.isZero(arg)) {
|
||||
// addInstr(result, IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol), null)
|
||||
// } else {
|
||||
// if (paramDt == IRDataType.FLOAT) {
|
||||
// val tr = translateExpression(arg)
|
||||
// addToResult(result, tr, -1, tr.resultFpReg)
|
||||
// addInstr(result, IRInstruction(Opcode.STOREM, paramDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol), null)
|
||||
// } else {
|
||||
// val tr = translateExpression(arg)
|
||||
// addToResult(result, tr, tr.resultReg, -1)
|
||||
// addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1 = tr.resultReg, labelSymbol = symbol), null)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return if(fcall.void) {
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name), null)
|
||||
// return value
|
||||
val returnRegSpec = if(fcall.void) null else {
|
||||
val returnIrType = irType(callTarget.returnType!!)
|
||||
if(returnIrType==IRDataType.FLOAT)
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), null)
|
||||
else
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null)
|
||||
}
|
||||
// create the call
|
||||
val call = IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
addInstr(result, call, null)
|
||||
return if(fcall.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
} else {
|
||||
var resultReg = -1
|
||||
var resultFpReg = -1
|
||||
if(fcall.type==DataType.FLOAT) {
|
||||
resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
addInstr(result, IRInstruction(Opcode.CALLR, IRDataType.FLOAT, fpReg1=resultFpReg, labelSymbol=fcall.name), null)
|
||||
} else {
|
||||
resultReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.CALLR, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null)
|
||||
}
|
||||
ExpressionCodeResult(result, codeGen.irType(fcall.type), resultReg, resultFpReg)
|
||||
}
|
||||
else if(fcall.type==DataType.FLOAT)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
|
||||
}
|
||||
is StRomSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
val paramDt = codeGen.irType(parameter.type)
|
||||
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
|
||||
if(codeGen.isZero(arg)) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr), null)
|
||||
val paramDt = irType(parameter.type)
|
||||
val tr = translateExpression(arg)
|
||||
if(paramDt==IRDataType.FLOAT)
|
||||
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, parameter.register)))
|
||||
else
|
||||
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
|
||||
result += tr.chunks
|
||||
}
|
||||
// return value
|
||||
val returnRegSpec = if(fcall.void) null else {
|
||||
if(callTarget.returns.isEmpty())
|
||||
null
|
||||
else if(callTarget.returns.size==1) {
|
||||
val returns = callTarget.returns[0]
|
||||
val returnIrType = irType(returns.type)
|
||||
if(returnIrType==IRDataType.FLOAT)
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
|
||||
else
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
|
||||
} else {
|
||||
if (paramDt == IRDataType.FLOAT)
|
||||
throw AssemblyError("doesn't support float register argument in asm romsub")
|
||||
val tr = translateExpression(arg)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STORECPU, paramDt, reg1 = tr.resultReg, labelSymbol = paramRegStr), null)
|
||||
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
|
||||
val returns = callTarget.returns.first { it.register.registerOrPair!=null }
|
||||
val returnIrType = irType(returns.type)
|
||||
if(returnIrType==IRDataType.FLOAT)
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
|
||||
else
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
|
||||
}
|
||||
}
|
||||
// just a regular call without using Vm register call convention: the value is returned in CPU registers!
|
||||
addInstr(result, IRInstruction(Opcode.CALL, address = callTarget.address.toInt()), null)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
if(!fcall.void) {
|
||||
when(callTarget.returns.size) {
|
||||
0 -> throw AssemblyError("expect a return value")
|
||||
1 -> {
|
||||
if(fcall.type==DataType.FLOAT)
|
||||
throw AssemblyError("doesn't support float register result in asm romsub")
|
||||
val returns = callTarget.returns.single()
|
||||
val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
|
||||
}
|
||||
else -> {
|
||||
val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null }
|
||||
if(returnRegister!=null) {
|
||||
// we skip the other values returned in the status flags.
|
||||
val regStr = returnRegister.register.registerOrPair.toString()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
|
||||
} else {
|
||||
val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null }
|
||||
if(firstReturnRegister!=null) {
|
||||
// we just take the first register return value and ignore the rest.
|
||||
val regStr = firstReturnRegister.register.registerOrPair.toString()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
|
||||
} else {
|
||||
throw AssemblyError("invalid number of return values from call")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, if(fcall.void) IRDataType.BYTE else codeGen.irType(fcall.type), resultReg, -1)
|
||||
// create the call
|
||||
val call =
|
||||
if(callTarget.address==null)
|
||||
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
else
|
||||
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
addInstr(result, call, null)
|
||||
return if(fcall.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else if(fcall.type==DataType.FLOAT)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
|
||||
}
|
||||
else -> throw AssemblyError("invalid node type")
|
||||
}
|
||||
|
||||
@@ -53,25 +53,15 @@ class IRCodeGen(
|
||||
replaceMemoryMappedVars(irProg)
|
||||
ensureFirstChunkLabels(irProg)
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
|
||||
if(options.optimize) {
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize()
|
||||
|
||||
val remover = IRUnusedCodeRemover(irProg, irSymbolTable, errors)
|
||||
do {
|
||||
val numRemoved = remover.optimize()
|
||||
} while(numRemoved>0 && errors.noErrors())
|
||||
|
||||
errors.report()
|
||||
|
||||
irProg.linkChunks() // re-link
|
||||
} else {
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimizeOnlyJoinChunks()
|
||||
}
|
||||
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
|
||||
val regOptimizer = IRRegisterOptimizer(irProg)
|
||||
regOptimizer.optimize()
|
||||
|
||||
return irProg
|
||||
}
|
||||
|
||||
@@ -153,7 +143,7 @@ class IRCodeGen(
|
||||
// note: we do still export the memory mapped symbols so a code generator can use those
|
||||
// for instance when a piece of inlined assembly references them.
|
||||
val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>()
|
||||
irProg.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }.forEach { chunk ->
|
||||
irProg.foreachCodeChunk { chunk ->
|
||||
chunk.instructions.withIndex().forEach {
|
||||
(idx, instr) ->
|
||||
val symbolExpr = instr.labelSymbol
|
||||
@@ -466,46 +456,51 @@ class IRCodeGen(
|
||||
val endLabel = createLabelName()
|
||||
if(iterableVar.dt==DataType.STR) {
|
||||
// iterate over a zero-terminated string
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
val chunk = IRCodeChunk(loopLabel, null)
|
||||
chunk += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = iterable.name)
|
||||
chunk += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=tmpReg, immediate = 0, labelSymbol = endLabel)
|
||||
chunk += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
result += chunk
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
|
||||
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1 = tmpReg, immediate = 0, labelSymbol = endLabel)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
val jumpChunk = IRCodeChunk(null, null)
|
||||
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
|
||||
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1 = indexReg)
|
||||
jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
|
||||
result += jumpChunk
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
} else if(iterable.type in SplitWordArrayTypes) {
|
||||
// iterate over lsb/msb split word array
|
||||
val elementDt = ArrayToElementTypes.getValue(iterable.type)
|
||||
if(elementDt !in WordDatatypes)
|
||||
throw AssemblyError("weird dt")
|
||||
val length = iterableVar.length!!
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
val tmpRegLsb = registers.nextFree()
|
||||
val tmpRegMsb = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = length, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = length, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=tmpRegLsb, reg2=tmpRegMsb)
|
||||
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpRegLsb, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
|
||||
it += IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(length==256) 0 else length, labelSymbol = loopLabel)
|
||||
}
|
||||
} else {
|
||||
// iterate over array
|
||||
// iterate over regular array
|
||||
val elementDt = ArrayToElementTypes.getValue(iterable.type)
|
||||
val elementSize = program.memsizer.memorySize(elementDt)
|
||||
val lengthBytes = iterableVar.length!! * elementSize
|
||||
if(lengthBytes<256) {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0)
|
||||
result += chunk
|
||||
val chunk2 = IRCodeChunk(loopLabel, null)
|
||||
chunk2 += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
|
||||
chunk2 += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
result += chunk2
|
||||
result += translateNode(forLoop.statements)
|
||||
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = lengthBytes, labelSymbol = loopLabel), null)
|
||||
} else if(lengthBytes==256) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
val chunk = IRCodeChunk(loopLabel, null)
|
||||
chunk += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
|
||||
chunk += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
result += chunk
|
||||
result += translateNode(forLoop.statements)
|
||||
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = 0, labelSymbol = loopLabel), null)
|
||||
} else {
|
||||
throw AssemblyError("iterator length should never exceed 256")
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
result += IRCodeChunk(loopLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
|
||||
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(lengthBytes==256) 0 else lengthBytes, labelSymbol = loopLabel), null)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird for iterable")
|
||||
@@ -535,16 +530,36 @@ class IRCodeGen(
|
||||
addToResult(result, fromTr, fromTr.resultReg, -1)
|
||||
|
||||
val labelAfterFor = createLabelName()
|
||||
val greaterOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
|
||||
addInstr(result, IRInstruction(greaterOpcode, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor), null)
|
||||
val precheckInstruction = if(loopvarDt in SignedDatatypes) {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGTSR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
|
||||
else
|
||||
IRInstruction(Opcode.BGTSR, loopvarDtIr, toTr.resultReg, fromTr.resultReg, labelSymbol=labelAfterFor)
|
||||
} else {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGTR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
|
||||
else
|
||||
IRInstruction(Opcode.BGTR, loopvarDtIr, toTr.resultReg, fromTr.resultReg, labelSymbol=labelAfterFor)
|
||||
}
|
||||
addInstr(result, precheckInstruction, null)
|
||||
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
|
||||
// if endvalue >= index, iterate loop
|
||||
val branchOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGESR else Opcode.BGER
|
||||
addInstr(result, IRInstruction(branchOpcode, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel), null)
|
||||
val branchInstr = if(loopvarDt in SignedDatatypes) {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel)
|
||||
else
|
||||
IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=fromTr.resultReg, reg2=toTr.resultReg, labelSymbol=loopLabel)
|
||||
} else {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGER, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel)
|
||||
else
|
||||
IRInstruction(Opcode.BGER, loopvarDtIr, reg1=fromTr.resultReg, reg2=toTr.resultReg, labelSymbol=loopLabel)
|
||||
}
|
||||
addInstr(result, branchInstr, null)
|
||||
|
||||
result += IRCodeChunk(labelAfterFor, null)
|
||||
return result
|
||||
@@ -1206,7 +1221,7 @@ class IRCodeGen(
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result,
|
||||
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = elseLabel),
|
||||
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = elseLabel),
|
||||
null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
@@ -1218,7 +1233,7 @@ class IRCodeGen(
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result,
|
||||
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = afterIfLabel),
|
||||
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel),
|
||||
null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
@@ -1344,59 +1359,127 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
|
||||
val operationMem: Opcode
|
||||
val operationRegister: Opcode
|
||||
when(postIncrDecr.operator) {
|
||||
"++" -> {
|
||||
operationMem = Opcode.INCM
|
||||
operationRegister = Opcode.INC
|
||||
}
|
||||
"--" -> {
|
||||
operationMem = Opcode.DECM
|
||||
operationRegister = Opcode.DEC
|
||||
}
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
val ident = postIncrDecr.target.identifier
|
||||
val memory = postIncrDecr.target.memory
|
||||
val array = postIncrDecr.target.array
|
||||
val irDt = irType(postIncrDecr.target.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(ident!=null) {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
|
||||
} else if(memory!=null) {
|
||||
if(memory.address is PtNumber) {
|
||||
val address = (memory.address as PtNumber).number.toInt()
|
||||
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
val incReg = registers.nextFree()
|
||||
chunk += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
chunk += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
chunk += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
result += chunk
|
||||
}
|
||||
} else if (array!=null) {
|
||||
val array = postIncrDecr.target.array
|
||||
if(array?.splitWords==true) {
|
||||
val variable = array.variable.name
|
||||
val itemsize = program.memsizer.memorySize(array.type)
|
||||
val fixedIndex = constIntValue(array.index)
|
||||
val iterable = symbolTable.flat.getValue(array.variable.name) as StStaticVariable
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
|
||||
val skipLabel = createLabelName()
|
||||
when(postIncrDecr.operator) {
|
||||
"++" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
"--" -> {
|
||||
val valueReg=registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null).also {
|
||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
} else {
|
||||
val arrayLength = iterable.length!!
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
val incReg = registers.nextFree()
|
||||
chunk += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
chunk += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
chunk += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
result += chunk
|
||||
val skipLabel = createLabelName()
|
||||
when(postIncrDecr.operator) {
|
||||
"++" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
"--" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
} else {
|
||||
val ident = postIncrDecr.target.identifier
|
||||
val memory = postIncrDecr.target.memory
|
||||
val irDt = irType(postIncrDecr.target.type)
|
||||
val operationMem: Opcode
|
||||
val operationRegister: Opcode
|
||||
when(postIncrDecr.operator) {
|
||||
"++" -> {
|
||||
operationMem = Opcode.INCM
|
||||
operationRegister = Opcode.INC
|
||||
}
|
||||
"--" -> {
|
||||
operationMem = Opcode.DECM
|
||||
operationRegister = Opcode.DEC
|
||||
}
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
if(ident!=null) {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
|
||||
} else if(memory!=null) {
|
||||
if(memory.address is PtNumber) {
|
||||
val address = (memory.address as PtNumber).number.toInt()
|
||||
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
}
|
||||
}
|
||||
} else if (array!=null) {
|
||||
val variable = array.variable.name
|
||||
val itemsize = program.memsizer.memorySize(array.type)
|
||||
val fixedIndex = constIntValue(array.index)
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
|
||||
} else {
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
if(itemsize>1)
|
||||
result += multiplyByConst(IRDataType.BYTE, indexTr.resultReg, itemsize)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
}
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1419,10 +1502,10 @@ class IRCodeGen(
|
||||
addToResult(result, countTr, countTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = skipRepeatLabel), null)
|
||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.DEC, irDt, reg1=countTr.resultReg)
|
||||
chunk += IRInstruction(Opcode.BNE, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = repeatLabel)
|
||||
result += chunk
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg)
|
||||
it += IRInstruction(Opcode.BNE, irDt, reg1 = countTr.resultReg, immediate = 0, labelSymbol = repeatLabel)
|
||||
}
|
||||
result += IRCodeChunk(skipRepeatLabel, null)
|
||||
return result
|
||||
}
|
||||
@@ -1470,7 +1553,7 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun translate(block: PtBlock): IRBlock {
|
||||
val irBlock = IRBlock(block.name, block.address, translate(block.alignment), block.position) // no use for other attributes yet?
|
||||
val irBlock = IRBlock(block.name, block.address, block.library, block.forceOutput, translate(block.alignment), block.position) // no use for other attributes yet?
|
||||
for (child in block.children) {
|
||||
when(child) {
|
||||
is PtNop -> { /* nothing */ }
|
||||
@@ -1539,20 +1622,6 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun irType(type: DataType): IRDataType {
|
||||
return when(type) {
|
||||
DataType.BOOL,
|
||||
DataType.UBYTE,
|
||||
DataType.BYTE -> IRDataType.BYTE
|
||||
DataType.UWORD,
|
||||
DataType.WORD -> IRDataType.WORD
|
||||
DataType.FLOAT -> IRDataType.FLOAT
|
||||
in PassByReferenceDatatypes -> IRDataType.WORD
|
||||
else -> throw AssemblyError("no IR datatype for $type")
|
||||
}
|
||||
}
|
||||
|
||||
private var labelSequenceNumber = 0
|
||||
internal fun createLabelName(): String {
|
||||
labelSequenceNumber++
|
||||
@@ -1583,4 +1652,14 @@ class IRCodeGen(
|
||||
irSymbolTable.add(staticVar)
|
||||
return tempvar
|
||||
}
|
||||
|
||||
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
|
||||
return IRCodeChunk(label, null).also {
|
||||
val args = params.map { (dt, reg)->
|
||||
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
|
||||
}
|
||||
val returnSpec = if(returns==null) null else FunctionCallArgs.RegSpec(returns.first, returns.second, null)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number, fcallArgs = FunctionCallArgs(args, returnSpec))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,44 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.intermediate.*
|
||||
|
||||
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
fun optimizeOnlyJoinChunks() {
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
fun optimize(optimizationsEnabled: Boolean, errors: IErrorReporter) {
|
||||
if(!optimizationsEnabled)
|
||||
return optimizeOnlyJoinChunks()
|
||||
|
||||
peepholeOptimize()
|
||||
val remover = IRUnusedCodeRemover(irprog, errors)
|
||||
var totalRemovals = 0
|
||||
do {
|
||||
val numRemoved = remover.optimize()
|
||||
totalRemovals += numRemoved
|
||||
} while(numRemoved>0 && errors.noErrors())
|
||||
errors.report()
|
||||
|
||||
if(totalRemovals>0) {
|
||||
irprog.linkChunks() // re-link again.
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeOnlyJoinChunks() {
|
||||
irprog.foreachSub { sub ->
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
joinChunks(sub)
|
||||
}
|
||||
irprog.linkChunks() // re-link
|
||||
}
|
||||
|
||||
fun optimize() {
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
private fun peepholeOptimize() {
|
||||
irprog.foreachSub { sub ->
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
joinChunks(sub)
|
||||
|
||||
// TODO also do register optimization step here?
|
||||
|
||||
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
||||
// we don't optimize Inline Asm chunks here.
|
||||
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
|
||||
@@ -36,7 +60,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
removeEmptyChunks(sub)
|
||||
}
|
||||
|
||||
irprog.linkChunks() // re-link
|
||||
irprog.linkChunks() // re-link
|
||||
}
|
||||
|
||||
private fun removeEmptyChunks(sub: IRSubroutine) {
|
||||
@@ -93,7 +117,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(sub.chunks.isEmpty())
|
||||
return
|
||||
|
||||
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
||||
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
||||
if(chunk.label!=null)
|
||||
return false
|
||||
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
||||
@@ -110,12 +134,39 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
chunks += sub.chunks[0]
|
||||
for(ix in 1 until sub.chunks.size) {
|
||||
val lastChunk = chunks.last()
|
||||
if(mayJoin(lastChunk, sub.chunks[ix])) {
|
||||
lastChunk.instructions += sub.chunks[ix].instructions
|
||||
lastChunk.next = sub.chunks[ix].next
|
||||
val candidate = sub.chunks[ix]
|
||||
when(candidate) {
|
||||
is IRCodeChunk -> {
|
||||
if(mayJoinCodeChunks(lastChunk, candidate)) {
|
||||
lastChunk.instructions += candidate.instructions
|
||||
lastChunk.next = candidate.next
|
||||
}
|
||||
else
|
||||
chunks += candidate
|
||||
}
|
||||
is IRInlineAsmChunk -> {
|
||||
if(candidate.label!=null)
|
||||
chunks += candidate
|
||||
else if(lastChunk.isEmpty()) {
|
||||
val label = lastChunk.label
|
||||
if(label!=null)
|
||||
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
|
||||
else
|
||||
chunks += candidate
|
||||
}
|
||||
}
|
||||
is IRInlineBinaryChunk -> {
|
||||
if(candidate.label!=null)
|
||||
chunks += candidate
|
||||
else if(lastChunk.isEmpty()) {
|
||||
val label = lastChunk.label
|
||||
if(label!=null)
|
||||
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
|
||||
else
|
||||
chunks += candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
chunks += sub.chunks[ix]
|
||||
}
|
||||
sub.chunks.clear()
|
||||
sub.chunks += chunks
|
||||
@@ -202,14 +253,16 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
|
||||
// replace call + return --> jump
|
||||
if(idx>0 && ins.opcode==Opcode.RETURN) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLR) {
|
||||
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
// This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
|
||||
// If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
|
||||
// if(idx>0 && ins.opcode==Opcode.RETURN) {
|
||||
// val previous = chunk.instructions[idx-1]
|
||||
// if(previous.opcode==Opcode.CALL) {
|
||||
// chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||
// chunk.instructions.removeAt(idx)
|
||||
// changed = true
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -5,23 +5,32 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
internal class IRUnusedCodeRemover(
|
||||
class IRUnusedCodeRemover(
|
||||
private val irprog: IRProgram,
|
||||
private val symbolTable: IRSymbolTable,
|
||||
private val errors: IErrorReporter
|
||||
) {
|
||||
fun optimize(): Int {
|
||||
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
|
||||
var numRemoved = removeUnusedSubroutines() + removeUnusedAsmSubroutines()
|
||||
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.label?.let { allLabeledChunks[it] = chunk }
|
||||
// remove empty blocks
|
||||
irprog.blocks.reversed().forEach { block ->
|
||||
if(block.isEmpty()) {
|
||||
irprog.blocks.remove(block)
|
||||
irprog.st.removeTree(block.label)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
|
||||
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
|
||||
return numRemoved
|
||||
}
|
||||
|
||||
// remove empty subs
|
||||
private fun removeUnusedSubroutines(): Int {
|
||||
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
|
||||
irprog.foreachCodeChunk { chunk ->
|
||||
chunk.label?.let { allLabeledChunks[it] = chunk }
|
||||
}
|
||||
|
||||
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
|
||||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
@@ -29,24 +38,86 @@ internal class IRUnusedCodeRemover(
|
||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
symbolTable.removeTree(sub.label)
|
||||
irprog.st.removeTree(sub.label)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty blocks
|
||||
irprog.blocks.reversed().forEach { block ->
|
||||
if(block.isEmpty()) {
|
||||
irprog.blocks.remove(block)
|
||||
symbolTable.removeTree(block.label)
|
||||
numRemoved++
|
||||
return numRemoved
|
||||
}
|
||||
|
||||
private fun removeUnusedAsmSubroutines(): Int {
|
||||
val allLabeledAsmsubs = irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
|
||||
.associateBy { it.label }
|
||||
|
||||
var numRemoved = removeSimpleUnlinkedAsmsubs(allLabeledAsmsubs)
|
||||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(libraryFilePrefix)) {
|
||||
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
irprog.st.removeTree(sub.label)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numRemoved
|
||||
}
|
||||
|
||||
private fun removeSimpleUnlinkedAsmsubs(allSubs: Map<String, IRAsmSubroutine>): Int {
|
||||
val linkedAsmSubs = mutableSetOf<IRAsmSubroutine>()
|
||||
|
||||
// TODO: asmsubs in library modules are never removed, we can't really tell here if they're actually being called or not...
|
||||
|
||||
// check if asmsub is called from another asmsub
|
||||
irprog.blocks.asSequence().forEach { block ->
|
||||
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
|
||||
if (block.forceOutput || block.library)
|
||||
linkedAsmSubs += sub
|
||||
if (sub.asmChunk.isNotEmpty()) {
|
||||
allSubs.forEach { (label, asmsub) ->
|
||||
if (sub.asmChunk.assembly.contains(label))
|
||||
linkedAsmSubs += asmsub
|
||||
}
|
||||
}
|
||||
val inlineAsm = sub.asmChunk.next as? IRInlineAsmChunk
|
||||
if(inlineAsm!=null) {
|
||||
allSubs.forEach { (label, asmsub) ->
|
||||
if (inlineAsm.assembly.contains(label))
|
||||
linkedAsmSubs += asmsub
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if asmsub is linked or called from another regular subroutine
|
||||
irprog.foreachCodeChunk { chunk ->
|
||||
chunk.instructions.forEach {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return removeUnlinkedAsmsubs(linkedAsmSubs)
|
||||
}
|
||||
|
||||
private fun removeUnlinkedAsmsubs(linkedAsmSubs: Set<IRAsmSubroutine>): Int {
|
||||
var numRemoved = 0
|
||||
irprog.blocks.asSequence().forEach { block ->
|
||||
block.children.withIndex().reversed().forEach { (index, child) ->
|
||||
if(child is IRAsmSubroutine && child !in linkedAsmSubs) {
|
||||
block.children.removeAt(index)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
}
|
||||
return numRemoved
|
||||
}
|
||||
|
||||
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
|
||||
val entrypointSub = irprog.blocks.single { it.label=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
|
||||
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
|
||||
@@ -79,29 +150,27 @@ internal class IRUnusedCodeRemover(
|
||||
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
|
||||
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
|
||||
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.next?.let { next -> linkedChunks += next }
|
||||
chunk.instructions.forEach {
|
||||
if(it.branchTarget==null) {
|
||||
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
|
||||
} else {
|
||||
linkedChunks += it.branchTarget!!
|
||||
}
|
||||
irprog.foreachCodeChunk { chunk ->
|
||||
chunk.next?.let { next -> linkedChunks += next }
|
||||
chunk.instructions.forEach {
|
||||
if(it.branchTarget==null) {
|
||||
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
|
||||
} else {
|
||||
linkedChunks += it.branchTarget!!
|
||||
}
|
||||
if (chunk.label == "main.start")
|
||||
linkedChunks += chunk
|
||||
}
|
||||
if (chunk.label == "main.start")
|
||||
linkedChunks += chunk
|
||||
}
|
||||
|
||||
return removeUnlinkedChunks(linkedChunks)
|
||||
}
|
||||
|
||||
private fun removeUnlinkedChunks(
|
||||
linkedChunks: MutableSet<IRCodeChunkBase>
|
||||
linkedChunks: Set<IRCodeChunkBase>
|
||||
): Int {
|
||||
var numRemoved = 0
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
irprog.foreachSub { sub ->
|
||||
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
|
||||
if (chunk !in linkedChunks) {
|
||||
if (chunk === sub.chunks[0]) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import prog8.intermediate.*
|
||||
class TestIRPeepholeOpt: FunSpec({
|
||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||
require(chunks.first().label=="main.start")
|
||||
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
||||
chunks.forEach { sub += it }
|
||||
block += sub
|
||||
@@ -36,7 +36,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
return makeIRProgram(listOf(chunk))
|
||||
}
|
||||
|
||||
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }
|
||||
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.allSubs().flatMap { it.chunks }.toList()
|
||||
|
||||
test("remove nops") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
@@ -46,7 +46,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 3
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().single().instructions.size shouldBe 1
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().size shouldBe 4
|
||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().size shouldBe 4
|
||||
irProg.chunks()[0].label shouldBe "main.start"
|
||||
irProg.chunks()[1].label shouldBe "label"
|
||||
@@ -92,7 +92,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 6
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 1
|
||||
instr[0].opcode shouldBe Opcode.CLC
|
||||
@@ -107,7 +107,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 1
|
||||
instr[0].opcode shouldBe Opcode.LOADR
|
||||
@@ -130,7 +130,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 10
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 2
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 2
|
||||
instr[0].opcode shouldBe Opcode.INC
|
||||
@@ -161,7 +161,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 8
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
val instr = irProg.chunks().single().instructions
|
||||
instr.size shouldBe 4
|
||||
instr[0].opcode shouldBe Opcode.LOAD
|
||||
|
||||
@@ -441,7 +441,7 @@ class TestVmCodeGen: FunSpec({
|
||||
irChunks.size shouldBe 1
|
||||
}
|
||||
|
||||
test("romsub allowed in codegen") {
|
||||
test("romsub allowed in ir-codegen") {
|
||||
//main {
|
||||
// romsub $5000 = routine()
|
||||
//
|
||||
@@ -452,7 +452,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val romsub = PtAsmSub("routine", 0x5000u, emptySet(), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(romsub)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||
|
||||
@@ -68,6 +68,124 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
// choose specific builtin function for the given types
|
||||
val func = functionCallExpr.target.nameInSource
|
||||
if(func==listOf("clamp")) {
|
||||
val t1 = functionCallExpr.args[0].inferType(program)
|
||||
if(t1.isKnown) {
|
||||
val replaceFunc: String
|
||||
if(t1.isBytes) {
|
||||
replaceFunc = if(t1.istype(DataType.BYTE)) "clamp__byte" else "clamp__ubyte"
|
||||
} else if(t1.isInteger) {
|
||||
replaceFunc = if(t1.istype(DataType.WORD)) "clamp__word" else "clamp__uword"
|
||||
} else {
|
||||
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
|
||||
return noModifications
|
||||
}
|
||||
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||
functionCallExpr))
|
||||
}
|
||||
}
|
||||
else if(func==listOf("min") || func==listOf("max")) {
|
||||
val t1 = functionCallExpr.args[0].inferType(program)
|
||||
val t2 = functionCallExpr.args[1].inferType(program)
|
||||
if(t1.isKnown && t2.isKnown) {
|
||||
val funcName = func[0]
|
||||
val replaceFunc: String
|
||||
if(t1.isBytes && t2.isBytes) {
|
||||
replaceFunc = if(t1.istype(DataType.BYTE) || t2.istype(DataType.BYTE))
|
||||
"${funcName}__byte"
|
||||
else
|
||||
"${funcName}__ubyte"
|
||||
} else if(t1.isInteger && t2.isInteger) {
|
||||
replaceFunc = if(t1.istype(DataType.WORD) || t2.istype(DataType.WORD))
|
||||
"${funcName}__word"
|
||||
else
|
||||
"${funcName}__uword"
|
||||
} else if(t1.isNumeric && t2.isNumeric) {
|
||||
errors.err("min/max not supported for floats", functionCallExpr.position)
|
||||
return noModifications
|
||||
} else {
|
||||
errors.err("expected numeric arguments", functionCallExpr.args[0].position)
|
||||
return noModifications
|
||||
}
|
||||
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||
functionCallExpr))
|
||||
}
|
||||
}
|
||||
else if(func==listOf("abs")) {
|
||||
val t1 = functionCallExpr.args[0].inferType(program)
|
||||
if(t1.isKnown) {
|
||||
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
|
||||
val replaceFunc = when(dt) {
|
||||
DataType.BYTE -> "abs__byte"
|
||||
DataType.WORD -> "abs__word"
|
||||
DataType.FLOAT -> "abs__float"
|
||||
DataType.UBYTE, DataType.UWORD -> {
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||
}
|
||||
else -> {
|
||||
errors.err("expected numeric argument", functionCallExpr.args[0].position)
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||
functionCallExpr))
|
||||
}
|
||||
}
|
||||
else if(func==listOf("sqrt")) {
|
||||
val t1 = functionCallExpr.args[0].inferType(program)
|
||||
if(t1.isKnown) {
|
||||
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
|
||||
val replaceFunc = when(dt) {
|
||||
DataType.UBYTE -> "sqrt__ubyte"
|
||||
DataType.UWORD -> "sqrt__uword"
|
||||
DataType.FLOAT -> "sqrt__float"
|
||||
else -> {
|
||||
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||
functionCallExpr))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
// choose specific builtin function for the given types
|
||||
val func = functionCallStatement.target.nameInSource
|
||||
if(func==listOf("divmod")) {
|
||||
val argTypes = functionCallStatement.args.map {it.inferType(program)}.toSet()
|
||||
if(argTypes.size!=1) {
|
||||
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
|
||||
return noModifications
|
||||
}
|
||||
val t1 = argTypes.single()
|
||||
if(t1.isKnown) {
|
||||
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
|
||||
val replaceFunc = when(dt) {
|
||||
DataType.UBYTE -> "divmod__ubyte"
|
||||
DataType.UWORD -> "divmod__uword"
|
||||
else -> {
|
||||
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
return listOf(IAstModification.SetExpression({functionCallStatement.target = it as IdentifierReference},
|
||||
IdentifierReference(listOf(replaceFunc), functionCallStatement.target.position),
|
||||
functionCallStatement))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +275,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> {
|
||||
val rangeExpr = decl.value as? RangeExpression
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array
|
||||
|
||||
@@ -157,6 +157,12 @@ class UnusedCodeRemover(private val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
if(assignment.target isSameAs assignment.value)
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun deduplicateAssignments(statements: List<Statement>, scope: IStatementContainer): List<IAstModification> {
|
||||
// removes 'duplicate' assignments that assign the same target directly after another, unless it is a function call
|
||||
val linesToRemove = mutableListOf<Assignment>()
|
||||
|
||||
@@ -38,10 +38,10 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
|
||||
|
||||
testImplementation project(':intermediate')
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
||||
@@ -7,31 +7,6 @@ atari {
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
; ---- kernal routines ----
|
||||
; TODO
|
||||
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; TODO
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
clc
|
||||
; TODO reset screen mode etc etc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
rts ; no phase 2 steps on the Atari
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +15,26 @@ sys {
|
||||
|
||||
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; TODO
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
clc
|
||||
; TODO reset screen mode etc etc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
rts ; no phase 2 steps on the Atari
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
@@ -188,6 +183,19 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_set_irqd() {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_clear_irqd() {
|
||||
%asm {{
|
||||
plp
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
; TODO
|
||||
|
||||
@@ -70,7 +70,7 @@ sub uppercase() {
|
||||
; TODO
|
||||
}
|
||||
|
||||
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -81,7 +81,7 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -91,7 +91,7 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -101,7 +101,7 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -118,7 +118,6 @@ romsub $F2Fd = waitkey()
|
||||
asmsub chrout(ubyte char @ A) {
|
||||
%asm {{
|
||||
sta _tmp_outchar+1
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
tya
|
||||
@@ -130,7 +129,6 @@ _tmp_outchar
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -210,7 +208,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
@@ -228,7 +226,7 @@ asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
@@ -249,7 +247,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
@@ -261,7 +259,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
; Prog8 definitions for the Commodore-128
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
c64 {
|
||||
cbm {
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
@@ -47,6 +48,99 @@ c64 {
|
||||
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
|
||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||
|
||||
; STROUT --> use txt.print
|
||||
; CLEARSCR -> use txt.clear_screen
|
||||
; HOMECRSR -> use txt.home or txt.plot
|
||||
|
||||
romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||
|
||||
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
|
||||
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 compatible ROM kernal routines ----
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
txa
|
||||
pha
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr cbm.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
c64 {
|
||||
; C64 I/O registers (VIC, SID, CIA)
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
&ubyte SPRPTR0 = 2040
|
||||
&ubyte SPRPTR1 = 2041
|
||||
@@ -196,95 +290,91 @@ c64 {
|
||||
|
||||
; ---- end of SID registers ----
|
||||
|
||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||
}
|
||||
|
||||
; STROUT --> use txt.print
|
||||
; CLEARSCR -> use txt.clear_screen
|
||||
; HOMECRSR -> use txt.home or txt.plot
|
||||
c128 {
|
||||
; ---- C128 specific registers ----
|
||||
|
||||
romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
|
||||
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
|
||||
&ubyte VM3 = $0A2E ; starting page for VDC screen mem
|
||||
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
|
||||
|
||||
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
|
||||
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
; ---- C128 specific system utility routines: ----
|
||||
|
||||
; ---- end of C64 compatible ROM kernal routines ----
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
asmsub disable_basic() clobbers(A) {
|
||||
%asm {{
|
||||
txa
|
||||
pha
|
||||
jsr c64.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda $0a04 ; disable BASIC shadow registers
|
||||
and #$fe
|
||||
sta $0a04
|
||||
|
||||
lda #$01 ; disable BASIC IRQ service routine
|
||||
sta $12fd
|
||||
|
||||
lda #$ff ; disable screen editor IRQ setup
|
||||
sta $d8
|
||||
|
||||
lda #$b7 ; skip programmable function key check
|
||||
sta $033c
|
||||
|
||||
lda #$0e ; bank out BASIC ROM
|
||||
sta $ff00
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- end of C128 specific system utility routines ----
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
sta $ff00 ; select default bank 15
|
||||
jsr cbm.IOINIT
|
||||
jsr cbm.RESTOR
|
||||
jsr cbm.CINT
|
||||
lda #6
|
||||
sta c64.EXTCOL
|
||||
lda #7
|
||||
sta cbm.COLOR
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
jsr disable_runstop_and_charsetswitch
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr c64.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
rts ; no phase 2 steps on the C128
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
jmp sys.enable_runstop_and_charsetswitch
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- system utility routines that are essentially the same as on the C64: -----
|
||||
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||
%asm {{
|
||||
lda #$80
|
||||
@@ -305,18 +395,18 @@ asmsub enable_runstop_and_charsetswitch() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
rol a
|
||||
sta _use_kernal
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta c64.CINV
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
sta c64.CINV+1
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr _irq_handler_init
|
||||
@@ -334,7 +424,7 @@ _modified jsr $ffff ; modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
|
||||
+ jmp cbm.IRQDFRT ; continue with normal kernal irq routine
|
||||
|
||||
_use_kernal .byte 0
|
||||
|
||||
@@ -386,10 +476,10 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<c64.IRQDFRT
|
||||
sta c64.CINV
|
||||
lda #>c64.IRQDFRT
|
||||
sta c64.CINV+1
|
||||
lda #<cbm.IRQDFRT
|
||||
sta cbm.CINV
|
||||
lda #>cbm.IRQDFRT
|
||||
sta cbm.CINV+1
|
||||
lda #0
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #%10000001
|
||||
@@ -399,21 +489,21 @@ asmsub restore_irq() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
rol a
|
||||
sta set_irq._use_kernal
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
sei
|
||||
jsr _setup_raster_irq
|
||||
lda #<_raster_irq_handler
|
||||
sta c64.CINV
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta c64.CINV+1
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
@@ -421,8 +511,8 @@ _raster_irq_handler
|
||||
jsr set_irq._irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr set_irq._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda set_irq._use_kernal
|
||||
bne +
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
@@ -432,7 +522,7 @@ _modified jsr $ffff ; modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with kernal irq routine
|
||||
+ jmp cbm.IRQDFRT ; continue with kernal irq routine
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
@@ -456,99 +546,13 @@ _setup_raster_irq
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c128 {
|
||||
; ---- C128 specific registers ----
|
||||
|
||||
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
|
||||
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
|
||||
&ubyte VM3 = $0A2E ; starting page for VDC screen mem
|
||||
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
|
||||
|
||||
|
||||
; ---- C128 specific system utility routines: ----
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
lda #0
|
||||
sta $ff00 ; select default bank 15
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
lda #6
|
||||
sta c64.EXTCOL
|
||||
lda #7
|
||||
sta c64.COLOR
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
jsr c64.disable_runstop_and_charsetswitch
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
rts ; no phase 2 steps on the C128
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
jmp c64.enable_runstop_and_charsetswitch
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub disable_basic() clobbers(A) {
|
||||
%asm {{
|
||||
lda $0a04 ; disable BASIC shadow registers
|
||||
and #$fe
|
||||
sta $0a04
|
||||
|
||||
lda #$01 ; disable BASIC IRQ service routine
|
||||
sta $12fd
|
||||
|
||||
lda #$ff ; disable screen editor IRQ setup
|
||||
sta $d8
|
||||
|
||||
lda #$b7 ; skip programmable function key check
|
||||
sta $033c
|
||||
|
||||
lda #$0e ; bank out BASIC ROM
|
||||
sta $ff00
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- end of C128 specific system utility routines ----
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
lda #0
|
||||
sta $ff00 ; default bank 15
|
||||
jmp (c64.RESET_VEC)
|
||||
jmp (cbm.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -566,9 +570,9 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
ldx P8ZP_SCRATCH_B1
|
||||
rts
|
||||
|
||||
+ lda c64.TIME_LO
|
||||
+ lda cbm.TIME_LO
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- lda c64.TIME_LO
|
||||
- lda cbm.TIME_LO
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
@@ -725,13 +729,26 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_set_irqd() {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_clear_irqd() {
|
||||
%asm {{
|
||||
plp
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
lda #0
|
||||
sta $ff00 ; default bank 15
|
||||
jsr c64.CLRCHN ; reset i/o channels
|
||||
jsr c64.enable_runstop_and_charsetswitch
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr sys.enable_runstop_and_charsetswitch
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
|
||||
@@ -30,10 +30,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||
%asm {{
|
||||
sec
|
||||
jsr c64.PLOT
|
||||
jsr cbm.PLOT
|
||||
tay
|
||||
clc
|
||||
jmp c64.PLOT
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- sta c64.Screen+250*0-1,y
|
||||
sta c64.Screen+250*1-1,y
|
||||
sta c64.Screen+250*2-1,y
|
||||
sta c64.Screen+250*3-1,y
|
||||
- sta cbm.Screen+250*0-1,y
|
||||
sta cbm.Screen+250*1-1,y
|
||||
sta cbm.Screen+250*2-1,y
|
||||
sta cbm.Screen+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
@@ -72,10 +72,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- sta c64.Colors+250*0-1,y
|
||||
sta c64.Colors+250*1-1,y
|
||||
sta c64.Colors+250*2-1,y
|
||||
sta c64.Colors+250*3-1,y
|
||||
- sta cbm.Colors+250*0-1,y
|
||||
sta cbm.Colors+250*1-1,y
|
||||
sta cbm.Colors+250*2-1,y
|
||||
sta cbm.Colors+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
@@ -83,7 +83,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
}
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
c64.COLOR = txtcol
|
||||
cbm.COLOR = txtcol
|
||||
}
|
||||
|
||||
sub lowercase() {
|
||||
@@ -96,7 +96,7 @@ sub uppercase() {
|
||||
c128.VM1 &= ~2
|
||||
}
|
||||
|
||||
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -110,10 +110,10 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
lda c64.Colors + 40*row + 1,x
|
||||
sta c64.Colors + 40*row + 0,x
|
||||
lda cbm.Screen + 40*row + 1,x
|
||||
sta cbm.Screen + 40*row + 0,x
|
||||
lda cbm.Colors + 40*row + 1,x
|
||||
sta cbm.Colors + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
@@ -125,8 +125,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
lda cbm.Screen + 40*row + 1,x
|
||||
sta cbm.Screen + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
@@ -137,7 +137,7 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -149,10 +149,10 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
lda c64.Colors + 40*row + 0,x
|
||||
sta c64.Colors + 40*row + 1,x
|
||||
lda cbm.Screen + 40*row + 0,x
|
||||
sta cbm.Screen + 40*row + 1,x
|
||||
lda cbm.Colors + 40*row + 0,x
|
||||
sta cbm.Colors + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -162,8 +162,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
lda cbm.Screen + 40*row + 0,x
|
||||
sta cbm.Screen + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -173,7 +173,7 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -185,10 +185,10 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row-1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row-1),x
|
||||
lda cbm.Colors + 40*row,x
|
||||
sta cbm.Colors + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -198,8 +198,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -209,7 +209,7 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -221,10 +221,10 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row+1),x
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
lda cbm.Colors + 40*row,x
|
||||
sta cbm.Colors + 40*(row+1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -234,8 +234,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -245,20 +245,20 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
; by just one call to cbm.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
@@ -272,11 +272,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
@@ -292,16 +292,16 @@ _print_byte_digits
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
@@ -315,45 +315,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr c64.CHROUT
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
@@ -361,7 +361,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
@@ -373,7 +373,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -394,7 +394,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
@@ -417,14 +417,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp c64.CHROUT
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
@@ -457,7 +457,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr c64.CHRIN
|
||||
- jsr cbm.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
@@ -588,7 +588,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr c64.PLOT
|
||||
jsr cbm.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
@@ -597,7 +597,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
; -- returns the text screen width (number of columns)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
txa
|
||||
rts
|
||||
}}
|
||||
@@ -606,7 +606,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||
; -- returns the text screen height (number of rows)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
|
||||
@@ -144,3 +144,19 @@ func_all_f_stack .proc
|
||||
jsr a_times_5
|
||||
jmp prog8_lib.func_all_b_stack
|
||||
.pend
|
||||
|
||||
func_abs_f_into_FAC1 .proc
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
jsr ABS
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sqrt_into_FAC1 .proc
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
jsr SQR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
+133
-120
@@ -1,7 +1,11 @@
|
||||
; Prog8 definitions for the Commodore-64
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
c64 {
|
||||
|
||||
cbm {
|
||||
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
@@ -50,6 +54,91 @@ c64 {
|
||||
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
|
||||
|
||||
; ---- CBM ROM kernal routines (C64 addresses) ----
|
||||
|
||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
txa
|
||||
pha
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr cbm.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c64 {
|
||||
; C64 I/O registers (VIC, SID, CIA)
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
&ubyte SPRPTR0 = 2040
|
||||
&ubyte SPRPTR1 = 2041
|
||||
@@ -199,94 +288,14 @@ c64 {
|
||||
|
||||
; ---- end of SID registers ----
|
||||
|
||||
|
||||
; ---- C64 ROM kernal routines ----
|
||||
|
||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 ROM kernal routines ----
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
txa
|
||||
pha
|
||||
jsr c64.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr c64.RDTIM
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
|
||||
; ---- C64 specific system utility routines: ----
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
@@ -301,13 +310,13 @@ asmsub init_system() {
|
||||
sta $00
|
||||
lda #%00100111
|
||||
sta $01
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
jsr cbm.IOINIT
|
||||
jsr cbm.RESTOR
|
||||
jsr cbm.CINT
|
||||
lda #6
|
||||
sta c64.EXTCOL
|
||||
lda #7
|
||||
sta c64.COLOR
|
||||
sta cbm.COLOR
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
jsr disable_runstop_and_charsetswitch
|
||||
@@ -327,7 +336,7 @@ asmsub init_system_phase2() {
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
jmp c64.enable_runstop_and_charsetswitch
|
||||
jmp sys.enable_runstop_and_charsetswitch
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -351,18 +360,18 @@ asmsub enable_runstop_and_charsetswitch() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
rol a
|
||||
sta _use_kernal
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta c64.CINV
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
sta c64.CINV+1
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr _irq_handler_init
|
||||
@@ -380,7 +389,7 @@ _modified jsr $ffff ; modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
|
||||
+ jmp cbm.IRQDFRT ; continue with normal kernal irq routine
|
||||
|
||||
_use_kernal .byte 0
|
||||
|
||||
@@ -432,10 +441,10 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<c64.IRQDFRT
|
||||
sta c64.CINV
|
||||
lda #>c64.IRQDFRT
|
||||
sta c64.CINV+1
|
||||
lda #<cbm.IRQDFRT
|
||||
sta cbm.CINV
|
||||
lda #>cbm.IRQDFRT
|
||||
sta cbm.CINV+1
|
||||
lda #0
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #%10000001
|
||||
@@ -445,21 +454,21 @@ asmsub restore_irq() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
rol a
|
||||
sta set_irq._use_kernal
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
sei
|
||||
jsr _setup_raster_irq
|
||||
lda #<_raster_irq_handler
|
||||
sta c64.CINV
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta c64.CINV+1
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
@@ -467,8 +476,8 @@ _raster_irq_handler
|
||||
jsr set_irq._irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr set_irq._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda set_irq._use_kernal
|
||||
bne +
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
@@ -478,7 +487,7 @@ _modified jsr $ffff ; modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with kernal irq routine
|
||||
+ jmp cbm.IRQDFRT ; continue with kernal irq routine
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
@@ -502,23 +511,14 @@ _setup_raster_irq
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- end of C64 specific system utility routines ----
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
|
||||
asmsub reset_system() {
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
lda #14
|
||||
sta $01 ; bank the kernal in
|
||||
jmp (c64.RESET_VEC)
|
||||
jmp (cbm.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -536,9 +536,9 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
ldx P8ZP_SCRATCH_B1
|
||||
rts
|
||||
|
||||
+ lda c64.TIME_LO
|
||||
+ lda cbm.TIME_LO
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- lda c64.TIME_LO
|
||||
- lda cbm.TIME_LO
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
@@ -695,13 +695,26 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_set_irqd() {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_clear_irqd() {
|
||||
%asm {{
|
||||
plp
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
lda #31
|
||||
sta $01 ; bank the kernal in
|
||||
jsr c64.CLRCHN ; reset i/o channels
|
||||
jsr c64.enable_runstop_and_charsetswitch
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr sys.enable_runstop_and_charsetswitch
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
|
||||
@@ -30,10 +30,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||
%asm {{
|
||||
sec
|
||||
jsr c64.PLOT
|
||||
jsr cbm.PLOT
|
||||
tay
|
||||
clc
|
||||
jmp c64.PLOT
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- sta c64.Screen+250*0-1,y
|
||||
sta c64.Screen+250*1-1,y
|
||||
sta c64.Screen+250*2-1,y
|
||||
sta c64.Screen+250*3-1,y
|
||||
- sta cbm.Screen+250*0-1,y
|
||||
sta cbm.Screen+250*1-1,y
|
||||
sta cbm.Screen+250*2-1,y
|
||||
sta cbm.Screen+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
@@ -72,10 +72,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- sta c64.Colors+250*0-1,y
|
||||
sta c64.Colors+250*1-1,y
|
||||
sta c64.Colors+250*2-1,y
|
||||
sta c64.Colors+250*3-1,y
|
||||
- sta cbm.Colors+250*0-1,y
|
||||
sta cbm.Colors+250*1-1,y
|
||||
sta cbm.Colors+250*2-1,y
|
||||
sta cbm.Colors+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
@@ -83,7 +83,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
}
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
c64.COLOR = txtcol
|
||||
cbm.COLOR = txtcol
|
||||
}
|
||||
|
||||
sub lowercase() {
|
||||
@@ -94,7 +94,7 @@ sub uppercase() {
|
||||
c64.VMCSB &= ~2
|
||||
}
|
||||
|
||||
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -108,10 +108,10 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
lda c64.Colors + 40*row + 1,x
|
||||
sta c64.Colors + 40*row + 0,x
|
||||
lda cbm.Screen + 40*row + 1,x
|
||||
sta cbm.Screen + 40*row + 0,x
|
||||
lda cbm.Colors + 40*row + 1,x
|
||||
sta cbm.Colors + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
@@ -123,8 +123,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
lda cbm.Screen + 40*row + 1,x
|
||||
sta cbm.Screen + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
@@ -135,7 +135,7 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -147,10 +147,10 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
lda c64.Colors + 40*row + 0,x
|
||||
sta c64.Colors + 40*row + 1,x
|
||||
lda cbm.Screen + 40*row + 0,x
|
||||
sta cbm.Screen + 40*row + 1,x
|
||||
lda cbm.Colors + 40*row + 0,x
|
||||
sta cbm.Colors + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -160,8 +160,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
lda cbm.Screen + 40*row + 0,x
|
||||
sta cbm.Screen + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -171,7 +171,7 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -183,10 +183,10 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row-1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row-1),x
|
||||
lda cbm.Colors + 40*row,x
|
||||
sta cbm.Colors + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -196,8 +196,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -207,7 +207,7 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -219,10 +219,10 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row+1),x
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
lda cbm.Colors + 40*row,x
|
||||
sta cbm.Colors + 40*(row+1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -232,8 +232,8 @@ _scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
@@ -243,20 +243,20 @@ _scroll_screen ; scroll only the screen memory
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
; by just one call to cbm.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
@@ -270,11 +270,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
@@ -290,16 +290,16 @@ _print_byte_digits
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
@@ -313,45 +313,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr c64.CHROUT
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
@@ -359,7 +359,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
@@ -371,7 +371,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -392,7 +392,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
@@ -415,14 +415,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp c64.CHROUT
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
@@ -455,7 +455,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr c64.CHRIN
|
||||
- jsr cbm.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
@@ -586,7 +586,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr c64.PLOT
|
||||
jsr cbm.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
@@ -595,7 +595,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
; -- returns the text screen width (number of columns)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
txa
|
||||
rts
|
||||
}}
|
||||
@@ -604,7 +604,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||
; -- returns the text screen height (number of rows)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
; Cx16 specific disk drive I/O routines.
|
||||
|
||||
%import diskio
|
||||
%import string
|
||||
|
||||
cx16diskio {
|
||||
|
||||
; Same as diskio.load() but with additional bank parameter to select the Ram bank to load into.
|
||||
; Use kernal LOAD routine to load the given program file in memory.
|
||||
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
|
||||
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
|
||||
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
||||
; and the rest is loaded at the given location in memory.
|
||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||
; You can use the load_size() function to calcuate the size of the file that was loaded.
|
||||
sub load(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
|
||||
cx16.rambank(bank)
|
||||
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, false)
|
||||
}
|
||||
|
||||
; Same as diskio.load_raw() but with additional bank parameter to select the Ram bank to load into.
|
||||
; Use kernal LOAD routine to load the given file in memory.
|
||||
; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
|
||||
; The load address is mandatory. Returns the number of bytes loaded.
|
||||
; If you load into regular system ram, use cx16.getrambank() for the bank argument,
|
||||
; or alternatively make sure to reset the correct ram bank yourself after the load!
|
||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||
; You can use the load_size() function to calcuate the size of the file that was loaded.
|
||||
sub load_raw(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
|
||||
cx16.rambank(bank)
|
||||
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, true)
|
||||
}
|
||||
|
||||
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
|
||||
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
|
||||
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
|
||||
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
||||
}
|
||||
|
||||
asmsub vload(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
; -- like the basic command VLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file has to have the usual 2 byte header (which will be skipped)
|
||||
%asm {{
|
||||
clc
|
||||
internal_vload:
|
||||
phx
|
||||
pha
|
||||
tya
|
||||
tax
|
||||
bcc +
|
||||
ldy #%00000010 ; headerless load mode
|
||||
bne ++
|
||||
+ ldy #0 ; normal load mode
|
||||
+ lda #1
|
||||
jsr c64.SETLFS
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr prog8_lib.strlen
|
||||
tya
|
||||
ldx cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr c64.SETNAM
|
||||
pla
|
||||
clc
|
||||
adc #2
|
||||
ldx cx16.r1
|
||||
ldy cx16.r1+1
|
||||
stz P8ZP_SCRATCH_B1
|
||||
jsr c64.LOAD
|
||||
bcs +
|
||||
inc P8ZP_SCRATCH_B1
|
||||
+ jsr c64.CLRCHN
|
||||
lda #1
|
||||
jsr c64.CLOSE
|
||||
plx
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vload_raw(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file is read fully including the first two bytes.
|
||||
%asm {{
|
||||
sec
|
||||
jmp vload.internal_vload
|
||||
}}
|
||||
}
|
||||
|
||||
; Replacement function that makes use of fast block read capability of the X16,
|
||||
; and can wrap over multiple ram banks while reading.
|
||||
; Use this in place of regular diskio.f_read() on X16.
|
||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
if not diskio.iteration_in_progress or not num_bytes
|
||||
return 0
|
||||
|
||||
diskio.list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
|
||||
; commander X16 supports fast block-read via macptr() kernal call
|
||||
uword size
|
||||
while num_bytes {
|
||||
size = 255
|
||||
if num_bytes<size
|
||||
size = num_bytes
|
||||
size = cx16.macptr(lsb(size), bufferpointer, false)
|
||||
if_cs
|
||||
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
||||
diskio.list_blocks += size
|
||||
bufferpointer += size
|
||||
if msb(bufferpointer) == $c0
|
||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||
num_bytes -= size
|
||||
if c64.READST() & $40 {
|
||||
diskio.f_close() ; end of file, close it
|
||||
break
|
||||
}
|
||||
}
|
||||
return diskio.list_blocks ; number of bytes read
|
||||
|
||||
byte_read_loop: ; fallback if macptr() isn't supported on the device
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
}}
|
||||
while num_bytes {
|
||||
if c64.READST() {
|
||||
diskio.f_close()
|
||||
if c64.READST() & $40 ; eof?
|
||||
return diskio.list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
%asm {{
|
||||
jsr c64.CHRIN
|
||||
m_in_buffer sta $ffff
|
||||
inc m_in_buffer+1
|
||||
bne +
|
||||
inc m_in_buffer+2
|
||||
+
|
||||
}}
|
||||
diskio.list_blocks++
|
||||
num_bytes--
|
||||
}
|
||||
return diskio.list_blocks ; number of bytes read
|
||||
}
|
||||
|
||||
; replacement function that makes use of fast block read capability of the X16
|
||||
; use this in place of regular diskio.f_read_all() on X16
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
if not diskio.iteration_in_progress
|
||||
return 0
|
||||
|
||||
uword total_read = 0
|
||||
while not c64.READST() {
|
||||
cx16.r0 = cx16diskio.f_read(bufferpointer, 256)
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
}
|
||||
return total_read
|
||||
}
|
||||
|
||||
|
||||
sub chdir(ubyte drivenumber, str path) {
|
||||
; -- change current directory.
|
||||
diskio.list_filename[0] = 'c'
|
||||
diskio.list_filename[1] = 'd'
|
||||
diskio.list_filename[2] = ':'
|
||||
void string.copy(path, &diskio.list_filename+3)
|
||||
diskio.send_command(drivenumber, diskio.list_filename)
|
||||
}
|
||||
|
||||
sub mkdir(ubyte drivenumber, str name) {
|
||||
; -- make a new subdirectory.
|
||||
diskio.list_filename[0] = 'm'
|
||||
diskio.list_filename[1] = 'd'
|
||||
diskio.list_filename[2] = ':'
|
||||
void string.copy(name, &diskio.list_filename+3)
|
||||
diskio.send_command(drivenumber, diskio.list_filename)
|
||||
}
|
||||
|
||||
sub rmdir(ubyte drivenumber, str name) {
|
||||
; -- remove a subdirectory.
|
||||
void string.find(name, '*')
|
||||
if_cs
|
||||
return ; refuse to act on a wildcard *
|
||||
diskio.list_filename[0] = 'r'
|
||||
diskio.list_filename[1] = 'd'
|
||||
diskio.list_filename[2] = ':'
|
||||
void string.copy(name, &diskio.list_filename+3)
|
||||
diskio.send_command(drivenumber, diskio.list_filename)
|
||||
}
|
||||
|
||||
sub relabel(ubyte drivenumber, str name) {
|
||||
; -- change the disk label.
|
||||
diskio.list_filename[0] = 'r'
|
||||
diskio.list_filename[1] = '-'
|
||||
diskio.list_filename[2] = 'h'
|
||||
diskio.list_filename[3] = ':'
|
||||
void string.copy(name, &diskio.list_filename+4)
|
||||
diskio.send_command(drivenumber, diskio.list_filename)
|
||||
}
|
||||
|
||||
sub f_seek(uword pos_hiword, uword pos_loword) {
|
||||
; -- seek in the reading file opened with f_open, to the given 32-bits position
|
||||
ubyte[6] command = ['p',0,0,0,0,0]
|
||||
command[1] = 12 ; f_open uses channel 12
|
||||
command[2] = lsb(pos_loword)
|
||||
command[3] = msb(pos_loword)
|
||||
command[4] = lsb(pos_hiword)
|
||||
command[5] = msb(pos_hiword)
|
||||
send_command:
|
||||
c64.SETNAM(sizeof(command), &command)
|
||||
c64.SETLFS(15, diskio.last_drivenumber, 15)
|
||||
void c64.OPEN()
|
||||
c64.CLOSE(15)
|
||||
}
|
||||
|
||||
; TODO see if we can get this to work as well:
|
||||
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
|
||||
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
|
||||
; cx16diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
|
||||
; cx16diskio.f_seek.command[2] = lsb(pos_loword)
|
||||
; cx16diskio.f_seek.command[3] = msb(pos_loword)
|
||||
; cx16diskio.f_seek.command[4] = lsb(pos_hiword)
|
||||
; cx16diskio.f_seek.command[5] = msb(pos_hiword)
|
||||
; goto cx16diskio.f_seek.send_command
|
||||
; }
|
||||
}
|
||||
@@ -0,0 +1,724 @@
|
||||
; Commander X16 disk drive I/O routines.
|
||||
; Largely compatible with the default C64 ones, but adds more stuff specific to the X16 as well.
|
||||
|
||||
%import textio
|
||||
%import string
|
||||
%import syslib
|
||||
|
||||
diskio {
|
||||
|
||||
ubyte drivenumber = 8
|
||||
|
||||
sub set_drive(ubyte number) {
|
||||
drivenumber = number
|
||||
}
|
||||
|
||||
sub directory() -> bool {
|
||||
; -- Prints the directory contents to the screen. Returns success.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
ubyte status = 1
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
}
|
||||
|
||||
; while not stop key pressed / EOF encountered, read data.
|
||||
status = cbm.READST()
|
||||
if status!=0 {
|
||||
status = 1
|
||||
goto io_error
|
||||
}
|
||||
|
||||
while status==0 {
|
||||
ubyte low = cbm.CHRIN()
|
||||
ubyte high = cbm.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.spc()
|
||||
ubyte @zp char
|
||||
repeat {
|
||||
char = cbm.CHRIN()
|
||||
if char==0
|
||||
break
|
||||
txt.chrout(char)
|
||||
}
|
||||
txt.nl()
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
void cbm.CHRIN()
|
||||
status = cbm.READST()
|
||||
if cbm.STOP2()
|
||||
break
|
||||
}
|
||||
status = cbm.READST()
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(12)
|
||||
|
||||
if status and status & $40 == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
txt.print_ub(status)
|
||||
txt.nl()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
sub diskname() -> uword {
|
||||
; -- Returns pointer to disk name string or 0 if failure.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
ubyte okay = false
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 6 {
|
||||
void cbm.CHRIN() ; skip the 6 prologue bytes
|
||||
}
|
||||
if cbm.READST()!=0
|
||||
goto io_error
|
||||
|
||||
cx16.r0 = &list_filename
|
||||
repeat {
|
||||
@(cx16.r0) = cbm.CHRIN()
|
||||
if @(cx16.r0)==0
|
||||
break
|
||||
cx16.r0++
|
||||
}
|
||||
okay = true
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(12)
|
||||
if okay
|
||||
return &list_filename
|
||||
return 0
|
||||
}
|
||||
|
||||
; internal variables for the iterative file lister / loader
|
||||
bool list_skip_disk_name
|
||||
uword list_pattern
|
||||
uword list_blocks
|
||||
bool iteration_in_progress = false
|
||||
str list_filetype = "???" ; prg, seq, dir
|
||||
str list_filename = "?" * 50
|
||||
|
||||
; ----- get a list of files (uses iteration functions internally) -----
|
||||
|
||||
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
|
||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||
uword buffer_start = filenames_buffer
|
||||
ubyte files_found = 0
|
||||
if lf_start_list(pattern_ptr) {
|
||||
while lf_next_entry() {
|
||||
if list_filetype!="dir" {
|
||||
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
|
||||
files_found++
|
||||
if filenames_buffer - buffer_start > filenames_buf_size-20 {
|
||||
@(filenames_buffer)=0
|
||||
lf_end_list()
|
||||
sys.set_carry()
|
||||
return files_found
|
||||
}
|
||||
}
|
||||
}
|
||||
lf_end_list()
|
||||
}
|
||||
@(filenames_buffer)=0
|
||||
sys.clear_carry()
|
||||
return files_found
|
||||
}
|
||||
|
||||
; ----- iterative file lister functions (uses io channel 12) -----
|
||||
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
lf_end_list()
|
||||
list_pattern = pattern_ptr
|
||||
list_skip_disk_name = true
|
||||
iteration_in_progress = true
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
}
|
||||
|
||||
if cbm.READST()==0
|
||||
return true
|
||||
|
||||
io_error:
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_next_entry() -> bool {
|
||||
; -- retrieve the next entry from an iterative file listing session.
|
||||
; results will be found in list_blocks, list_filename, and list_filetype.
|
||||
; if it returns false though, there are no more entries (or an error occurred).
|
||||
|
||||
if not iteration_in_progress
|
||||
return false
|
||||
|
||||
repeat {
|
||||
void cbm.CHKIN(12) ; use #12 as input channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
ubyte blocks_msb = cbm.CHRIN()
|
||||
|
||||
if cbm.READST()
|
||||
goto close_end
|
||||
|
||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||
|
||||
; read until the filename starts after the first "
|
||||
while cbm.CHRIN()!='\"' {
|
||||
if cbm.READST()
|
||||
goto close_end
|
||||
}
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = cbm.CHRIN()
|
||||
if char==0
|
||||
break
|
||||
if char=='\"'
|
||||
break
|
||||
@(nameptr) = char
|
||||
nameptr++
|
||||
}
|
||||
|
||||
@(nameptr) = 0
|
||||
|
||||
do {
|
||||
cx16.r15L = cbm.CHRIN()
|
||||
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
|
||||
list_filetype[0] = cx16.r15L
|
||||
list_filetype[1] = cbm.CHRIN()
|
||||
list_filetype[2] = cbm.CHRIN()
|
||||
while cbm.CHRIN() {
|
||||
; read the rest of the entry until the end
|
||||
}
|
||||
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
void cbm.CHRIN()
|
||||
|
||||
if not list_skip_disk_name {
|
||||
if not list_pattern
|
||||
return true
|
||||
if string.pattern_match(list_filename, list_pattern)
|
||||
return true
|
||||
}
|
||||
list_skip_disk_name = false
|
||||
}
|
||||
|
||||
close_end:
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_end_list() {
|
||||
; -- end an iterative file listing session (close channels).
|
||||
if iteration_in_progress {
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file loader functions (uses io channel 12) -----
|
||||
|
||||
sub f_open(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative reading with f_read
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
f_close()
|
||||
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
|
||||
void cbm.OPEN() ; open 12,8,12,"filename"
|
||||
if_cc {
|
||||
if cbm.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cc {
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if not cbm.READST() {
|
||||
cbm.CLOSE(12) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
void cbm.CHKIN(12)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
f_close()
|
||||
return false
|
||||
}
|
||||
|
||||
; optimized for Commander X16 to use MACPTR block read kernal call
|
||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
if not iteration_in_progress or not num_bytes
|
||||
return 0
|
||||
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
|
||||
; commander X16 supports fast block-read via macptr() kernal call
|
||||
uword size
|
||||
while num_bytes {
|
||||
size = 255
|
||||
if num_bytes<size
|
||||
size = num_bytes
|
||||
size = cx16.macptr(lsb(size), bufferpointer, false)
|
||||
if_cs
|
||||
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
||||
list_blocks += size
|
||||
bufferpointer += size
|
||||
if msb(bufferpointer) == $c0
|
||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||
num_bytes -= size
|
||||
if cbm.READST() & $40 {
|
||||
f_close() ; end of file, close it
|
||||
break
|
||||
}
|
||||
}
|
||||
return list_blocks ; number of bytes read
|
||||
|
||||
byte_read_loop: ; fallback if macptr() isn't supported on the device
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
}}
|
||||
while num_bytes {
|
||||
if cbm.READST() {
|
||||
f_close()
|
||||
if cbm.READST() & $40 ; eof?
|
||||
return list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
%asm {{
|
||||
jsr cbm.CHRIN
|
||||
m_in_buffer sta $ffff
|
||||
inc m_in_buffer+1
|
||||
bne +
|
||||
inc m_in_buffer+2
|
||||
+
|
||||
}}
|
||||
list_blocks++
|
||||
num_bytes--
|
||||
}
|
||||
return list_blocks ; number of bytes read
|
||||
}
|
||||
|
||||
; optimized for Commander X16 to use MACPTR block read kernal call
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
uword total_read = 0
|
||||
while not cbm.READST() {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
}
|
||||
return total_read
|
||||
}
|
||||
|
||||
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
|
||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||
; Reads characters from the input file UNTIL a newline or return character (or EOF).
|
||||
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
|
||||
; I/O error status should be checked by the caller itself via READST() routine.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx #12
|
||||
jsr cbm.CHKIN ; use channel 12 again for input
|
||||
ldy #0
|
||||
_loop jsr cbm.CHRIN
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
beq _end
|
||||
iny
|
||||
cmp #$0a
|
||||
beq _line_end
|
||||
cmp #$0d
|
||||
bne _loop
|
||||
_line_end dey ; get rid of the trailing end-of-line char
|
||||
lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
_end rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub f_close() {
|
||||
; -- end an iterative file loading session (close channels).
|
||||
if iteration_in_progress {
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file writing functions (uses io channel 13) -----
|
||||
|
||||
sub f_open_w(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative writing with f_write
|
||||
f_close_w()
|
||||
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(13, drivenumber, 1)
|
||||
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||
if_cc {
|
||||
cbm.CHKOUT(13) ; use #13 as output channel
|
||||
return not cbm.READST()
|
||||
}
|
||||
f_close_w()
|
||||
return false
|
||||
}
|
||||
|
||||
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||
; -- write the given number of bytes to the currently open file
|
||||
if num_bytes!=0 {
|
||||
cbm.CHKOUT(13) ; use #13 as output channel again
|
||||
repeat num_bytes {
|
||||
cbm.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
}
|
||||
return not cbm.READST()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub f_close_w() {
|
||||
; -- end an iterative file writing session (close channels).
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(13)
|
||||
}
|
||||
|
||||
|
||||
; ---- other functions ----
|
||||
|
||||
sub status() -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
uword messageptr = &list_filename
|
||||
cbm.SETNAM(0, list_filename)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN() ; open 15,8,15
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while not cbm.READST() {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
@(messageptr) = cx16.r5L
|
||||
messageptr++
|
||||
}
|
||||
@(messageptr) = 0
|
||||
|
||||
done:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(15)
|
||||
return list_filename
|
||||
|
||||
io_error:
|
||||
list_filename = "?disk error"
|
||||
goto done
|
||||
}
|
||||
|
||||
|
||||
; saves a block of memory to disk, including the default 2 byte prg header.
|
||||
sub save(uword filenameptr, uword address, uword size) -> bool {
|
||||
return internal_save_routine(filenameptr, address, size, false)
|
||||
}
|
||||
|
||||
; like save() but omits the 2 byte prg header.
|
||||
sub save_raw(uword filenameptr, uword address, uword size) -> bool {
|
||||
return internal_save_routine(filenameptr, address, size, true)
|
||||
}
|
||||
|
||||
sub internal_save_routine(uword filenameptr, uword address, uword size, bool headerless) -> bool {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = address + size
|
||||
cx16.r0L = 0
|
||||
|
||||
%asm {{
|
||||
lda address
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
lda headerless
|
||||
beq +
|
||||
lda #<P8ZP_SCRATCH_W1
|
||||
jsr cx16.BSAVE
|
||||
bra ++
|
||||
+ lda #<P8ZP_SCRATCH_W1
|
||||
jsr cbm.SAVE
|
||||
+ php
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
plp
|
||||
}}
|
||||
|
||||
if_cc
|
||||
cx16.r0L = cbm.READST()==0
|
||||
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
|
||||
return cx16.r0L
|
||||
}
|
||||
|
||||
; Use kernal LOAD routine to load the given program file in memory.
|
||||
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
|
||||
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
|
||||
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
||||
; and the rest is loaded at the given location in memory.
|
||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
|
||||
; (which is possible on the Commander X16), the returned size is not correct,
|
||||
; because it doesn't take the number of ram banks into account.
|
||||
; You can use the load_size() function to calcuate the size in this case.
|
||||
; NOTE: data is read into the current Ram bank if you're reading into banked ram.
|
||||
; if you require loading into another ram bank, you have to set that
|
||||
; yourself using cx16.rambank(bank) before calling load().
|
||||
sub load(uword filenameptr, uword address_override) -> uword {
|
||||
return internal_load_routine(filenameptr, address_override, false)
|
||||
}
|
||||
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details.
|
||||
sub load_raw(uword filenameptr, uword address) -> uword {
|
||||
return internal_load_routine(filenameptr, address, true)
|
||||
}
|
||||
|
||||
|
||||
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
cx16.r1 = 0
|
||||
if address_override
|
||||
secondary = 0
|
||||
if headerless
|
||||
secondary |= %00000010 ; activate cx16 kernal headerless load support
|
||||
cbm.SETLFS(1, drivenumber, secondary)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
ldx address_override
|
||||
ldy address_override+1
|
||||
jsr cbm.LOAD
|
||||
bcs +
|
||||
stx cx16.r1
|
||||
sty cx16.r1+1
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
}}
|
||||
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
return cx16.r1
|
||||
}
|
||||
|
||||
sub delete(uword filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
list_filename[0] = 's'
|
||||
list_filename[1] = ':'
|
||||
ubyte flen = string.copy(filenameptr, &list_filename+2)
|
||||
cbm.SETNAM(flen+2, list_filename)
|
||||
cbm.SETLFS(1, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub rename(uword oldfileptr, uword newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
list_filename[0] = 'r'
|
||||
list_filename[1] = ':'
|
||||
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
|
||||
list_filename[flen_new+2] = '='
|
||||
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
|
||||
cbm.SETNAM(3+flen_new+flen_old, list_filename)
|
||||
cbm.SETLFS(1, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub send_command(uword commandptr) {
|
||||
; -- send a dos command to the drive
|
||||
cbm.SETNAM(string.length(commandptr), commandptr)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(15)
|
||||
}
|
||||
|
||||
|
||||
; CommanderX16 extensions over the basic C64/C128 diskio routines:
|
||||
|
||||
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
|
||||
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
|
||||
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
|
||||
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
||||
}
|
||||
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
; -- like the basic command VLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file has to have the usual 2 byte header (which will be skipped)
|
||||
%asm {{
|
||||
clc
|
||||
internal_vload:
|
||||
phx
|
||||
pha
|
||||
ldx drivenumber
|
||||
bcc +
|
||||
ldy #%00000010 ; headerless load mode
|
||||
bne ++
|
||||
+ ldy #0 ; normal load mode
|
||||
+ lda #1
|
||||
jsr cbm.SETLFS
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr prog8_lib.strlen
|
||||
tya
|
||||
ldx cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr cbm.SETNAM
|
||||
pla
|
||||
clc
|
||||
adc #2
|
||||
ldx cx16.r1
|
||||
ldy cx16.r1+1
|
||||
stz P8ZP_SCRATCH_B1
|
||||
jsr cbm.LOAD
|
||||
bcs +
|
||||
inc P8ZP_SCRATCH_B1
|
||||
+ jsr cbm.CLRCHN
|
||||
lda #1
|
||||
jsr cbm.CLOSE
|
||||
plx
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
|
||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||
; the file is read fully including the first two bytes.
|
||||
%asm {{
|
||||
sec
|
||||
jmp vload.internal_vload
|
||||
}}
|
||||
}
|
||||
|
||||
sub chdir(str path) {
|
||||
; -- change current directory.
|
||||
list_filename[0] = 'c'
|
||||
list_filename[1] = 'd'
|
||||
list_filename[2] = ':'
|
||||
void string.copy(path, &list_filename+3)
|
||||
send_command(list_filename)
|
||||
}
|
||||
|
||||
sub mkdir(str name) {
|
||||
; -- make a new subdirectory.
|
||||
list_filename[0] = 'm'
|
||||
list_filename[1] = 'd'
|
||||
list_filename[2] = ':'
|
||||
void string.copy(name, &list_filename+3)
|
||||
send_command(list_filename)
|
||||
}
|
||||
|
||||
sub rmdir(str name) {
|
||||
; -- remove a subdirectory.
|
||||
void string.find(name, '*')
|
||||
if_cs
|
||||
return ; refuse to act on a wildcard *
|
||||
list_filename[0] = 'r'
|
||||
list_filename[1] = 'd'
|
||||
list_filename[2] = ':'
|
||||
void string.copy(name, &list_filename+3)
|
||||
send_command(list_filename)
|
||||
}
|
||||
|
||||
sub relabel(str name) {
|
||||
; -- change the disk label.
|
||||
list_filename[0] = 'r'
|
||||
list_filename[1] = '-'
|
||||
list_filename[2] = 'h'
|
||||
list_filename[3] = ':'
|
||||
void string.copy(name, &list_filename+4)
|
||||
send_command(list_filename)
|
||||
}
|
||||
|
||||
sub f_seek(uword pos_hiword, uword pos_loword) {
|
||||
; -- seek in the reading file opened with f_open, to the given 32-bits position
|
||||
ubyte[6] command = ['p',0,0,0,0,0]
|
||||
command[1] = 12 ; f_open uses channel 12
|
||||
command[2] = lsb(pos_loword)
|
||||
command[3] = msb(pos_loword)
|
||||
command[4] = lsb(pos_hiword)
|
||||
command[5] = msb(pos_hiword)
|
||||
send_command:
|
||||
cbm.SETNAM(sizeof(command), &command)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLOSE(15)
|
||||
}
|
||||
|
||||
|
||||
; NOTE: f_seek_w() doesn't work reliably right now. I only manage to corrupt the fat32 filesystem on the sdcard with it...
|
||||
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
|
||||
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
|
||||
; diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
|
||||
; diskio.f_seek.command[2] = lsb(pos_loword)
|
||||
; diskio.f_seek.command[3] = msb(pos_loword)
|
||||
; diskio.f_seek.command[4] = lsb(pos_hiword)
|
||||
; diskio.f_seek.command[5] = msb(pos_hiword)
|
||||
; goto diskio.f_seek.send_command
|
||||
; }
|
||||
|
||||
}
|
||||
@@ -84,7 +84,7 @@ gfx2 {
|
||||
else -> {
|
||||
; back to default text mode
|
||||
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
|
||||
c64.CINT()
|
||||
cbm.CINT()
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
|
||||
width = 0
|
||||
height = 0
|
||||
@@ -155,6 +155,9 @@ gfx2 {
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, uword y, uword length, ubyte color) {
|
||||
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
|
||||
ubyte[9] masked_starts = [ 0, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111]
|
||||
|
||||
if length==0
|
||||
return
|
||||
when active_mode {
|
||||
@@ -163,12 +166,13 @@ gfx2 {
|
||||
ubyte separate_pixels = (8-lsb(x)) & 7
|
||||
if separate_pixels as uword > length
|
||||
separate_pixels = lsb(length)
|
||||
repeat separate_pixels {
|
||||
; TODO optimize this by writing a masked byte in 1 go
|
||||
plot(x, y, color)
|
||||
x++
|
||||
if separate_pixels {
|
||||
position(x,y)
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_starts[separate_pixels]
|
||||
length -= separate_pixels
|
||||
x += separate_pixels
|
||||
}
|
||||
length -= separate_pixels
|
||||
if length {
|
||||
position(x, y)
|
||||
separate_pixels = lsb(length) & 7
|
||||
@@ -205,11 +209,9 @@ _loop lda length
|
||||
bra _loop
|
||||
_done
|
||||
}}
|
||||
repeat separate_pixels {
|
||||
; TODO optimize this by writing a masked byte in 1 go
|
||||
plot(x, y, color)
|
||||
x++
|
||||
}
|
||||
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_ends[separate_pixels]
|
||||
}
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
|
||||
}
|
||||
@@ -366,8 +368,14 @@ _done
|
||||
return
|
||||
position2(x,y,true)
|
||||
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||
color &= 3
|
||||
color <<= gfx2.plot.shift4c[lsb(x) & 3] ; TODO with lookup table
|
||||
;; color &= 3
|
||||
;; color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||
cx16.r2L = lsb(x) & 3
|
||||
when color & 3 {
|
||||
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
|
||||
}
|
||||
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
@@ -547,10 +555,13 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub plot(uword @zp x, uword y, ubyte color) {
|
||||
sub plot(uword @zp x, uword @zp y, ubyte @zp color) {
|
||||
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
|
||||
ubyte[4] @shared shift4c = [6,4,2,0]
|
||||
ubyte[4] shiftedleft_4c_1 = [1<<6, 1<<4, 1<<2, 1<<0]
|
||||
ubyte[4] shiftedleft_4c_2 = [2<<6, 2<<4, 2<<2, 2<<0]
|
||||
ubyte[4] shiftedleft_4c_3 = [3<<6, 3<<4, 3<<2, 3<<0]
|
||||
|
||||
when active_mode {
|
||||
1 -> {
|
||||
@@ -643,8 +654,13 @@ _done
|
||||
; TODO also mostly usable for lores 4c?
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = lsb(x) & 3 ; xbits
|
||||
color &= 3
|
||||
color <<= shift4c[cx16.r2L] ; TODO with lookup table
|
||||
; color &= 3
|
||||
; color <<= shift4c[cx16.r2L]
|
||||
when color & 3 {
|
||||
1 -> color = shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> color = shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> color = shiftedleft_4c_3[cx16.r2L]
|
||||
}
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1L
|
||||
@@ -743,13 +759,125 @@ _done
|
||||
sta cx16.r0L
|
||||
}}
|
||||
cx16.r1L = lsb(x) & 3
|
||||
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L] ; TODO with lookup table
|
||||
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L]
|
||||
return cx16.r0L & 3
|
||||
}
|
||||
else -> return 0
|
||||
}
|
||||
}
|
||||
|
||||
sub fill(word @zp x, word @zp y, ubyte new_color) {
|
||||
; Non-recursive scanline flood fill.
|
||||
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
|
||||
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
|
||||
const ubyte MAXDEPTH = 48
|
||||
word[MAXDEPTH] @split @shared stack_xl
|
||||
word[MAXDEPTH] @split @shared stack_xr
|
||||
word[MAXDEPTH] @split @shared stack_y
|
||||
byte[MAXDEPTH] @shared stack_dy
|
||||
cx16.r12L = 0 ; stack pointer
|
||||
word x1
|
||||
word x2
|
||||
byte dy
|
||||
cx16.r10L = new_color
|
||||
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
|
||||
if cx16.r12L==MAXDEPTH
|
||||
return
|
||||
cx16.r0s = sy+sdy
|
||||
if cx16.r0s>=0 and cx16.r0s<=height-1 {
|
||||
;; stack_xl[cx16.r12L] = sxl
|
||||
;; stack_xr[cx16.r12L] = sxr
|
||||
;; stack_y[cx16.r12L] = sy
|
||||
;; stack_dy[cx16.r12L] = sdy
|
||||
;; cx16.r12L++
|
||||
%asm {{
|
||||
ldy cx16.r12L
|
||||
lda sxl
|
||||
sta stack_xl_lsb,y
|
||||
lda sxl+1
|
||||
sta stack_xl_msb,y
|
||||
lda sxr
|
||||
sta stack_xr_lsb,y
|
||||
lda sxr+1
|
||||
sta stack_xr_msb,y
|
||||
lda sy
|
||||
sta stack_y_lsb,y
|
||||
lda sy+1
|
||||
sta stack_y_msb,y
|
||||
ldy cx16.r12L
|
||||
lda sdy
|
||||
sta stack_dy,y
|
||||
inc cx16.r12L
|
||||
}}
|
||||
}
|
||||
}
|
||||
sub pop_stack() {
|
||||
;; cx16.r12L--
|
||||
;; x1 = stack_xl[cx16.r12L]
|
||||
;; x2 = stack_xr[cx16.r12L]
|
||||
;; y = stack_y[cx16.r12L]
|
||||
;; dy = stack_dy[cx16.r12L]
|
||||
%asm {{
|
||||
dec cx16.r12L
|
||||
ldy cx16.r12L
|
||||
lda stack_xl_lsb,y
|
||||
sta x1
|
||||
lda stack_xl_msb,y
|
||||
sta x1+1
|
||||
lda stack_xr_lsb,y
|
||||
sta x2
|
||||
lda stack_xr_msb,y
|
||||
sta x2+1
|
||||
lda stack_y_lsb,y
|
||||
sta y
|
||||
lda stack_y_msb,y
|
||||
sta y+1
|
||||
ldy cx16.r12L
|
||||
lda stack_dy,y
|
||||
sta dy
|
||||
}}
|
||||
y+=dy
|
||||
}
|
||||
cx16.r11L = pget(x as uword, y as uword) ; old_color
|
||||
if cx16.r11L == cx16.r10L
|
||||
return
|
||||
if x<0 or x > width-1 or y<0 or y > height-1
|
||||
return
|
||||
push_stack(x, x, y, 1)
|
||||
push_stack(x, x, y + 1, -1)
|
||||
word left = 0
|
||||
while cx16.r12L {
|
||||
pop_stack()
|
||||
x = x1
|
||||
while x >= 0 and pget(x as uword, y as uword) == cx16.r11L {
|
||||
plot(x as uword, y as uword, cx16.r10L)
|
||||
x--
|
||||
}
|
||||
if x>= x1
|
||||
goto skip
|
||||
|
||||
left = x + 1
|
||||
if left < x1
|
||||
push_stack(left, x1 - 1, y, -dy)
|
||||
x = x1 + 1
|
||||
|
||||
do {
|
||||
while x <= width-1 and pget(x as uword, y as uword) == cx16.r11L {
|
||||
plot(x as uword, y as uword, cx16.r10L)
|
||||
x++
|
||||
}
|
||||
push_stack(left, x - 1, y, dy)
|
||||
if x > x2 + 1
|
||||
push_stack(x2 + 1, x - 1, y, -dy)
|
||||
skip:
|
||||
x++
|
||||
while x <= x2 and pget(x as uword, y as uword) != cx16.r11L
|
||||
x++
|
||||
left = x
|
||||
} until x>x2
|
||||
}
|
||||
}
|
||||
|
||||
sub position(uword @zp x, uword y) {
|
||||
ubyte bank
|
||||
when active_mode {
|
||||
|
||||
@@ -22,7 +22,7 @@ psg {
|
||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||
envelope_states[voice_num] = 255
|
||||
sys.set_irqd()
|
||||
sys.irqsafe_set_irqd()
|
||||
cx16.r0 = $f9c2 + voice_num * 4
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||
@@ -33,7 +33,7 @@ psg {
|
||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||
envelope_volumes[voice_num] = mkword(volume, 0)
|
||||
envelope_maxvolumes[voice_num] = volume
|
||||
sys.clear_irqd()
|
||||
sys.irqsafe_clear_irqd()
|
||||
}
|
||||
|
||||
; sub freq_hz(ubyte voice_num, float hertz) {
|
||||
@@ -48,7 +48,7 @@ psg {
|
||||
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
|
||||
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
|
||||
; Write freq MSB first and then LSB to reduce the chance on clicks
|
||||
sys.set_irqd()
|
||||
sys.irqsafe_set_irqd()
|
||||
cx16.r0 = $f9c1 + voice_num * 4
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||
@@ -57,7 +57,7 @@ psg {
|
||||
cx16.VERA_DATA0 = msb(vera_freq)
|
||||
cx16.VERA_ADDR_L--
|
||||
cx16.VERA_DATA0 = lsb(vera_freq)
|
||||
sys.clear_irqd()
|
||||
sys.irqsafe_clear_irqd()
|
||||
}
|
||||
|
||||
sub volume(ubyte voice_num, ubyte vol) {
|
||||
@@ -87,8 +87,9 @@ psg {
|
||||
envelope_attacks[voice_num] = attack
|
||||
envelope_sustains[voice_num] = sustain
|
||||
envelope_releases[voice_num] = release
|
||||
if maxvolume<envelope_volumes[voice_num]
|
||||
envelope_volumes[voice_num] = maxvolume
|
||||
cx16.r0 = mkword(maxvolume, 0)
|
||||
if cx16.r0<envelope_volumes[voice_num]
|
||||
envelope_volumes[voice_num] = cx16.r0
|
||||
envelope_maxvolumes[voice_num] = maxvolume
|
||||
envelope_states[voice_num] = 0
|
||||
}
|
||||
@@ -106,12 +107,11 @@ psg {
|
||||
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
|
||||
; or just install this routine as the only irq handler if you don't have to do other things there.
|
||||
; Example: cx16.set_irq(&psg.envelopes_irq, true)
|
||||
; NOTE: this routine calls save/restore_vera_context() for you, don't nest this!
|
||||
; NOTE: this routine calls save/restore_vera_context() for you, don't nest this or call it yourself!
|
||||
|
||||
; cx16.r0 = the volume word (volume scaled by 256)
|
||||
; cx16.r1L = the voice number
|
||||
; cx16.r2L = attack value
|
||||
|
||||
pushw(cx16.r0)
|
||||
push(cx16.r1L)
|
||||
push(cx16.r2L)
|
||||
@@ -171,7 +171,7 @@ psg {
|
||||
}
|
||||
|
||||
ubyte[16] envelope_states
|
||||
uword[16] envelope_volumes ; scaled by 256
|
||||
uword[16] @split envelope_volumes ; scaled by 256
|
||||
ubyte[16] envelope_attacks
|
||||
ubyte[16] envelope_sustains
|
||||
ubyte[16] envelope_releases
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
; Prog8 definitions for the CommanderX16
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
|
||||
c64 {
|
||||
cbm {
|
||||
|
||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
|
||||
; STROUT --> use txt.print
|
||||
; CLEARSCR -> use txt.clear_screen
|
||||
@@ -13,12 +13,12 @@ romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
@@ -30,23 +30,23 @@ romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- utility
|
||||
@@ -55,7 +55,7 @@ asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.STOP
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
plx
|
||||
lda #0
|
||||
@@ -67,10 +67,14 @@ asmsub STOP2() -> ubyte @A {
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs.
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.RDTIM
|
||||
php
|
||||
sei
|
||||
jsr cbm.RDTIM
|
||||
plp
|
||||
cli
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
@@ -85,37 +89,37 @@ asmsub RDTIM16() -> uword @AY {
|
||||
cx16 {
|
||||
|
||||
; irq, system and hardware vectors:
|
||||
&uword IERROR = $0300
|
||||
&uword IMAIN = $0302
|
||||
&uword ICRNCH = $0304
|
||||
&uword IQPLOP = $0306
|
||||
&uword IGONE = $0308
|
||||
&uword IEVAL = $030a
|
||||
&ubyte SAREG = $030c ; register storage for A for SYS calls
|
||||
&ubyte SXREG = $030d ; register storage for X for SYS calls
|
||||
&ubyte SYREG = $030e ; register storage for Y for SYS calls
|
||||
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
|
||||
&uword USRADD = $0311 ; vector for the USR() basic command
|
||||
&uword IERROR = $0300
|
||||
&uword IMAIN = $0302
|
||||
&uword ICRNCH = $0304
|
||||
&uword IQPLOP = $0306
|
||||
&uword IGONE = $0308
|
||||
&uword IEVAL = $030a
|
||||
&ubyte SAREG = $030c ; register storage for A for SYS calls
|
||||
&ubyte SXREG = $030d ; register storage for X for SYS calls
|
||||
&ubyte SYREG = $030e ; register storage for Y for SYS calls
|
||||
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
|
||||
&uword USRADD = $0311 ; vector for the USR() basic command
|
||||
; $0313 is unused.
|
||||
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||
&uword CBINV = $0316 ; BRK vector (in ram)
|
||||
&uword NMINV = $0318 ; NMI vector (in ram)
|
||||
&uword IOPEN = $031a
|
||||
&uword ICLOSE = $031c
|
||||
&uword ICHKIN = $031e
|
||||
&uword ICKOUT = $0320
|
||||
&uword ICLRCH = $0322
|
||||
&uword IBASIN = $0324
|
||||
&uword IBSOUT = $0326
|
||||
&uword ISTOP = $0328
|
||||
&uword IGETIN = $032a
|
||||
&uword ICLALL = $032c
|
||||
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
|
||||
&uword ILOAD = $0330
|
||||
&uword ISAVE = $0332
|
||||
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
|
||||
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||
&uword CBINV = $0316 ; BRK vector (in ram)
|
||||
&uword NMINV = $0318 ; NMI vector (in ram)
|
||||
&uword IOPEN = $031a
|
||||
&uword ICLOSE = $031c
|
||||
&uword ICHKIN = $031e
|
||||
&uword ICKOUT = $0320
|
||||
&uword ICLRCH = $0322
|
||||
&uword IBASIN = $0324
|
||||
&uword IBSOUT = $0326
|
||||
&uword ISTOP = $0328
|
||||
&uword IGETIN = $032a
|
||||
&uword ICLALL = $032c
|
||||
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
|
||||
&uword ILOAD = $0330
|
||||
&uword ISAVE = $0332
|
||||
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
|
||||
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
|
||||
@@ -271,44 +275,44 @@ cx16 {
|
||||
; I/O
|
||||
|
||||
const uword VIA1_BASE = $9f00 ;VIA 6522 #1
|
||||
&ubyte via1prb = VIA1_BASE + 0
|
||||
&ubyte via1pra = VIA1_BASE + 1
|
||||
&ubyte via1ddrb = VIA1_BASE + 2
|
||||
&ubyte via1ddra = VIA1_BASE + 3
|
||||
&ubyte via1t1l = VIA1_BASE + 4
|
||||
&ubyte via1t1h = VIA1_BASE + 5
|
||||
&ubyte via1t1ll = VIA1_BASE + 6
|
||||
&ubyte via1t1lh = VIA1_BASE + 7
|
||||
&ubyte via1t2l = VIA1_BASE + 8
|
||||
&ubyte via1t2h = VIA1_BASE + 9
|
||||
&ubyte via1sr = VIA1_BASE + 10
|
||||
&ubyte via1acr = VIA1_BASE + 11
|
||||
&ubyte via1pcr = VIA1_BASE + 12
|
||||
&ubyte via1ifr = VIA1_BASE + 13
|
||||
&ubyte via1ier = VIA1_BASE + 14
|
||||
&ubyte via1ora = VIA1_BASE + 15
|
||||
&ubyte via1prb = VIA1_BASE + 0
|
||||
&ubyte via1pra = VIA1_BASE + 1
|
||||
&ubyte via1ddrb = VIA1_BASE + 2
|
||||
&ubyte via1ddra = VIA1_BASE + 3
|
||||
&ubyte via1t1l = VIA1_BASE + 4
|
||||
&ubyte via1t1h = VIA1_BASE + 5
|
||||
&ubyte via1t1ll = VIA1_BASE + 6
|
||||
&ubyte via1t1lh = VIA1_BASE + 7
|
||||
&ubyte via1t2l = VIA1_BASE + 8
|
||||
&ubyte via1t2h = VIA1_BASE + 9
|
||||
&ubyte via1sr = VIA1_BASE + 10
|
||||
&ubyte via1acr = VIA1_BASE + 11
|
||||
&ubyte via1pcr = VIA1_BASE + 12
|
||||
&ubyte via1ifr = VIA1_BASE + 13
|
||||
&ubyte via1ier = VIA1_BASE + 14
|
||||
&ubyte via1ora = VIA1_BASE + 15
|
||||
|
||||
const uword VIA2_BASE = $9f10 ;VIA 6522 #2
|
||||
&ubyte via2prb = VIA2_BASE + 0
|
||||
&ubyte via2pra = VIA2_BASE + 1
|
||||
&ubyte via2ddrb = VIA2_BASE + 2
|
||||
&ubyte via2ddra = VIA2_BASE + 3
|
||||
&ubyte via2t1l = VIA2_BASE + 4
|
||||
&ubyte via2t1h = VIA2_BASE + 5
|
||||
&ubyte via2t1ll = VIA2_BASE + 6
|
||||
&ubyte via2t1lh = VIA2_BASE + 7
|
||||
&ubyte via2t2l = VIA2_BASE + 8
|
||||
&ubyte via2t2h = VIA2_BASE + 9
|
||||
&ubyte via2sr = VIA2_BASE + 10
|
||||
&ubyte via2acr = VIA2_BASE + 11
|
||||
&ubyte via2pcr = VIA2_BASE + 12
|
||||
&ubyte via2ifr = VIA2_BASE + 13
|
||||
&ubyte via2ier = VIA2_BASE + 14
|
||||
&ubyte via2ora = VIA2_BASE + 15
|
||||
&ubyte via2prb = VIA2_BASE + 0
|
||||
&ubyte via2pra = VIA2_BASE + 1
|
||||
&ubyte via2ddrb = VIA2_BASE + 2
|
||||
&ubyte via2ddra = VIA2_BASE + 3
|
||||
&ubyte via2t1l = VIA2_BASE + 4
|
||||
&ubyte via2t1h = VIA2_BASE + 5
|
||||
&ubyte via2t1ll = VIA2_BASE + 6
|
||||
&ubyte via2t1lh = VIA2_BASE + 7
|
||||
&ubyte via2t2l = VIA2_BASE + 8
|
||||
&ubyte via2t2h = VIA2_BASE + 9
|
||||
&ubyte via2sr = VIA2_BASE + 10
|
||||
&ubyte via2acr = VIA2_BASE + 11
|
||||
&ubyte via2pcr = VIA2_BASE + 12
|
||||
&ubyte via2ifr = VIA2_BASE + 13
|
||||
&ubyte via2ier = VIA2_BASE + 14
|
||||
&ubyte via2ora = VIA2_BASE + 15
|
||||
|
||||
; YM-2151 sound chip
|
||||
&ubyte YM_ADDRESS = $9f40
|
||||
&ubyte YM_DATA = $9f41
|
||||
&ubyte YM_DATA = $9f41
|
||||
|
||||
const uword extdev = $9f60
|
||||
|
||||
@@ -320,7 +324,7 @@ cx16 {
|
||||
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
|
||||
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
|
||||
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
|
||||
romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc
|
||||
romsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) clobbers(A, X, Y) -> bool @Pc
|
||||
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
|
||||
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
|
||||
romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
|
||||
@@ -337,12 +341,12 @@ romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
|
||||
romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
||||
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
|
||||
romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y)
|
||||
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, ubyte fill @Pc) clobbers(A,X,Y)
|
||||
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, bool fill @Pc) clobbers(A,X,Y)
|
||||
romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y)
|
||||
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, ubyte fill @Pc) clobbers(A,X,Y)
|
||||
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, bool fill @Pc) clobbers(A,X,Y)
|
||||
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
|
||||
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
|
||||
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y)
|
||||
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, bool is_control @Pc) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
|
||||
|
||||
@@ -364,16 +368,17 @@ romsub $ff1a = FB_filter_pixels(uword pointer @ R0, uword count @R1) clobbers(A
|
||||
romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y)
|
||||
|
||||
; misc
|
||||
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, ubyte @Pc
|
||||
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> ubyte @Pc
|
||||
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc
|
||||
romsub $FEBA = BSAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; like cbm.SAVE, but omits the 2-byte prg header
|
||||
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc
|
||||
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> bool @Pc
|
||||
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, bool apply_mask @Pc) clobbers(A,X,Y) -> bool @Pc
|
||||
romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y)
|
||||
romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y)
|
||||
romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y)
|
||||
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
|
||||
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
|
||||
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
||||
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y)
|
||||
romsub $fede = console_put_char(ubyte char @A, bool wrapping @Pc) clobbers(A,X,Y)
|
||||
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
|
||||
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
|
||||
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
|
||||
@@ -381,7 +386,7 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
|
||||
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
|
||||
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
||||
romsub $ff47 = enter_basic(bool cold_or_warm @Pc) clobbers(A,X,Y)
|
||||
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
|
||||
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
|
||||
|
||||
@@ -400,16 +405,16 @@ romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; altern
|
||||
|
||||
; Audio (bank 10)
|
||||
romsub $C04B = psg_init() clobbers(A,X,Y)
|
||||
romsub $C063 = ym_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)init YM chip
|
||||
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> ubyte @Pc ; load default YM patches
|
||||
romsub $C09F = audio_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)initialize PSG and YM audio chips
|
||||
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
|
||||
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
|
||||
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize PSG and YM audio chips
|
||||
; TODO: add more of the audio routines?
|
||||
|
||||
|
||||
asmsub kbdbuf_clear() {
|
||||
; -- convenience helper routine to clear the keyboard buffer
|
||||
%asm {{
|
||||
- jsr c64.GETIN
|
||||
- jsr cbm.GETIN
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
@@ -482,7 +487,7 @@ asmsub numbanks() -> uword @AY {
|
||||
%asm {{
|
||||
phx
|
||||
sec
|
||||
jsr c64.MEMTOP
|
||||
jsr cbm.MEMTOP
|
||||
ldy #0
|
||||
cmp #0
|
||||
bne +
|
||||
@@ -624,7 +629,62 @@ asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- system stuff -----
|
||||
asmsub save_vera_context() clobbers(A) {
|
||||
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||
%asm {{
|
||||
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||
lda cx16.VERA_ADDR_L
|
||||
sta _vera_storage
|
||||
lda cx16.VERA_ADDR_M
|
||||
sta _vera_storage+1
|
||||
lda cx16.VERA_ADDR_H
|
||||
sta _vera_storage+2
|
||||
lda cx16.VERA_CTRL
|
||||
sta _vera_storage+3
|
||||
eor #1
|
||||
sta _vera_storage+7
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.VERA_ADDR_L
|
||||
sta _vera_storage+4
|
||||
lda cx16.VERA_ADDR_M
|
||||
sta _vera_storage+5
|
||||
lda cx16.VERA_ADDR_H
|
||||
sta _vera_storage+6
|
||||
rts
|
||||
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_vera_context() clobbers(A) {
|
||||
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
||||
%asm {{
|
||||
lda cx16.save_vera_context._vera_storage+7
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.save_vera_context._vera_storage+6
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.save_vera_context._vera_storage+5
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.save_vera_context._vera_storage+4
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.save_vera_context._vera_storage+3
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.save_vera_context._vera_storage+2
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.save_vera_context._vera_storage+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.save_vera_context._vera_storage+0
|
||||
sta cx16.VERA_ADDR_L
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
@@ -635,29 +695,29 @@ asmsub init_system() {
|
||||
tay
|
||||
jsr cx16.mouse_config ; disable mouse
|
||||
cld
|
||||
lda VERA_DC_VIDEO
|
||||
lda cx16.VERA_DC_VIDEO
|
||||
and #%00000111 ; retain chroma + output mode
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda #$0a
|
||||
sta $01 ; rom bank 10 (audio)
|
||||
jsr audio_init ; silence
|
||||
jsr cx16.audio_init ; silence
|
||||
stz $01 ; rom bank 0 (kernal)
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
lda VERA_DC_VIDEO
|
||||
jsr cbm.IOINIT
|
||||
jsr cbm.RESTOR
|
||||
jsr cbm.CINT
|
||||
lda cx16.VERA_DC_VIDEO
|
||||
and #%11111000
|
||||
ora P8ZP_SCRATCH_REG
|
||||
sta VERA_DC_VIDEO ; restore old output mode
|
||||
sta cx16.VERA_DC_VIDEO ; restore old output mode
|
||||
lda #$90 ; black
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
lda #1
|
||||
sta $00 ; select ram bank 1
|
||||
jsr c64.CHROUT ; swap fg/bg
|
||||
jsr cbm.CHROUT ; swap fg/bg
|
||||
lda #$9e ; yellow
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
lda #147 ; clear screen
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
lda #0
|
||||
tax
|
||||
tay
|
||||
@@ -692,12 +752,12 @@ asmsub cleanup_at_exit() {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
rol a
|
||||
sta _use_kernal
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
@@ -771,57 +831,6 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub save_vera_context() clobbers(A) {
|
||||
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||
%asm {{
|
||||
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||
lda cx16.VERA_ADDR_L
|
||||
sta _vera_storage
|
||||
lda cx16.VERA_ADDR_M
|
||||
sta _vera_storage+1
|
||||
lda cx16.VERA_ADDR_H
|
||||
sta _vera_storage+2
|
||||
lda cx16.VERA_CTRL
|
||||
sta _vera_storage+3
|
||||
eor #1
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.VERA_ADDR_L
|
||||
sta _vera_storage+4
|
||||
lda cx16.VERA_ADDR_M
|
||||
sta _vera_storage+5
|
||||
lda cx16.VERA_ADDR_H
|
||||
sta _vera_storage+6
|
||||
lda cx16.VERA_CTRL
|
||||
sta _vera_storage+7
|
||||
rts
|
||||
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_vera_context() clobbers(A) {
|
||||
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
||||
%asm {{
|
||||
lda cx16.save_vera_context._vera_storage+7
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.save_vera_context._vera_storage+6
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.save_vera_context._vera_storage+5
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.save_vera_context._vera_storage+4
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.save_vera_context._vera_storage+3
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.save_vera_context._vera_storage+2
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.save_vera_context._vera_storage+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.save_vera_context._vera_storage+0
|
||||
sta cx16.VERA_ADDR_L
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
@@ -891,15 +900,6 @@ asmsub set_rasterline(uword line @AY) {
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
; We do this via the SMC so that a true reset is performed that also resets the Vera fully.
|
||||
@@ -926,6 +926,7 @@ sys {
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||
; note: this routine cannot be used from inside a irq handler
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -937,9 +938,13 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
plx
|
||||
rts
|
||||
|
||||
+ jsr c64.RDTIM
|
||||
+ sei
|
||||
jsr cbm.RDTIM
|
||||
cli
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- jsr c64.RDTIM
|
||||
- sei
|
||||
jsr cbm.RDTIM
|
||||
cli
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
@@ -1079,10 +1084,23 @@ _longcopy
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_set_irqd() {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub irqsafe_clear_irqd() {
|
||||
%asm {{
|
||||
plp
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
jsr c64.CLRCHN ; reset i/o channels
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
|
||||
@@ -33,10 +33,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||
%asm {{
|
||||
sec
|
||||
jsr c64.PLOT
|
||||
jsr cbm.PLOT
|
||||
tay
|
||||
clc
|
||||
jmp c64.PLOT
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
sty _ly+1
|
||||
phx
|
||||
pha
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
@@ -94,7 +94,7 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
@@ -125,7 +125,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
%asm {{
|
||||
phx
|
||||
sta _la+1
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
@@ -158,35 +158,35 @@ ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
txtcol &= 15
|
||||
c64.CHROUT(color_to_charcode[txtcol])
|
||||
cbm.CHROUT(color_to_charcode[txtcol])
|
||||
}
|
||||
|
||||
sub color2 (ubyte txtcol, ubyte bgcol) {
|
||||
txtcol &= 15
|
||||
bgcol &= 15
|
||||
c64.CHROUT(color_to_charcode[bgcol])
|
||||
c64.CHROUT(1) ; switch fg and bg colors
|
||||
c64.CHROUT(color_to_charcode[txtcol])
|
||||
cbm.CHROUT(color_to_charcode[bgcol])
|
||||
cbm.CHROUT(1) ; switch fg and bg colors
|
||||
cbm.CHROUT(color_to_charcode[txtcol])
|
||||
}
|
||||
|
||||
sub lowercase() {
|
||||
c64.CHROUT($0e)
|
||||
cbm.CHROUT($0e)
|
||||
; this is not 100% compatible: cx16.screen_set_charset(3, 0) ; lowercase petscii charset
|
||||
}
|
||||
|
||||
sub uppercase() {
|
||||
c64.CHROUT($8e)
|
||||
cbm.CHROUT($8e)
|
||||
; this is not 100% compatible: cx16.screen_set_charset(2, 0) ; uppercase petscii charset
|
||||
}
|
||||
|
||||
sub iso() {
|
||||
c64.CHROUT($0f)
|
||||
cbm.CHROUT($0f)
|
||||
; This doesn't enable it completely: cx16.screen_set_charset(1, 0) ; iso charset
|
||||
}
|
||||
|
||||
sub iso_off() {
|
||||
; -- you have to call this first when switching back from iso charset to regular charset.
|
||||
c64.CHROUT($8f)
|
||||
cbm.CHROUT($8f)
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ asmsub scroll_left() clobbers(A, Y) {
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
dey
|
||||
@@ -241,7 +241,7 @@ asmsub scroll_right() clobbers(A) {
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
txa
|
||||
@@ -295,7 +295,7 @@ asmsub scroll_up() clobbers(A, Y) {
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1
|
||||
@@ -345,7 +345,7 @@ asmsub scroll_down() clobbers(A, Y) {
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1
|
||||
@@ -396,20 +396,20 @@ _nextline
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
; by just one call to cbm.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
@@ -423,11 +423,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
@@ -443,16 +443,16 @@ _print_byte_digits
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
bra _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
@@ -466,45 +466,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
bra print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
phx
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr c64.CHROUT
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
plx
|
||||
@@ -512,7 +512,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
@@ -524,7 +524,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -545,7 +545,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ plx
|
||||
@@ -568,14 +568,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp c64.CHROUT
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
@@ -607,7 +607,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr c64.CHRIN
|
||||
- jsr cbm.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
@@ -769,7 +769,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
phx
|
||||
tax
|
||||
clc
|
||||
jsr c64.PLOT
|
||||
jsr cbm.PLOT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
@@ -778,7 +778,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
; -- returns the text screen width (number of columns)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
txa
|
||||
rts
|
||||
}}
|
||||
@@ -787,7 +787,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||
; -- returns the text screen height (number of rows)
|
||||
%asm {{
|
||||
jsr c64.SCREEN
|
||||
jsr cbm.SCREEN
|
||||
tya
|
||||
rts
|
||||
}}
|
||||
|
||||
+145
-168
@@ -1,4 +1,4 @@
|
||||
; C64 and Cx16 disk drive I/O routines.
|
||||
; C64/C128 disk drive I/O routines.
|
||||
|
||||
%import textio
|
||||
%import string
|
||||
@@ -6,54 +6,60 @@
|
||||
|
||||
diskio {
|
||||
|
||||
sub directory(ubyte drivenumber) -> bool {
|
||||
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
|
||||
ubyte drivenumber = 8
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(12, drivenumber, 0)
|
||||
sub set_drive(ubyte number) {
|
||||
drivenumber = number
|
||||
}
|
||||
|
||||
sub directory() -> bool {
|
||||
; -- Prints the directory contents to the screen. Returns success.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
ubyte status = 1
|
||||
void c64.OPEN() ; open 12,8,0,"$"
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(12) ; use #12 as input channel
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
}
|
||||
|
||||
; while not stop key pressed / EOF encountered, read data.
|
||||
status = c64.READST()
|
||||
status = cbm.READST()
|
||||
if status!=0 {
|
||||
status = 1
|
||||
goto io_error
|
||||
}
|
||||
|
||||
while status==0 {
|
||||
ubyte low = c64.CHRIN()
|
||||
ubyte high = c64.CHRIN()
|
||||
ubyte low = cbm.CHRIN()
|
||||
ubyte high = cbm.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.spc()
|
||||
ubyte @zp char
|
||||
repeat {
|
||||
char = c64.CHRIN()
|
||||
char = cbm.CHRIN()
|
||||
if char==0
|
||||
break
|
||||
txt.chrout(char)
|
||||
}
|
||||
txt.nl()
|
||||
void c64.CHRIN() ; skip 2 bytes
|
||||
void c64.CHRIN()
|
||||
status = c64.READST()
|
||||
if c64.STOP2()
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
void cbm.CHRIN()
|
||||
status = cbm.READST()
|
||||
if cbm.STOP2()
|
||||
break
|
||||
}
|
||||
status = c64.READST()
|
||||
status = cbm.READST()
|
||||
|
||||
io_error:
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(12)
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(12)
|
||||
|
||||
if status and status & $40 == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
@@ -65,28 +71,28 @@ io_error:
|
||||
return true
|
||||
}
|
||||
|
||||
sub diskname(ubyte drivenumber) -> uword {
|
||||
sub diskname() -> uword {
|
||||
; -- Returns pointer to disk name string or 0 if failure.
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(12, drivenumber, 0)
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
ubyte okay = false
|
||||
void c64.OPEN() ; open 12,8,0,"$"
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(12) ; use #12 as input channel
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 6 {
|
||||
void c64.CHRIN() ; skip the 6 prologue bytes
|
||||
void cbm.CHRIN() ; skip the 6 prologue bytes
|
||||
}
|
||||
if c64.READST()!=0
|
||||
if cbm.READST()!=0
|
||||
goto io_error
|
||||
|
||||
cx16.r0 = &list_filename
|
||||
repeat {
|
||||
@(cx16.r0) = c64.CHRIN()
|
||||
@(cx16.r0) = cbm.CHRIN()
|
||||
if @(cx16.r0)==0
|
||||
break
|
||||
cx16.r0++
|
||||
@@ -94,8 +100,8 @@ io_error:
|
||||
okay = true
|
||||
|
||||
io_error:
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(12)
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(12)
|
||||
if okay
|
||||
return &list_filename
|
||||
return 0
|
||||
@@ -106,13 +112,12 @@ io_error:
|
||||
uword list_pattern
|
||||
uword list_blocks
|
||||
bool iteration_in_progress = false
|
||||
ubyte last_drivenumber = 8 ; which drive was last used for a f_open operation?
|
||||
str list_filetype = "???" ; prg, seq, dir
|
||||
str list_filename = "?" * 50
|
||||
|
||||
; ----- get a list of files (uses iteration functions internally) -----
|
||||
|
||||
sub list_filenames(ubyte drivenumber, uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
@@ -120,10 +125,10 @@ io_error:
|
||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||
uword buffer_start = filenames_buffer
|
||||
ubyte files_found = 0
|
||||
if lf_start_list(drivenumber, pattern_ptr) {
|
||||
if lf_start_list(pattern_ptr) {
|
||||
while lf_next_entry() {
|
||||
if list_filetype!="dir" {
|
||||
filenames_buffer += string.copy(diskio.list_filename, filenames_buffer) + 1
|
||||
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
|
||||
files_found++
|
||||
if filenames_buffer - buffer_start > filenames_buf_size-20 {
|
||||
@(filenames_buffer)=0
|
||||
@@ -142,7 +147,7 @@ io_error:
|
||||
|
||||
; ----- iterative file lister functions (uses io channel 12) -----
|
||||
|
||||
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> bool {
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
lf_end_list()
|
||||
@@ -150,20 +155,20 @@ io_error:
|
||||
list_skip_disk_name = true
|
||||
iteration_in_progress = true
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(12, drivenumber, 0)
|
||||
void c64.OPEN() ; open 12,8,0,"$"
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(12) ; use #12 as input channel
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
||||
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||
}
|
||||
|
||||
if c64.READST()==0
|
||||
if cbm.READST()==0
|
||||
return true
|
||||
|
||||
io_error:
|
||||
@@ -180,26 +185,26 @@ io_error:
|
||||
return false
|
||||
|
||||
repeat {
|
||||
void c64.CHKIN(12) ; use #12 as input channel again
|
||||
void cbm.CHKIN(12) ; use #12 as input channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = c64.CHRIN()
|
||||
ubyte blocks_msb = c64.CHRIN()
|
||||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
ubyte blocks_msb = cbm.CHRIN()
|
||||
|
||||
if c64.READST()
|
||||
if cbm.READST()
|
||||
goto close_end
|
||||
|
||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||
|
||||
; read until the filename starts after the first "
|
||||
while c64.CHRIN()!='\"' {
|
||||
if c64.READST()
|
||||
while cbm.CHRIN()!='\"' {
|
||||
if cbm.READST()
|
||||
goto close_end
|
||||
}
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = c64.CHRIN()
|
||||
ubyte char = cbm.CHRIN()
|
||||
if char==0
|
||||
break
|
||||
if char=='\"'
|
||||
@@ -211,17 +216,17 @@ io_error:
|
||||
@(nameptr) = 0
|
||||
|
||||
do {
|
||||
cx16.r15L = c64.CHRIN()
|
||||
cx16.r15L = cbm.CHRIN()
|
||||
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
|
||||
list_filetype[0] = cx16.r15L
|
||||
list_filetype[1] = c64.CHRIN()
|
||||
list_filetype[2] = c64.CHRIN()
|
||||
while c64.CHRIN() {
|
||||
list_filetype[1] = cbm.CHRIN()
|
||||
list_filetype[2] = cbm.CHRIN()
|
||||
while cbm.CHRIN() {
|
||||
; read the rest of the entry until the end
|
||||
}
|
||||
|
||||
void c64.CHRIN() ; skip 2 bytes
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
void cbm.CHRIN()
|
||||
|
||||
if not list_skip_disk_name {
|
||||
if not list_pattern
|
||||
@@ -240,8 +245,8 @@ close_end:
|
||||
sub lf_end_list() {
|
||||
; -- end an iterative file listing session (close channels).
|
||||
if iteration_in_progress {
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(12)
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
@@ -249,25 +254,24 @@ close_end:
|
||||
|
||||
; ----- iterative file loader functions (uses io channel 12) -----
|
||||
|
||||
sub f_open(ubyte drivenumber, uword filenameptr) -> bool {
|
||||
sub f_open(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative reading with f_read
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
f_close()
|
||||
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
c64.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
|
||||
last_drivenumber = drivenumber
|
||||
void c64.OPEN() ; open 12,8,12,"filename"
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
|
||||
void cbm.OPEN() ; open 12,8,12,"filename"
|
||||
if_cc {
|
||||
if c64.READST()==0 {
|
||||
if cbm.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
void c64.CHKIN(12) ; use #12 as input channel
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
if_cc {
|
||||
void c64.CHRIN() ; read first byte to test for file not found
|
||||
if not c64.READST() {
|
||||
c64.CLOSE(12) ; close file because we already consumed first byte
|
||||
void c64.OPEN() ; re-open the file
|
||||
void c64.CHKIN(12)
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if not cbm.READST() {
|
||||
cbm.CLOSE(12) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
void cbm.CHKIN(12)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -280,9 +284,6 @@ close_end:
|
||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
; NOTE: on systems with banked ram (such as Commander X16) this routine DOES NOT
|
||||
; automatically load into subsequent banks if it reaches a bank boundary!
|
||||
; Consider using cx16diskio.f_read() on X16.
|
||||
if not iteration_in_progress or not num_bytes
|
||||
return 0
|
||||
|
||||
@@ -295,14 +296,14 @@ close_end:
|
||||
sta m_in_buffer+2
|
||||
}}
|
||||
while num_bytes {
|
||||
if c64.READST() {
|
||||
if cbm.READST() {
|
||||
f_close()
|
||||
if c64.READST() & $40 ; eof?
|
||||
if cbm.READST() & $40 ; eof?
|
||||
return list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
%asm {{
|
||||
jsr c64.CHRIN
|
||||
jsr cbm.CHRIN
|
||||
m_in_buffer sta $ffff
|
||||
inc m_in_buffer+1
|
||||
bne +
|
||||
@@ -317,12 +318,11 @@ m_in_buffer sta $ffff
|
||||
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
; Note: Consider using cx16diskio.f_read_all() on X16!
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
uword total_read = 0
|
||||
while not c64.READST() {
|
||||
while not cbm.READST() {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
@@ -340,9 +340,9 @@ m_in_buffer sta $ffff
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx #12
|
||||
jsr c64.CHKIN ; use channel 12 again for input
|
||||
jsr cbm.CHKIN ; use channel 12 again for input
|
||||
ldy #0
|
||||
_loop jsr c64.CHRIN
|
||||
_loop jsr cbm.CHRIN
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
beq _end
|
||||
iny
|
||||
@@ -357,12 +357,11 @@ _end rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub f_close() {
|
||||
; -- end an iterative file loading session (close channels).
|
||||
if iteration_in_progress {
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(12)
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
@@ -370,16 +369,16 @@ _end rts
|
||||
|
||||
; ----- iterative file writing functions (uses io channel 13) -----
|
||||
|
||||
sub f_open_w(ubyte drivenumber, uword filenameptr) -> bool {
|
||||
sub f_open_w(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative writing with f_write
|
||||
f_close_w()
|
||||
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
c64.SETLFS(13, drivenumber, 1)
|
||||
void c64.OPEN() ; open 13,8,1,"filename"
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(13, drivenumber, 1)
|
||||
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||
if_cc {
|
||||
c64.CHKOUT(13) ; use #13 as output channel
|
||||
return not c64.READST()
|
||||
cbm.CHKOUT(13) ; use #13 as output channel
|
||||
return not cbm.READST()
|
||||
}
|
||||
f_close_w()
|
||||
return false
|
||||
@@ -388,39 +387,39 @@ _end rts
|
||||
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||
; -- write the given number of bytes to the currently open file
|
||||
if num_bytes!=0 {
|
||||
c64.CHKOUT(13) ; use #13 as output channel again
|
||||
cbm.CHKOUT(13) ; use #13 as output channel again
|
||||
repeat num_bytes {
|
||||
c64.CHROUT(@(bufferpointer))
|
||||
cbm.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
}
|
||||
return not c64.READST()
|
||||
return not cbm.READST()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub f_close_w() {
|
||||
; -- end an iterative file writing session (close channels).
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(13)
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(13)
|
||||
}
|
||||
|
||||
|
||||
; ---- other functions ----
|
||||
|
||||
sub status(ubyte drivenumber) -> uword {
|
||||
sub status() -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
uword messageptr = &list_filename
|
||||
c64.SETNAM(0, list_filename)
|
||||
c64.SETLFS(15, drivenumber, 15)
|
||||
void c64.OPEN() ; open 15,8,15
|
||||
cbm.SETNAM(0, list_filename)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN() ; open 15,8,15
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(15) ; use #15 as input channel
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while not c64.READST() {
|
||||
cx16.r5L = c64.CHRIN()
|
||||
while not cbm.READST() {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
@(messageptr) = cx16.r5L
|
||||
@@ -429,8 +428,8 @@ _end rts
|
||||
@(messageptr) = 0
|
||||
|
||||
done:
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(15)
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(15)
|
||||
return list_filename
|
||||
|
||||
io_error:
|
||||
@@ -438,9 +437,9 @@ io_error:
|
||||
goto done
|
||||
}
|
||||
|
||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> bool {
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
c64.SETLFS(1, drivenumber, 0)
|
||||
sub save(uword filenameptr, uword address, uword size) -> bool {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = address + size
|
||||
cx16.r0L = 0
|
||||
|
||||
@@ -453,17 +452,17 @@ io_error:
|
||||
lda #<P8ZP_SCRATCH_W1
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
jsr c64.SAVE
|
||||
jsr cbm.SAVE
|
||||
php
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
plp
|
||||
}}
|
||||
|
||||
if_cc
|
||||
cx16.r0L = c64.READST()==0
|
||||
cx16.r0L = cbm.READST()==0
|
||||
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
|
||||
return cx16.r0L
|
||||
}
|
||||
@@ -474,99 +473,77 @@ io_error:
|
||||
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
||||
; and the rest is loaded at the given location in memory.
|
||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
|
||||
; (which is possible on the Commander X16), the returned size is not correct,
|
||||
; because it doesn't take the number of ram banks into account.
|
||||
; Also consider using cx16diskio.load() instead on the Commander X16.
|
||||
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
|
||||
return internal_load_routine(drivenumber, filenameptr, address_override, false)
|
||||
}
|
||||
|
||||
; Use kernal LOAD routine to load the given file in memory.
|
||||
; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
|
||||
; This is different from Basic's LOAD instruction which always skips the first two bytes.
|
||||
; The load address is mandatory.
|
||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
|
||||
; (which is possible on the Commander X16), the returned size is not correct,
|
||||
; because it doesn't take the number of ram banks into account.
|
||||
; Also consider using cx16diskio.load_raw() instead on the Commander X16.
|
||||
sub load_raw(ubyte drivenumber, uword filenameptr, uword address) -> uword {
|
||||
if sys.target==16 ; are we on commander X16?
|
||||
return internal_load_routine(drivenumber, filenameptr, address, true)
|
||||
; fallback to reading the 2 header bytes separately
|
||||
if not f_open(drivenumber, filenameptr)
|
||||
return 0
|
||||
cx16.r1 = f_read(address, 2)
|
||||
f_close()
|
||||
if cx16.r1!=2
|
||||
return 0
|
||||
address += 2
|
||||
return load(drivenumber, filenameptr, address)
|
||||
}
|
||||
|
||||
|
||||
; Internal routine, only to be used on Commander X16 platform if headerless=true,
|
||||
; because this routine uses kernal support for that to load headerless files.
|
||||
; On C64 it will always be called with headerless=false.
|
||||
sub internal_load_routine(ubyte drivenumber, uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
sub load(uword filenameptr, uword address_override) -> uword {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
cx16.r1 = 0
|
||||
if address_override
|
||||
secondary = 0
|
||||
if headerless
|
||||
secondary |= %00000010 ; activate cx16 kernal headerless load support
|
||||
c64.SETLFS(1, drivenumber, secondary)
|
||||
cbm.SETLFS(1, drivenumber, secondary)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
ldx address_override
|
||||
ldy address_override+1
|
||||
jsr c64.LOAD
|
||||
jsr cbm.LOAD
|
||||
bcs +
|
||||
stx cx16.r1
|
||||
sty cx16.r1+1
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
}}
|
||||
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
return cx16.r1
|
||||
}
|
||||
|
||||
sub delete(ubyte drivenumber, uword filenameptr) {
|
||||
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||
; No program header is assumed in the file. Everything is loaded.
|
||||
; See comments on load() for more details.
|
||||
sub load_raw(uword filenameptr, uword address) -> uword {
|
||||
; read the 2 header bytes separately to skip them
|
||||
if not f_open(filenameptr)
|
||||
return 0
|
||||
cx16.r1 = f_read(address, 2)
|
||||
f_close()
|
||||
if cx16.r1!=2
|
||||
return 0
|
||||
address += 2
|
||||
return load(filenameptr, address)
|
||||
}
|
||||
|
||||
sub delete(uword filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
list_filename[0] = 's'
|
||||
list_filename[1] = ':'
|
||||
ubyte flen = string.copy(filenameptr, &list_filename+2)
|
||||
c64.SETNAM(flen+2, list_filename)
|
||||
c64.SETLFS(1, drivenumber, 15)
|
||||
void c64.OPEN()
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
cbm.SETNAM(flen+2, list_filename)
|
||||
cbm.SETLFS(1, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
||||
sub rename(uword oldfileptr, uword newfileptr) {
|
||||
; -- rename a file on the drive
|
||||
list_filename[0] = 'r'
|
||||
list_filename[1] = ':'
|
||||
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
|
||||
list_filename[flen_new+2] = '='
|
||||
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
|
||||
c64.SETNAM(3+flen_new+flen_old, list_filename)
|
||||
c64.SETLFS(1, drivenumber, 15)
|
||||
void c64.OPEN()
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
cbm.SETNAM(3+flen_new+flen_old, list_filename)
|
||||
cbm.SETLFS(1, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(1)
|
||||
}
|
||||
|
||||
sub send_command(ubyte drivenumber, uword commandptr) {
|
||||
sub send_command(uword commandptr) {
|
||||
; -- send a dos command to the drive
|
||||
c64.SETNAM(string.length(commandptr), commandptr)
|
||||
c64.SETLFS(15, drivenumber, 15)
|
||||
void c64.OPEN()
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(15)
|
||||
cbm.SETNAM(string.length(commandptr), commandptr)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(15)
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ sub print_f(float value) {
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx floats_store_reg
|
||||
@@ -39,18 +39,6 @@ sub pow(float value, float power) -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
sub fabs(float value) -> float {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
jsr ABS
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub sin(float angle) -> float {
|
||||
%asm {{
|
||||
lda #<angle
|
||||
@@ -128,18 +116,6 @@ sub log2(float value) -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
sub sqrt(float value) -> float {
|
||||
%asm {{
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr SQR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub rad(float angle) -> float {
|
||||
; -- convert degrees to radians (d * pi / 180)
|
||||
%asm {{
|
||||
@@ -237,4 +213,27 @@ sub rndseedf(float seed) {
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub minf(float f1, float f2) -> float {
|
||||
if f1<f2
|
||||
return f1
|
||||
return f2
|
||||
}
|
||||
|
||||
|
||||
sub maxf(float f1, float f2) -> float {
|
||||
if f1>f2
|
||||
return f1
|
||||
return f2
|
||||
}
|
||||
|
||||
|
||||
sub clampf(float value, float minimum, float maximum) -> float {
|
||||
if value>maximum
|
||||
value=maximum
|
||||
if value>minimum
|
||||
return value
|
||||
return minimum
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,16 +86,15 @@ func_all_w_stack .proc
|
||||
|
||||
abs_b_stack .proc
|
||||
; -- push abs(A) on stack (as unsigned word)
|
||||
jsr abs_b_into_AY
|
||||
jsr abs_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
stz P8ESTACK_HI,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_b_into_AY .proc
|
||||
; -- AY = abs(A) (abs always returns unsigned word)
|
||||
ldy #0
|
||||
abs_b_into_A .proc
|
||||
; -- A = abs(A)
|
||||
cmp #0
|
||||
bmi +
|
||||
rts
|
||||
@@ -556,3 +555,111 @@ func_pokew .proc
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
func_clamp_byte .proc
|
||||
; signed value in A, result in A
|
||||
; minimum in P8ZP_SCRATCH_W1
|
||||
; maximum in P8ZP_SCRATCH_W1+1
|
||||
tay
|
||||
sec
|
||||
sbc P8ZP_SCRATCH_W1+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
tay
|
||||
jmp ++
|
||||
+ tya
|
||||
+ sec
|
||||
sbc P8ZP_SCRATCH_W1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
tya
|
||||
rts
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
func_clamp_ubyte .proc
|
||||
; value in A, result in A
|
||||
; minimum in P8ZP_SCRATCH_W1
|
||||
; maximum in P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W1+1
|
||||
bcc +
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
+ cmp P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
rts
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_clamp_word .proc
|
||||
; signed value in AY, result in AY
|
||||
; minimum in P8ZP_SCRATCH_W1
|
||||
; maximum in P8ZP_SCRATCH_W2
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_REG
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl +
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
+ ldy P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_REG
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl +
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
+ ldy P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_clamp_uword .proc
|
||||
; value in AY, result in AY
|
||||
; minimum in P8ZP_SCRATCH_W1
|
||||
; maximum in P8ZP_SCRATCH_W2
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
cpy P8ZP_SCRATCH_W2+1
|
||||
bcc ++
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bcc ++
|
||||
+ beq +
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
+ ldy P8ZP_SCRATCH_REG
|
||||
lda P8ZP_SCRATCH_B1
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bcc ++
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
bcc ++
|
||||
+ beq +
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
+ ldy P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
rts
|
||||
|
||||
.pend
|
||||
@@ -127,7 +127,7 @@ _startloop dey
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc {
|
||||
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, bool @Pc {
|
||||
; Locates the first position of the given character in the string,
|
||||
; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
|
||||
%asm {{
|
||||
@@ -292,7 +292,7 @@ str = P8ZP_SCRATCH_W1
|
||||
sta modify_pattern2+2
|
||||
jsr _match
|
||||
lda #0
|
||||
adc #0
|
||||
rol a
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
|
||||
@@ -197,9 +197,7 @@ sub str2uword(str string) -> uword {
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%ir {{
|
||||
loadm.w r65535,conv.str2uword.string
|
||||
setparam.w r65535,0
|
||||
syscall 11
|
||||
pop.w r0
|
||||
syscall 11 (r65535.w) : r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
@@ -210,9 +208,7 @@ sub str2word(str string) -> word {
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%ir {{
|
||||
loadm.w r65535,conv.str2word.string
|
||||
setparam.w r65535,0
|
||||
syscall 12
|
||||
pop.w r0
|
||||
syscall 12 (r65535.w) : r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ sub print_f(float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%ir {{
|
||||
loadm.f fr65535,floats.print_f.value
|
||||
setparam.f fr65535,0
|
||||
syscall 25
|
||||
syscall 25 (fr65535.f)
|
||||
return
|
||||
}}
|
||||
}
|
||||
@@ -26,14 +25,6 @@ sub pow(float value, float power) -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
sub fabs(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.fabs.value
|
||||
fabs.f fr0,fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub sin(float angle) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.sin.angle
|
||||
@@ -82,14 +73,6 @@ sub log2(float value) -> float {
|
||||
}}
|
||||
}
|
||||
|
||||
sub sqrt(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.sqrt.value
|
||||
sqrt.f fr0,fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rad(float angle) -> float {
|
||||
; -- convert degrees to radians (d * pi / 180)
|
||||
return angle * PI / 180.0
|
||||
@@ -127,8 +110,7 @@ sub ceil(float value) -> float {
|
||||
|
||||
sub rndf() -> float {
|
||||
%ir {{
|
||||
syscall 35
|
||||
pop.f fr0
|
||||
syscall 35 () : fr0.f
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
@@ -136,9 +118,32 @@ sub rndf() -> float {
|
||||
sub rndseedf(float seed) {
|
||||
%ir {{
|
||||
loadm.f fr65535,floats.rndseedf.seed
|
||||
setparam.f fr65535,0
|
||||
syscall 32
|
||||
syscall 32 (fr65535.f)
|
||||
return
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub minf(float f1, float f2) -> float {
|
||||
if f1<f2
|
||||
return f1
|
||||
return f2
|
||||
}
|
||||
|
||||
|
||||
sub maxf(float f1, float f2) -> float {
|
||||
if f1>f2
|
||||
return f1
|
||||
return f2
|
||||
}
|
||||
|
||||
|
||||
sub clampf(float value, float minimum, float maximum) -> float {
|
||||
if value<minimum
|
||||
value=minimum
|
||||
if value<maximum
|
||||
return value
|
||||
return maximum
|
||||
}
|
||||
|
||||
}
|
||||
@@ -161,16 +161,14 @@ math {
|
||||
|
||||
sub rnd() -> ubyte {
|
||||
%ir {{
|
||||
syscall 33
|
||||
pop.b r0
|
||||
syscall 33 (): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndw() -> uword {
|
||||
%ir {{
|
||||
syscall 34
|
||||
pop.w r0
|
||||
syscall 34 (): r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
@@ -178,11 +176,9 @@ math {
|
||||
sub rndseed(uword seed1, uword seed2) {
|
||||
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
|
||||
%ir {{
|
||||
loadm.w r65535,math.rndseed.seed1
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,math.rndseed.seed1
|
||||
loadm.w r65535,math.rndseed.seed2
|
||||
setparam.w r65535,1
|
||||
syscall 31
|
||||
syscall 31 (r65534.w, r65535.w)
|
||||
return
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -84,12 +84,9 @@ string {
|
||||
; Note that you can also directly compare strings and string values with eachother using
|
||||
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
|
||||
%ir {{
|
||||
loadm.w r65535,string.compare.st1
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,string.compare.st1
|
||||
loadm.w r65535,string.compare.st2
|
||||
setparam.w r65535,1
|
||||
syscall 29
|
||||
pop.b r0
|
||||
syscall 29 (r65534.w, r65535.w) : r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ sys {
|
||||
sub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%ir {{
|
||||
syscall 0
|
||||
syscall 0 ()
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,14 @@ sys {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
||||
%ir {{
|
||||
loadm.w r65535,sys.wait.jiffies
|
||||
setparam.w r65535,0
|
||||
syscall 13
|
||||
syscall 13 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
sub waitvsync() {
|
||||
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||
%ir {{
|
||||
syscall 14
|
||||
syscall 14()
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -64,8 +63,7 @@ sys {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%ir {{
|
||||
loadm.b r65535,sys.exit.returnvalue
|
||||
setparam.b r65535,0
|
||||
syscall 1
|
||||
syscall 1 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -85,39 +83,31 @@ sys {
|
||||
sub gfx_enable(ubyte mode) {
|
||||
%ir {{
|
||||
loadm.b r65535,sys.gfx_enable.mode
|
||||
setparam.b r65535,0
|
||||
syscall 8
|
||||
syscall 8 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_clear(ubyte color) {
|
||||
%ir {{
|
||||
loadm.b r65535,sys.gfx_clear.color
|
||||
setparam.b r65535,0
|
||||
syscall 9
|
||||
syscall 9 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_plot(uword xx, uword yy, ubyte color) {
|
||||
%ir {{
|
||||
loadm.w r65535,sys.gfx_plot.xx
|
||||
setparam.w r65535,0
|
||||
loadm.w r65535,sys.gfx_plot.yy
|
||||
setparam.w r65535,1
|
||||
loadm.w r65533,sys.gfx_plot.xx
|
||||
loadm.w r65534,sys.gfx_plot.yy
|
||||
loadm.b r65535,sys.gfx_plot.color
|
||||
setparam.b r65535,2
|
||||
syscall 10
|
||||
syscall 10 (r65533.w, r65534.w, r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
|
||||
%ir {{
|
||||
loadm.w r65535,sys.gfx_getpixel.xx
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,sys.gfx_getpixel.xx
|
||||
loadm.w r65535,sys.gfx_getpixel.yy
|
||||
setparam.w r65535,1
|
||||
syscall 30
|
||||
pop.b r0
|
||||
syscall 30 (r65534.w, r65535.w): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@ sub clear_screen() {
|
||||
str @shared sequence = "\x1b[2J\x1B[H"
|
||||
%ir {{
|
||||
load.w r65535,txt.clear_screen.sequence
|
||||
setparam.w r65535,0
|
||||
syscall 3
|
||||
syscall 3 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -40,16 +39,14 @@ sub uppercase() {
|
||||
sub chrout(ubyte char) {
|
||||
%ir {{
|
||||
loadm.b r65535,txt.chrout.char
|
||||
setparam.b r65535,0
|
||||
syscall 2
|
||||
syscall 2 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub print (str text) {
|
||||
%ir {{
|
||||
loadm.w r65535,txt.print.text
|
||||
setparam.w r65535,0
|
||||
syscall 3
|
||||
syscall 3 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -125,12 +122,9 @@ sub input_chars (uword buffer) -> ubyte {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
%ir {{
|
||||
loadm.w r65535,txt.input_chars.buffer
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,txt.input_chars.buffer
|
||||
load.b r65535,80
|
||||
setparam.b r65535,1
|
||||
syscall 6
|
||||
pop.b r0
|
||||
syscall 6 (r65534.w, r65535.b): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.12
|
||||
9.0
|
||||
@@ -48,11 +48,12 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").required()
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
|
||||
val useNewExprCode by cli.option(ArgType.Boolean, fullName = "newexpr", description = "use new expression code-gen (experimental)")
|
||||
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
try {
|
||||
@@ -130,6 +131,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
useNewExprCode == true,
|
||||
compilationTarget,
|
||||
evalStackAddr,
|
||||
splitWordArrays == true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
@@ -195,6 +197,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
useNewExprCode == true,
|
||||
compilationTarget,
|
||||
evalStackAddr,
|
||||
splitWordArrays == true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
|
||||
@@ -7,10 +7,7 @@ import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
|
||||
import kotlin.math.*
|
||||
|
||||
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
|
||||
|
||||
@@ -19,12 +16,26 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
||||
"len" to ::builtinLen,
|
||||
"sizeof" to ::builtinSizeof,
|
||||
"sgn" to ::builtinSgn,
|
||||
"sqrt16" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
|
||||
"any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||
"all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
|
||||
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||
"mkword" to ::builtinMkword
|
||||
"mkword" to ::builtinMkword,
|
||||
"clamp__ubyte" to ::builtinClampUByte,
|
||||
"clamp__byte" to ::builtinClampByte,
|
||||
"clamp__uword" to ::builtinClampUWord,
|
||||
"clamp__word" to ::builtinClampWord,
|
||||
"min__ubyte" to ::builtinMinUByte,
|
||||
"min__byte" to ::builtinMinByte,
|
||||
"min__uword" to ::builtinMinUWord,
|
||||
"min__word" to ::builtinMinWord,
|
||||
"max__ubyte" to ::builtinMaxUByte,
|
||||
"max__byte" to ::builtinMaxByte,
|
||||
"max__uword" to ::builtinMaxUWord,
|
||||
"max__word" to ::builtinMaxWord
|
||||
)
|
||||
|
||||
private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0
|
||||
@@ -59,6 +70,16 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
|
||||
return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position)
|
||||
}
|
||||
|
||||
private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Double): NumericLiteral {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one float argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(constval.type != DataType.FLOAT)
|
||||
throw SyntaxError("built-in function requires one float argument", position)
|
||||
|
||||
return NumericLiteral(DataType.FLOAT, function(constval.number), args[0].position)
|
||||
}
|
||||
|
||||
private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Double): NumericLiteral {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||
@@ -156,3 +177,116 @@ private fun builtinSgn(args: List<Expression>, position: Position, program: Prog
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
return NumericLiteral(DataType.BYTE, constval.number.sign, position)
|
||||
}
|
||||
|
||||
private fun builtinMinByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("min requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.BYTE, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMinUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("min requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.UBYTE, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMinWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("min requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.WORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMinUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("min requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = max(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.BYTE, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = max(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.UBYTE, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = max(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.WORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = max(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinClampUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if(args.size!=3)
|
||||
throw SyntaxError("clamp requires 3 arguments", position)
|
||||
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(max(value.number, minimum.number), maximum.number)
|
||||
return NumericLiteral(DataType.UBYTE, result, position)
|
||||
}
|
||||
|
||||
private fun builtinClampByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if(args.size!=3)
|
||||
throw SyntaxError("clamp requires 3 arguments", position)
|
||||
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(max(value.number, minimum.number), maximum.number)
|
||||
return NumericLiteral(DataType.BYTE, result, position)
|
||||
}
|
||||
|
||||
private fun builtinClampUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if(args.size!=3)
|
||||
throw SyntaxError("clamp requires 3 arguments", position)
|
||||
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(max(value.number, minimum.number), maximum.number)
|
||||
return NumericLiteral(DataType.UWORD, result, position)
|
||||
}
|
||||
|
||||
private fun builtinClampWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if(args.size!=3)
|
||||
throw SyntaxError("clamp requires 3 arguments", position)
|
||||
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(max(value.number, minimum.number), maximum.number)
|
||||
return NumericLiteral(DataType.WORD, result, position)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class CompilerArguments(val filepath: Path,
|
||||
val useNewExprCode: Boolean,
|
||||
val compilationTarget: String,
|
||||
val evalStackBaseAddress: UInt?,
|
||||
val splitWordArrays: Boolean,
|
||||
val symbolDefs: Map<String, String>,
|
||||
val sourceDirs: List<String> = emptyList(),
|
||||
val outputDir: Path = Path(""),
|
||||
@@ -80,6 +81,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
varsHigh = args.varsHigh
|
||||
useNewExprCode = args.useNewExprCode
|
||||
evalStackBaseAddress = args.evalStackBaseAddress
|
||||
splitWordArrays = args.splitWordArrays
|
||||
outputDir = args.outputDir.normalize()
|
||||
symbolDefs = args.symbolDefs
|
||||
}
|
||||
@@ -281,10 +283,9 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
||||
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase()
|
||||
val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
|
||||
.flatMap { (it as Directive).args }.toSet()
|
||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||
val noSysInit = allOptions.any { it.name == "no_sysinit" }
|
||||
val allOptions = program.modules.flatMap { it.options() }.toSet()
|
||||
val floatsEnabled = "enable_floats" in allOptions
|
||||
val noSysInit = "no_sysinit" in allOptions
|
||||
val zpType: ZeropageType =
|
||||
if (zpoption == null)
|
||||
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
|
||||
@@ -32,6 +32,7 @@ class ModuleImporter(private val program: Program,
|
||||
val programPath = path.resolve(normalizedFilePath)
|
||||
if(programPath.exists()) {
|
||||
println("Compiling program ${Path("").absolute().relativize(programPath)}")
|
||||
println("Compiler target: $compilationTargetName")
|
||||
val source = SourceCode.File(programPath)
|
||||
return Ok(importModule(source))
|
||||
}
|
||||
|
||||
@@ -165,7 +165,8 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
||||
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
||||
iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_UW &&
|
||||
iterableDt != DataType.ARRAY_UW_SPLIT)
|
||||
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
||||
|
||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
|
||||
@@ -176,7 +177,8 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
|
||||
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
|
||||
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W &&
|
||||
iterableDt != DataType.ARRAY_W_SPLIT)
|
||||
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
@@ -333,7 +335,7 @@ internal class AstChecker(private val program: Program,
|
||||
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
||||
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL)
|
||||
err("parameter '${param.first.name}' should be (u)byte or bool")
|
||||
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
|
||||
}
|
||||
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
|
||||
@@ -341,8 +343,8 @@ internal class AstChecker(private val program: Program,
|
||||
err("parameter '${param.first.name}' should be (u)word (an address) or str")
|
||||
}
|
||||
else if(param.second.statusflag!=null) {
|
||||
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BOOL)
|
||||
err("parameter '${param.first.name}' should be bool or ubyte")
|
||||
if (param.first.type != DataType.BOOL)
|
||||
errors.err("parameter '${param.first.name}' should be of type bool", param.first.position)
|
||||
}
|
||||
}
|
||||
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
|
||||
@@ -356,8 +358,8 @@ internal class AstChecker(private val program: Program,
|
||||
err("return type #${index + 1} should be (u)word/address")
|
||||
}
|
||||
else if(pair.second.statusflag!=null) {
|
||||
if (pair.first != DataType.UBYTE && pair.first != DataType.BOOL)
|
||||
err("return type #${index + 1} should be bool or ubyte")
|
||||
if (pair.first != DataType.BOOL)
|
||||
err("return type #${index + 1} should be bool")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,7 +430,6 @@ internal class AstChecker(private val program: Program,
|
||||
val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc
|
||||
if(statusFlagsNoCarry.isNotEmpty())
|
||||
err("can only use Carry as status flag parameter")
|
||||
|
||||
}
|
||||
|
||||
// Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||
@@ -480,7 +481,9 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("target datatype is unknown", assignment.target.position)
|
||||
// otherwise, another error about missing symbol is already reported.
|
||||
} else {
|
||||
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
|
||||
// allow bitwise operations on different types as long as the size is the same
|
||||
if (!((assignment.value as? BinaryExpression)?.operator in BitwiseOperators && targetDt.isBytes && valueDt.isBytes || targetDt.isWords && valueDt.isWords))
|
||||
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,6 +630,9 @@ internal class AstChecker(private val program: Program,
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
||||
if(arraySize > 256)
|
||||
err("byte array length must be 1-256")
|
||||
in SplitWordArrayTypes ->
|
||||
if(arraySize > 256)
|
||||
err("split word array length must be 1-256")
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW ->
|
||||
if(arraySize > 128)
|
||||
err("word array length must be 1-128")
|
||||
@@ -675,6 +681,10 @@ internal class AstChecker(private val program: Program,
|
||||
if (length == 0 || length > 256)
|
||||
err("string and byte array length must be 1-256")
|
||||
}
|
||||
in SplitWordArrayTypes -> {
|
||||
if (length == 0 || length > 256)
|
||||
err("split word array length must be 1-256")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if (length == 0 || length > 128)
|
||||
err("word array length must be 1-128")
|
||||
@@ -687,6 +697,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(decl.splitArray && decl.type==VarDeclType.MEMORY)
|
||||
err("@split can't be used on memory mapped arrays")
|
||||
}
|
||||
|
||||
if(decl.datatype==DataType.STR) {
|
||||
@@ -790,7 +803,7 @@ internal class AstChecker(private val program: Program,
|
||||
err("this directive may only occur in a block or at module level")
|
||||
if(directive.args.isEmpty())
|
||||
err("missing option directive argument(s)")
|
||||
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge")}.any { !it })
|
||||
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays")}.any { !it })
|
||||
err("invalid option directive argument(s)")
|
||||
}
|
||||
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
|
||||
@@ -958,11 +971,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.operator in ComparisonOperators) {
|
||||
if(leftDt!=rightDt && !(leftDt in ByteDatatypes && rightDt in ByteDatatypes)) {
|
||||
throw FatalAstException("got comparison with different operand types: $leftDt ${expr.operator} $rightDt ${expr.position}")
|
||||
}
|
||||
} else {
|
||||
if(expr.operator !in ComparisonOperators) {
|
||||
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
|
||||
// str+str and str*number have already been const evaluated before we get here.
|
||||
errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
|
||||
@@ -1142,21 +1151,13 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(funcName[0] == "divmod") {
|
||||
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
|
||||
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
|
||||
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
|
||||
errors.err("all arguments must be ubyte", functionCallStatement.position)
|
||||
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UBYTE})
|
||||
errors.err("all arguments must be ubyte", functionCallStatement.position)
|
||||
}
|
||||
else if(funcName[0] == "divmodw") {
|
||||
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
|
||||
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
|
||||
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
|
||||
errors.err("all arguments must be uword", functionCallStatement.position)
|
||||
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UWORD})
|
||||
errors.err("all arguments must be uword", functionCallStatement.position)
|
||||
else if(funcName[0].startsWith("divmod")) {
|
||||
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression) {
|
||||
errors.err("arguments must be all ubyte or all uword", functionCallStatement.position)
|
||||
} else {
|
||||
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
|
||||
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
|
||||
}
|
||||
}
|
||||
|
||||
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
|
||||
@@ -1287,7 +1288,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
// check index value 0..255
|
||||
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
|
||||
if(dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
|
||||
if(dtxNum.isKnown && dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
|
||||
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
||||
|
||||
super.visit(arrayIndexedExpression)
|
||||
@@ -1437,22 +1438,23 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
return err("invalid byte array initialization value ${value.type}, expected $targetDt")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT-> {
|
||||
// value may be either a single word, or a word arraysize, or a range
|
||||
if(value.type istype targetDt) {
|
||||
if(!checkArrayValues(value, targetDt))
|
||||
return false
|
||||
val arraySpecSize = arrayspec.constIndex()
|
||||
val arraySize = value.value.size
|
||||
val maxLength = if(targetDt in SplitWordArrayTypes) 256 else 128
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize>128)
|
||||
return err("word array length must be 1-128")
|
||||
if(arraySpecSize>maxLength)
|
||||
return err("array length must be 1-$maxLength")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return true
|
||||
}
|
||||
return err("invalid word array size, must be 1-128")
|
||||
return err("invalid array size, must be 1-$maxLength")
|
||||
}
|
||||
return err("invalid word array initialization value ${value.type}, expected $targetDt")
|
||||
}
|
||||
@@ -1540,6 +1542,7 @@ internal class AstChecker(private val program: Program,
|
||||
when (it) {
|
||||
is NumericLiteral -> it.number.toInt()
|
||||
is AddressOf -> it.identifier.hashCode() and 0xffff
|
||||
is IdentifierReference -> it.hashCode() and 0xffff
|
||||
is TypecastExpression -> {
|
||||
val constVal = it.expression.constValue(program)
|
||||
val cast = constVal?.cast(it.type)
|
||||
@@ -1559,10 +1562,10 @@ internal class AstChecker(private val program: Program,
|
||||
DataType.ARRAY_B -> {
|
||||
correct = array.all { it in -128..127 }
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> {
|
||||
correct = array.all { (it in 0..65535) }
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> {
|
||||
correct = array.all { it in -32768..32767 }
|
||||
}
|
||||
DataType.ARRAY_F -> correct = true
|
||||
@@ -1611,8 +1614,11 @@ internal class AstChecker(private val program: Program,
|
||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
else {
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes) {
|
||||
// allow bitwise operations on different types as long as the size is the same
|
||||
if (!((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)))
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
@@ -162,9 +162,31 @@ class AstPreprocessor(val program: Program,
|
||||
nextAssignment.origin = AssignmentOrigin.VARINIT
|
||||
}
|
||||
}
|
||||
|
||||
if(options.splitWordArrays && (decl.datatype==DataType.ARRAY_W || decl.datatype==DataType.ARRAY_UW)) {
|
||||
if(!decl.definingBlock.isInLibrary)
|
||||
return makeSplitArray(decl)
|
||||
}
|
||||
|
||||
if(decl.datatype==DataType.ARRAY_W || decl.datatype==DataType.ARRAY_UW) {
|
||||
if ("splitarrays" in decl.definingBlock.options())
|
||||
return makeSplitArray(decl)
|
||||
if("splitarrays" in decl.definingModule.options())
|
||||
return makeSplitArray(decl)
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun makeSplitArray(decl: VarDecl): Iterable<IAstModification> {
|
||||
val splitDt = ArrayToElementTypes.getValue(decl.datatype)
|
||||
val newDecl = VarDecl(
|
||||
decl.type, decl.origin, splitDt, decl.zeropage, decl.arraysize, decl.name,
|
||||
decl.value, true, decl.sharedWithAsm, true, decl.position
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
// For non-kernal subroutines and non-asm parameters:
|
||||
// inject subroutine params as local variables (if they're not there yet).
|
||||
|
||||
@@ -93,6 +93,11 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if(binExpr.left isSameAs assignment.target)
|
||||
return noModifications
|
||||
val typeCast = binExpr.left as? TypecastExpression
|
||||
if(typeCast!=null && typeCast.expression isSameAs assignment.target)
|
||||
return noModifications
|
||||
val sourceDt = binExpr.left.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||
val (_, left) = binExpr.left.typecastTo(assignment.target.inferType(program).getOrElse { throw AssemblyError(
|
||||
"unknown dt"
|
||||
@@ -229,13 +234,13 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
val containingStatement = getContainingStatement(arrayIndexedExpression)
|
||||
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
|
||||
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
|
||||
return noModifications
|
||||
}
|
||||
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization/check for Vm target
|
||||
val containingStatement = getContainingStatement(arrayIndexedExpression)
|
||||
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
|
||||
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization for Vm target
|
||||
val index = arrayIndexedExpression.indexer.indexExpr
|
||||
if (index !is NumericLiteral && index !is IdentifierReference) {
|
||||
// replace complex indexing expression with a temp variable to hold the computed index first
|
||||
|
||||
@@ -30,7 +30,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
|
||||
newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position)
|
||||
}
|
||||
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name,
|
||||
newvalue, decl.isArray, decl.sharedWithAsm, decl.position)
|
||||
newvalue, decl.isArray, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
|
||||
newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position)
|
||||
}
|
||||
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name,
|
||||
newarray, true, decl.sharedWithAsm, decl.position)
|
||||
newarray, true, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
||||
"align_word" -> alignment = PtBlock.BlockAlignment.WORD
|
||||
"align_page" -> alignment = PtBlock.BlockAlignment.PAGE
|
||||
"force_output" -> forceOutput=true
|
||||
"merge" -> { /* ignore this one */ }
|
||||
"merge", "splitarrays" -> { /* ignore this one */ }
|
||||
else -> throw FatalAstException("weird directive option: ${arg.name}")
|
||||
}
|
||||
}
|
||||
@@ -433,7 +433,12 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
||||
|
||||
private fun transform(src: AddressOf): PtAddressOf {
|
||||
val addr = PtAddressOf(src.position)
|
||||
addr.add(transform(src.identifier))
|
||||
val (name, dt) = src.identifier.targetNameAndType(program)
|
||||
if(dt in SplitWordArrayTypes) {
|
||||
addr.add(PtIdentifier(name+"_lsb", dt, src.identifier.position))
|
||||
} else {
|
||||
addr.add(transform(src.identifier))
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,12 @@ import prog8.ast.expressions.ArrayLiteral
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.WhenChoice
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class LiteralsToAutoVars(private val program: Program,
|
||||
@@ -60,10 +58,12 @@ internal class LiteralsToAutoVars(private val program: Program,
|
||||
} else {
|
||||
val arrayDt = array.guessDatatype(program)
|
||||
if(arrayDt.isKnown) {
|
||||
val parentAssign = parent as? Assignment
|
||||
val targetDt = parentAssign?.target?.inferType(program) ?: arrayDt
|
||||
// turn the array literal it into an identifier reference
|
||||
val litval2 = array.cast(arrayDt.getOr(DataType.UNDEFINED))
|
||||
val litval2 = array.cast(targetDt.getOr(DataType.UNDEFINED))
|
||||
if(litval2!=null) {
|
||||
val vardecl2 = VarDecl.createAuto(litval2)
|
||||
val vardecl2 = VarDecl.createAuto(litval2, targetDt.getOr(DataType.UNDEFINED) in SplitWordArrayTypes)
|
||||
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(array, identifier, parent),
|
||||
|
||||
@@ -162,6 +162,7 @@ internal class StatementReorderer(val program: Program,
|
||||
null,
|
||||
false,
|
||||
it.sharedWithAsm,
|
||||
it.splitArray,
|
||||
it.position
|
||||
)
|
||||
IAstModification.ReplaceNode(it, newvar, subroutine)
|
||||
@@ -290,8 +291,10 @@ internal class StatementReorderer(val program: Program,
|
||||
} else {
|
||||
if (sourceVar.arraysize!!.constIndex() != targetVar.arraysize!!.constIndex())
|
||||
errors.err("element count mismatch", assign.position)
|
||||
if (sourceVar.datatype != targetVar.datatype)
|
||||
errors.err("element type mismatch", assign.position)
|
||||
if (sourceVar.datatype != targetVar.datatype) {
|
||||
if(!targetVar.splitArray || (sourceVar.datatype!=DataType.ARRAY_W && sourceVar.datatype!=DataType.ARRAY_UW))
|
||||
errors.err("element type mismatch", assign.position)
|
||||
}
|
||||
}
|
||||
|
||||
if(!errors.noErrors())
|
||||
|
||||
@@ -133,9 +133,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
if(leftDt istype DataType.WORD && rightDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
|
||||
// cast left to unsigned
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED), true, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
// cast left to unsigned word. Cast right to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
|
||||
mods += IAstModification.ReplaceNode(expr.left, cast, expr)
|
||||
if(rightDt istype DataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.right,
|
||||
TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
if(rightDt istype DataType.BYTE && leftDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
|
||||
// cast right to unsigned
|
||||
@@ -143,9 +150,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
}
|
||||
if(rightDt istype DataType.WORD && leftDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
|
||||
// cast right to unsigned
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOr(DataType.UNDEFINED), true, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
// cast right to unsigned word. Cast left to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
|
||||
mods += IAstModification.ReplaceNode(expr.right, cast, expr)
|
||||
if(leftDt istype DataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.left,
|
||||
TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
useNewExprCode = false,
|
||||
compilationTarget = target.name,
|
||||
evalStackBaseAddress = null,
|
||||
splitWordArrays = false,
|
||||
symbolDefs = emptyMap(),
|
||||
outputDir = outputDir
|
||||
)
|
||||
@@ -45,6 +46,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
private fun prepareTestFiles(source: String, optimize: Boolean, target: ICompilationTarget): Pair<String, Path> {
|
||||
val searchIn = mutableListOf(examplesDir)
|
||||
when (target) {
|
||||
is C64Target -> searchIn.add(0, assumeDirectory(examplesDir, "c64"))
|
||||
is Cx16Target -> searchIn.add(0, assumeDirectory(examplesDir, "cx16"))
|
||||
is VMTarget -> searchIn.add(0, assumeDirectory(examplesDir, "vm"))
|
||||
is C128Target -> searchIn.add(0, assumeDirectory(examplesDir, "c128"))
|
||||
@@ -68,10 +70,13 @@ class TestCompilerOnExamplesC64: FunSpec({
|
||||
"bdmusic",
|
||||
"bdmusic-irq",
|
||||
"charset",
|
||||
"cube3d",
|
||||
"cube3d-sprites",
|
||||
"plasma",
|
||||
"rasterbars",
|
||||
"sprites",
|
||||
"starfield",
|
||||
"tehtriz",
|
||||
"turtle-gfx",
|
||||
"wizzine",
|
||||
),
|
||||
@@ -93,13 +98,17 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
val onlyCx16 = cartesianProduct(
|
||||
listOf(
|
||||
"vtui/testvtui",
|
||||
"pcmaudio/play-adpcm",
|
||||
"pcmaudio/stream-wav",
|
||||
"amiga",
|
||||
"bdmusic",
|
||||
"bobs",
|
||||
"bubbleuniverse",
|
||||
"circles",
|
||||
"cobramk3-gfx",
|
||||
"colorbars",
|
||||
"cube3d",
|
||||
"cxlogo",
|
||||
"datetime",
|
||||
"diskspeed",
|
||||
"fileseek",
|
||||
@@ -138,7 +147,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"cube3d",
|
||||
"cube3d-float",
|
||||
"cube3d-gfx",
|
||||
"cxlogo",
|
||||
"dirlist",
|
||||
"fibonacci",
|
||||
"line-circle-gfx",
|
||||
@@ -148,7 +156,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"mandelbrot-gfx",
|
||||
"numbergame",
|
||||
"primes",
|
||||
"rasterbars",
|
||||
"screencodes",
|
||||
"sorting",
|
||||
"swirl",
|
||||
|
||||
@@ -53,6 +53,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
useNewExprCode = false,
|
||||
compilationTarget = Cx16Target.NAME,
|
||||
evalStackBaseAddress = null,
|
||||
splitWordArrays = false,
|
||||
symbolDefs = emptyMap(),
|
||||
sourceDirs,
|
||||
outputDir
|
||||
|
||||
@@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
|
||||
<INITGLOBALS>
|
||||
</INITGLOBALS>
|
||||
|
||||
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
|
||||
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
|
||||
</BLOCK>
|
||||
</PROGRAM>
|
||||
""")
|
||||
|
||||
@@ -112,7 +112,7 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
|
||||
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
|
||||
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
||||
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
@@ -150,7 +150,7 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
|
||||
test("regular variable not in mapped IO ram on C64") {
|
||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
|
||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||
@@ -162,7 +162,7 @@ class TestMemory: FunSpec({
|
||||
|
||||
test("memory mapped variable not in mapped IO ram on C64") {
|
||||
val address = 0x1000u
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||
@@ -174,7 +174,7 @@ class TestMemory: FunSpec({
|
||||
|
||||
test("memory mapped variable in mapped IO ram on C64") {
|
||||
val address = 0xd020u
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||
@@ -185,7 +185,7 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
|
||||
test("array not in mapped IO ram") {
|
||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
|
||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
@@ -198,7 +198,7 @@ class TestMemory: FunSpec({
|
||||
|
||||
test("memory mapped array not in mapped IO ram") {
|
||||
val address = 0x1000u
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
@@ -211,7 +211,7 @@ class TestMemory: FunSpec({
|
||||
|
||||
test("memory mapped array in mapped IO ram") {
|
||||
val address = 0xd800u
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||
|
||||
@@ -532,7 +532,7 @@ class TestOptimization: FunSpec({
|
||||
uword @shared zz
|
||||
zz += 60 ; NOT ok to remove initializer, should evaluate to 60
|
||||
ubyte @shared xx
|
||||
xx = 6+lsb(abs(xx)) ; is not an initializer because it references xx
|
||||
xx = 6+lsb(mkword(xx,22)) ; is not an initializer because it references xx
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -26,7 +26,7 @@ class TestSubroutines: FunSpec({
|
||||
|
||||
sub start() {
|
||||
func("abc")
|
||||
uword[] commands = ["abc", func]
|
||||
uword[] commands = ["abc", 1.234]
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
|
||||
@@ -27,14 +27,14 @@ class TestTypecasts: FunSpec({
|
||||
main {
|
||||
sub start() {
|
||||
float fl
|
||||
floats.print_f(abs(fl))
|
||||
floats.print_f(lsb(fl))
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors)
|
||||
result shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UBYTE, BYTE, UWORD, WORD]"
|
||||
errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UWORD, WORD]"
|
||||
}
|
||||
|
||||
test("not casting bool operands to logical operators") {
|
||||
|
||||
@@ -10,70 +10,70 @@ main {
|
||||
txt.print("ubyte shift left\n")
|
||||
a = shiftlb0()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb1()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb2()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb3()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb4()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb5()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb6()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb7()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb8()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftlb9()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN()
|
||||
|
||||
txt.print("ubyte shift right\n")
|
||||
a = shiftrb0()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb1()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb2()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb3()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb4()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb5()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb6()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb7()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb8()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
a = shiftrb9()
|
||||
txt.print_ubbin(a, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN()
|
||||
|
||||
|
||||
|
||||
@@ -82,70 +82,70 @@ main {
|
||||
byte signedb
|
||||
signedb = shiftlsb0()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb1()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb2()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb3()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb4()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb5()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb6()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb7()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb8()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftlsb9()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN()
|
||||
|
||||
txt.print("signed byte shift right\n")
|
||||
signedb = shiftrsb0()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb1()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb2()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb3()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb4()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb5()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb6()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb7()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb8()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
signedb = shiftrsb9()
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN()
|
||||
|
||||
|
||||
|
||||
@@ -154,233 +154,233 @@ main {
|
||||
uword uw
|
||||
uw = shiftluw0()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw1()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw2()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw3()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw4()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw5()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw6()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw7()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw8()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw9()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw10()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw11()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw12()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw13()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw14()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw15()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw16()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftluw17()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN()
|
||||
|
||||
txt.print("uword shift right\n")
|
||||
uw = shiftruw0()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw1()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw2()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw3()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw4()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw5()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw6()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw7()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw8()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw9()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw10()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw11()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw12()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw13()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw14()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw15()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw16()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
uw = shiftruw17()
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN()
|
||||
|
||||
txt.print("signed word shift left\n")
|
||||
word sw
|
||||
sw = shiftlsw0()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw1()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw2()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw3()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw4()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw5()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw6()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw7()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw8()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw9()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw10()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw11()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw12()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw13()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw14()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw15()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw16()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftlsw17()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
void cbm.CHRIN()
|
||||
|
||||
txt.print("signed word shift right\n")
|
||||
sw = shiftrsw0()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw1()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw2()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw3()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw4()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw5()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw6()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw7()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw8()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw9()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw10()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw11()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw12()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw13()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw14()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw15()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw16()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
sw = shiftrsw17()
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -298,10 +298,10 @@ main {
|
||||
|
||||
|
||||
uw = 50000
|
||||
ub = sqrt16(uw)
|
||||
ub = sqrt(uw)
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+sqrt16(uw)*1+zero
|
||||
ub = zero+sqrt(uw)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ main {
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_byte(byte a1, byte a2, byte c) {
|
||||
@@ -52,7 +52,7 @@ main {
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_uword(uword a1, uword a2, uword c) {
|
||||
@@ -67,7 +67,7 @@ main {
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_word(word a1, word a2, word c) {
|
||||
@@ -82,12 +82,12 @@ main {
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_float(float a1, float a2, float c) {
|
||||
float r = a1/a2
|
||||
if floats.fabs(r-c)<0.00001
|
||||
if abs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else
|
||||
txt.print("err! ")
|
||||
@@ -98,6 +98,6 @@ main {
|
||||
floats.print_f(a2)
|
||||
txt.print(" = ")
|
||||
floats.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ main {
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_byte(byte a1, byte a2, byte c) {
|
||||
@@ -63,7 +63,7 @@ main {
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_uword(uword a1, uword a2, uword c) {
|
||||
@@ -78,7 +78,7 @@ main {
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_word(word a1, word a2, word c) {
|
||||
@@ -93,12 +93,12 @@ main {
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_float(float a1, float a2, float c) {
|
||||
float r = a1-a2
|
||||
if floats.fabs(r-c)<0.00001
|
||||
if abs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else {
|
||||
txt.print("err! ")
|
||||
@@ -110,6 +110,6 @@ main {
|
||||
floats.print_f(a2)
|
||||
txt.print(" = ")
|
||||
floats.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ main {
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_byte(byte a1, byte a2, byte c) {
|
||||
@@ -54,7 +54,7 @@ main {
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_uword(uword a1, uword a2, uword c) {
|
||||
@@ -69,7 +69,7 @@ main {
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_word(word a1, word a2, word c) {
|
||||
@@ -84,12 +84,12 @@ main {
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_float(float a1, float a2, float c) {
|
||||
float r = a1*a2
|
||||
if floats.fabs(r-c)<0.00001
|
||||
if abs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else
|
||||
txt.print("err! ")
|
||||
@@ -100,6 +100,6 @@ main {
|
||||
floats.print_f(a2)
|
||||
txt.print(" = ")
|
||||
floats.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ main {
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_byte(byte a1, byte a2, byte c) {
|
||||
@@ -58,7 +58,7 @@ main {
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_uword(uword a1, uword a2, uword c) {
|
||||
@@ -73,7 +73,7 @@ main {
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_word(word a1, word a2, word c) {
|
||||
@@ -88,12 +88,12 @@ main {
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_float(float a1, float a2, float c) {
|
||||
float r = a1+a2
|
||||
if floats.fabs(r-c)<0.00001
|
||||
if abs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else
|
||||
txt.print("err! ")
|
||||
@@ -104,6 +104,6 @@ main {
|
||||
floats.print_f(a2)
|
||||
txt.print(" = ")
|
||||
floats.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
@@ -85,9 +85,9 @@ main {
|
||||
txt.print("err! ")
|
||||
txt.print(" ubyte ")
|
||||
txt.print_ub(value)
|
||||
c64.CHROUT(',')
|
||||
cbm.CHROUT(',')
|
||||
txt.print_ub(expected)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_b(byte value, byte expected) {
|
||||
@@ -97,9 +97,9 @@ main {
|
||||
txt.print("err! ")
|
||||
txt.print(" byte ")
|
||||
txt.print_b(value)
|
||||
c64.CHROUT(',')
|
||||
cbm.CHROUT(',')
|
||||
txt.print_b(expected)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_uw(uword value, uword expected) {
|
||||
@@ -109,9 +109,9 @@ main {
|
||||
txt.print("err! ")
|
||||
txt.print(" uword ")
|
||||
txt.print_uw(value)
|
||||
c64.CHROUT(',')
|
||||
cbm.CHROUT(',')
|
||||
txt.print_uw(expected)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_w(word value, word expected) {
|
||||
@@ -121,9 +121,9 @@ main {
|
||||
txt.print("err! ")
|
||||
txt.print(" word ")
|
||||
txt.print_w(value)
|
||||
c64.CHROUT(',')
|
||||
cbm.CHROUT(',')
|
||||
txt.print_w(expected)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_fl(float value, float expected) {
|
||||
@@ -133,8 +133,8 @@ main {
|
||||
txt.print("err! ")
|
||||
txt.print(" float ")
|
||||
floats.print_f(value)
|
||||
c64.CHROUT(',')
|
||||
cbm.CHROUT(',')
|
||||
floats.print_f(expected)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ main {
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
cbm.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub remainder_uword(uword a1, uword a2, uword c) {
|
||||
@@ -42,6 +42,6 @@ main {
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class TestIntermediateAst: FunSpec({
|
||||
ubyte cc
|
||||
ubyte[] array = [1,2,3]
|
||||
cc = 11 in array
|
||||
cc = sqrt16(lsb(cc))
|
||||
cc = sqrt(lsb(cc))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
+60
-1
@@ -1,12 +1,15 @@
|
||||
package prog8tests.codegeneration
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
class TestArrayInplaceAssign: FunSpec({
|
||||
class TestArrayThings: FunSpec({
|
||||
test("assign prefix var to array should compile fine and is not split into inplace array modification") {
|
||||
val text = """
|
||||
main {
|
||||
@@ -84,6 +87,62 @@ main {
|
||||
foow[1] = 4242
|
||||
foow[1] = ~foow[1]
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("split only for word arrays") {
|
||||
val text = """
|
||||
main {
|
||||
ubyte[10] @split sb
|
||||
uword[10] @split sw
|
||||
word[10] @split sw2
|
||||
float[10] @split sf
|
||||
|
||||
sub start() {
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors.forEach {
|
||||
it shouldContain "split"
|
||||
it shouldContain "word arrays"
|
||||
}
|
||||
}
|
||||
|
||||
test("split word arrays in asm as lsb/msb") {
|
||||
val text = """
|
||||
main {
|
||||
uword[10] @split @shared uw
|
||||
word[10] @split @shared sw
|
||||
uword[10] @shared normal
|
||||
|
||||
sub start() {
|
||||
%asm {{
|
||||
lda normal
|
||||
lda uw_lsb
|
||||
lda uw_msb
|
||||
lda sw_lsb
|
||||
lda sw_msb
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("split array assignments") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
str name1 = "name1"
|
||||
str name2 = "name2"
|
||||
uword[] @split names = [name1, name2, "name3"]
|
||||
cx16.r0++
|
||||
names = [1111,2222,3333]
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
||||
@@ -46,8 +46,8 @@ class TestAsmGenSymbols: StringSpec({
|
||||
}
|
||||
|
||||
*/
|
||||
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, Position.DUMMY)
|
||||
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", null, false, false, false, Position.DUMMY)
|
||||
val labelInSub = Label("locallabel", Position.DUMMY)
|
||||
|
||||
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
@@ -63,7 +63,7 @@ class TestAsmGenSymbols: StringSpec({
|
||||
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
|
||||
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
|
||||
val labelInBlock = Label("label_outside", Position.DUMMY)
|
||||
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, Position.DUMMY)
|
||||
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY)
|
||||
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
|
||||
|
||||
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
|
||||
|
||||
@@ -37,7 +37,8 @@ internal fun compileFile(
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
outputDir = outputDir,
|
||||
errors = errors ?: ErrorReporterForTests()
|
||||
errors = errors ?: ErrorReporterForTests(),
|
||||
splitWordArrays = false
|
||||
)
|
||||
return compileProgram(args)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.string.shouldNotContain
|
||||
import prog8.ast.expressions.BuiltinFunctionCall
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.code.target.C64Target
|
||||
@@ -244,25 +245,26 @@ main {
|
||||
val exc = shouldThrow<Exception> {
|
||||
VmRunner().runProgram(virtfile.readText())
|
||||
}
|
||||
exc.message shouldContain("does not support real inlined assembly")
|
||||
exc.message shouldContain("encountered unconverted inline assembly chunk")
|
||||
}
|
||||
|
||||
test("inline asm for virtual target with IR is accepted") {
|
||||
test("inline asm for virtual target with IR is accepted and converted to regular instructions") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
%ir {{
|
||||
incm.b $2000
|
||||
return
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
val othertarget = Cx16Target()
|
||||
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
|
||||
|
||||
val target = VMTarget()
|
||||
val result = compileText(target, false, src, writeAssembly = true)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText())
|
||||
val irSrc = virtfile.readText()
|
||||
irSrc.shouldContain("incm.b $2000")
|
||||
irSrc.shouldNotContain("INLINEASM")
|
||||
VmRunner().runProgram(irSrc)
|
||||
}
|
||||
|
||||
test("addresses from labels/subroutines not yet supported in VM") {
|
||||
|
||||
@@ -25,7 +25,7 @@ dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "org.antlr:antlr4-runtime:4.12.0"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
|
||||
implementation project(':parser')
|
||||
}
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ open class Module(final override var statements: MutableList<Statement>,
|
||||
|
||||
override fun toString() = "Module(name=$name, pos=$position, lib=${isLibrary})"
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = statements.any { it.referencesIdentifier(nameInSource) }
|
||||
|
||||
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet()
|
||||
|
||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
|
||||
@@ -40,7 +40,7 @@ fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair<List<String>
|
||||
// add new temp variable to the ast directly (we can do this here because we're not iterating inside those container blocks)
|
||||
val decl = VarDecl(
|
||||
VarDeclType.VAR, VarDeclOrigin.AUTOGENERATED, dt, ZeropageWish.DONTCARE,
|
||||
null, tmpvarName[1], null, isArray = false, sharedWithAsm = false, position = Position.DUMMY
|
||||
null, tmpvarName[1], null, isArray = false, sharedWithAsm = false, splitArray = false, position = Position.DUMMY
|
||||
)
|
||||
block.statements.add(decl)
|
||||
decl.linkParents(block)
|
||||
|
||||
Loaded 100 of 199 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user