mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13ab4166c0 | ||
|
|
3dc5a0e7f8 | ||
|
|
e15c5cde53 | ||
|
|
d88c09b098 | ||
|
|
893b383bdf | ||
|
|
dd7c9d62e6 | ||
|
|
97c5c90eff | ||
|
|
1fb94e7a7b | ||
|
|
daca87c6d0 | ||
|
|
203ec5fa46 | ||
|
|
9ea69c07b8 | ||
|
|
68539d6cc9 | ||
|
|
f75fd0811e | ||
|
|
836bc9d456 | ||
|
|
a37769aafe | ||
|
|
68e62e4bd2 | ||
|
|
a5cd3728c9 | ||
|
|
a48ce35f0b | ||
|
|
e1835b5775 | ||
|
|
433832b329 | ||
|
|
ee81da14d6 | ||
|
|
6395d1908e | ||
|
|
989a5a2f8a | ||
|
|
b7a622c68e | ||
|
|
a8507b437d | ||
|
|
e505bf9ccf | ||
|
|
a289b32053 | ||
|
|
c3f1f09ad1 | ||
|
|
70ee2026ff | ||
|
|
690782bf60 | ||
|
|
755cc4835e | ||
|
|
a684ea46e4 | ||
|
|
8fbe13f99d | ||
|
|
452e9e275f | ||
|
|
cd40088636 | ||
|
|
9b9e6f4af5 | ||
|
|
ae6eeadf54 | ||
|
|
5268b05060 | ||
|
|
390263a34e | ||
|
|
55646edc3e | ||
|
|
8d177beb78 | ||
|
|
1da0c59182 | ||
|
|
36e8f10d2b | ||
|
|
cdf5a8f20f | ||
|
|
eb64d92333 | ||
|
|
eb55da63ef | ||
|
|
918302f79b | ||
|
|
9d7131d9f6 | ||
|
|
229c1114dd | ||
|
|
885df9156f | ||
|
|
c319233ddc | ||
|
|
958b5c0780 | ||
|
|
880c0a5da8 | ||
|
|
237c6dc856 | ||
|
|
ccf6e32bf9 | ||
|
|
a1874f6f00 | ||
|
|
95e4490a8a | ||
|
|
31c132c2eb | ||
|
|
00b0ec58b4 | ||
|
|
a1d0e5bb65 | ||
|
|
03e0d4b2e8 | ||
|
|
6afdd4e6fd | ||
|
|
b500a0d477 | ||
|
|
dd2463a440 | ||
|
|
23a8bebd9e | ||
|
|
3caf9108ad | ||
|
|
bde4be8231 | ||
|
|
0bbbb12ed2 | ||
|
|
b570bdaed7 | ||
|
|
8c0843cc87 | ||
|
|
31458ffd81 | ||
|
|
c15c10a94e | ||
|
|
9fca978725 | ||
|
|
b125901717 | ||
|
|
eb018ae660 | ||
|
|
7e5a9474fe | ||
|
|
525a9b5036 | ||
|
|
c3fbdf34ca | ||
|
|
48bd51e1a5 | ||
|
|
10d0b03a90 | ||
|
|
e1b3582f08 | ||
|
|
95be1c9e22 | ||
|
|
1ce8fe06d5 | ||
|
|
15c649024e | ||
|
|
e97303c226 | ||
|
|
3b786c819d | ||
|
|
04959dbd8b | ||
|
|
5cd4b874ea | ||
|
|
f14ea1b3de | ||
|
|
9cc0cda0fb | ||
|
|
09a7a4bbe5 | ||
|
|
cfea8b3745 | ||
|
|
28bf0b61ce | ||
|
|
2dc2429735 | ||
|
|
83d4592526 | ||
|
|
2d528c26ae | ||
|
|
66b3dce794 | ||
|
|
93f77a1045 | ||
|
|
aa4d23a3d5 | ||
|
|
2d7ebff8e9 | ||
|
|
bad9dd3b3b | ||
|
|
2f4e517857 | ||
|
|
ff35ba3696 | ||
|
|
72768e7fad | ||
|
|
77f3852cdc | ||
|
|
66857ca477 | ||
|
|
75514fc7af | ||
|
|
be06d871b6 | ||
|
|
f98ee326b4 | ||
|
|
bc8126eb16 | ||
|
|
4c8beefdcb | ||
|
|
bbb6c53457 | ||
|
|
d8991894e3 | ||
|
|
c7b7dcfd03 | ||
|
|
2c9e50873c | ||
|
|
923367296d | ||
|
|
151a206617 | ||
|
|
e403c4cf99 | ||
|
|
e3fbe37f9f | ||
|
|
dc870cd5ea | ||
|
|
584be44743 | ||
|
|
5fffd35ec1 | ||
|
|
b92e22e4a6 | ||
|
|
3e6d16a7a8 | ||
|
|
ecbcc277b8 | ||
|
|
dff1d9e4dd | ||
|
|
7c0bde7310 | ||
|
|
a82d21ac05 | ||
|
|
0bf8378fcb | ||
|
|
017ef8a837 | ||
|
|
0d63cdcb96 | ||
|
|
68a6f99c9f | ||
|
|
60781bcfc4 | ||
|
|
77fa2e2722 | ||
|
|
c36afd872e | ||
|
|
7e58a4c130 | ||
|
|
19a4bf1088 | ||
|
|
9678bbae4b | ||
|
|
a4d093afa1 | ||
|
|
ba788bcf0f | ||
|
|
f2c62bee7e | ||
|
|
548721e306 | ||
|
|
1ae950a638 | ||
|
|
c9385e93fe | ||
|
|
9bb16e293c | ||
|
|
c223702ea0 | ||
|
|
9167ba499d | ||
|
|
0cba736446 | ||
|
|
a0ab0bd3e2 | ||
|
|
6cda76a116 | ||
|
|
46c12a8899 | ||
|
|
c5219dfb3f | ||
|
|
4a8ee6815a | ||
|
|
e1b6bb154a | ||
|
|
fea297e409 | ||
|
|
7cf6aba625 | ||
|
|
70ed2b4203 | ||
|
|
0adce9b9c6 | ||
|
|
70a78e74f6 | ||
|
|
d5707b7bf3 | ||
|
|
9f247901d4 | ||
|
|
47485e4b49 | ||
|
|
64254e758d | ||
|
|
ab8173637a | ||
|
|
3841cef497 | ||
|
|
b717f1c7eb | ||
|
|
da57f76de3 | ||
|
|
4784f1c65a | ||
|
|
41af63b333 | ||
|
|
e2bb0de24d | ||
|
|
b791fae9ce | ||
|
|
6033a9e20c | ||
|
|
9e8c8973d8 | ||
|
|
3933bf5c1a | ||
|
|
708e296774 | ||
|
|
84925ab69c |
No files matched your search
Generated
+3
-3
@@ -1,13 +1,13 @@
|
||||
<component name="libraryTable">
|
||||
<library name="antlr.antlr4" type="repository">
|
||||
<properties maven-id="org.antlr:antlr4:4.13.0">
|
||||
<properties maven-id="org.antlr:antlr4:4.13.1">
|
||||
<exclude>
|
||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.0/antlr4-4.13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.1/antlr4-4.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.1/antlr4-runtime-4.13.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
||||
|
||||
+6
-6
@@ -1,19 +1,19 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.6.2" />
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.7.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.7.2/kotest-assertions-core-jvm-5.7.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/kotest/kotest-assertions-shared-jvm/5.7.2/kotest-assertions-shared-jvm-5.7.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/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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$/io/kotest/kotest-common-jvm/5.7.2/kotest-common-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.7.2/kotest-assertions-api-jvm-5.7.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>
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.property.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:5.6.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.6.2/kotest-property-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.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 />
|
||||
</library>
|
||||
</component>
|
||||
+13
-13
@@ -1,29 +1,29 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.6.2" />
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.7.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.6.2/kotest-runner-junit5-jvm-5.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-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/kotest/kotest-runner-junit5-jvm/5.7.2/kotest-runner-junit5-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.7.2/kotest-framework-api-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.7.2/kotest-assertions-shared-jvm-5.7.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/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$/io/kotest/kotest-common-jvm/5.7.2/kotest-common-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.7.2/kotest-framework-engine-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.162/classgraph-4.8.162.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/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/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.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$/io/kotest/kotest-framework-discovery-jvm/5.7.2/kotest-framework-discovery-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.7.2/kotest-assertions-core-jvm-5.7.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$/io/kotest/kotest-assertions-api-jvm/5.7.2/kotest-assertions-api-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.7.2/kotest-extensions-jvm-5.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.7.2/kotest-framework-concurrency-jvm-5.7.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!/" />
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.5" />
|
||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.5/kotlinx-cli-jvm-0.3.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
Generated
+3
-3
@@ -1,9 +1,9 @@
|
||||
<component name="libraryTable">
|
||||
<library name="slf4j.simple" type="repository">
|
||||
<properties maven-id="org.slf4j:slf4j-simple:2.0.7" />
|
||||
<properties maven-id="org.slf4j:slf4j-simple:2.0.9" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.7/slf4j-simple-2.0.7.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.9/slf4j-simple-2.0.9.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
Generated
+3
@@ -16,6 +16,9 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11" />
|
||||
</component>
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
|
||||
@@ -74,6 +74,8 @@ What does Prog8 provide?
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||
- "pet32": Commodore PET (experimental)
|
||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||
|
||||
|
||||
|
||||
@@ -32,10 +32,10 @@ dependencies {
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,13 +196,10 @@ class StStaticVariable(name: String,
|
||||
}
|
||||
if(onetimeInitializationNumericValue!=null) {
|
||||
require(dt in NumericDatatypes)
|
||||
require(onetimeInitializationNumericValue!=0.0) { "zero as init value should just remain uninitialized"}
|
||||
}
|
||||
if(onetimeInitializationArrayValue!=null) {
|
||||
require(dt in ArrayDatatypes)
|
||||
if(onetimeInitializationArrayValue.all { it.number!=null} ) {
|
||||
require(onetimeInitializationArrayValue.any { it.number != 0.0 }) { "array of all zerors as init value should just remain uninitialized" }
|
||||
}
|
||||
require(length==onetimeInitializationArrayValue.size)
|
||||
}
|
||||
if(onetimeInitializationStringValue!=null) {
|
||||
require(dt == DataType.STR)
|
||||
@@ -213,8 +210,7 @@ class StStaticVariable(name: String,
|
||||
|
||||
|
||||
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
|
||||
StNode(name, StNodeType.CONSTANT, astNode) {
|
||||
}
|
||||
StNode(name, StNodeType.CONSTANT, astNode)
|
||||
|
||||
|
||||
class StMemVar(name: String,
|
||||
@@ -236,12 +232,11 @@ class StMemorySlab(
|
||||
val align: UInt,
|
||||
astNode: PtNode
|
||||
):
|
||||
StNode(name, StNodeType.MEMORYSLAB, astNode) {
|
||||
}
|
||||
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
||||
|
||||
|
||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
|
||||
StNode(name, StNodeType.SUBROUTINE, astNode) {
|
||||
}
|
||||
StNode(name, StNodeType.SUBROUTINE, astNode)
|
||||
|
||||
|
||||
class StRomSub(name: String,
|
||||
|
||||
@@ -26,8 +26,6 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY),
|
||||
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY)
|
||||
).forEach {
|
||||
it.parent = program
|
||||
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||
@@ -76,10 +74,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
numElements = value.value.length + 1 // include the terminating 0-byte
|
||||
}
|
||||
is PtArray -> {
|
||||
val array = makeInitialArray(value)
|
||||
initialArray = if(array.all { it.number==0.0 }) null else array // all 0 as init value -> just uninitialized
|
||||
initialArray = makeInitialArray(value)
|
||||
initialString = null
|
||||
numElements = array.size
|
||||
numElements = initialArray.size
|
||||
require(node.arraySize?.toInt()==numElements)
|
||||
}
|
||||
else -> {
|
||||
@@ -128,7 +125,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
|
||||
return value.children.map {
|
||||
when(it) {
|
||||
is PtAddressOf -> StArrayElement(null, it.identifier.name)
|
||||
is PtAddressOf -> {
|
||||
if(it.isFromArrayElement)
|
||||
TODO("address-of array element $it in initial array value")
|
||||
StArrayElement(null, it.identifier.name)
|
||||
}
|
||||
is PtIdentifier -> StArrayElement(null, it.name)
|
||||
is PtNumber -> StArrayElement(it.number, null)
|
||||
else -> throw AssemblyError("invalid array element $it")
|
||||
|
||||
@@ -75,6 +75,7 @@ class PtBlock(name: String,
|
||||
val library: Boolean,
|
||||
val forceOutput: Boolean,
|
||||
val noSymbolPrefixing: Boolean,
|
||||
val veraFxMuls: Boolean,
|
||||
val alignment: BlockAlignment,
|
||||
val source: SourceCode, // taken from the module the block is defined in.
|
||||
position: Position
|
||||
|
||||
@@ -24,7 +24,17 @@ 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 PtAddressOf -> {
|
||||
if(other !is PtAddressOf)
|
||||
return false
|
||||
if (other.type!==type || !(other.identifier isSameAs identifier))
|
||||
return false
|
||||
if(other.children.size!=children.size)
|
||||
return false
|
||||
if(children.size==1)
|
||||
return true
|
||||
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
|
||||
}
|
||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
|
||||
is PtBinaryExpression -> 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
|
||||
@@ -105,7 +115,12 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
|
||||
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||
val identifier: PtIdentifier
|
||||
get() = children.single() as PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
val arrayIndexExpr: PtExpression?
|
||||
get() = if(children.size==2) children[1] as PtExpression else null
|
||||
|
||||
val isFromArrayElement: Boolean
|
||||
get() = children.size==2
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +133,9 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
||||
val splitWords: Boolean
|
||||
get() = variable.type in SplitWordArrayTypes
|
||||
|
||||
val usesPointerVariable: Boolean
|
||||
get() = variable.type==DataType.UWORD
|
||||
|
||||
init {
|
||||
require(elementType in NumericDatatypes)
|
||||
}
|
||||
|
||||
@@ -126,7 +126,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
else
|
||||
"->"
|
||||
}
|
||||
else -> throw InternalCompilerException("unrecognised ast node $node")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ 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:
|
||||
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
|
||||
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
|
||||
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
@@ -78,7 +80,9 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// 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),
|
||||
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
|
||||
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
|
||||
"abs" to FSignature(true, listOf(FParam("value", 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),
|
||||
@@ -124,11 +128,9 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
|
||||
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
|
||||
"rsave" to FSignature(false, emptyList(), null),
|
||||
"rsavex" to FSignature(false, emptyList(), null),
|
||||
"rrestore" to FSignature(false, emptyList(), null),
|
||||
"rrestorex" to FSignature(false, emptyList(), null),
|
||||
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
)
|
||||
|
||||
val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse")
|
||||
val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse")
|
||||
@@ -13,15 +13,15 @@ class CompilationOptions(val output: OutputType,
|
||||
val compTarget: ICompilationTarget,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var slowCodegenWarnings: Boolean = false,
|
||||
var warnSymbolShadowing: Boolean = false,
|
||||
var optimize: Boolean = false,
|
||||
var optimizeFloatExpressions: Boolean = false,
|
||||
var asmQuiet: Boolean = false,
|
||||
var asmListfile: Boolean = false,
|
||||
var includeSourcelines: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var evalStackBaseAddress: UInt? = null,
|
||||
var breakpointCpuInstruction: Boolean = false,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
|
||||
@@ -13,8 +13,6 @@ interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
var ESTACK_LO: UInt
|
||||
var ESTACK_HI: UInt
|
||||
val PROGRAM_LOAD_ADDRESS : UInt
|
||||
val BSSHIGHRAM_START: UInt
|
||||
val BSSHIGHRAM_END: UInt
|
||||
@@ -29,11 +27,4 @@ interface IMachineDefinition {
|
||||
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
fun overrideEvalStack(evalStackBaseAddress: UInt) {
|
||||
require(evalStackBaseAddress and 255u == 0u)
|
||||
ESTACK_LO = evalStackBaseAddress
|
||||
ESTACK_HI = evalStackBaseAddress + 256u
|
||||
require(ESTACK_LO !in golden.region && ESTACK_HI !in golden.region) { "user-set ESTACK can't be in GOLDEN ram" }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
|
||||
import java.nio.file.InvalidPathException
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
@@ -10,7 +10,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
|
||||
fun toClickableStr(): String {
|
||||
if(this===DUMMY)
|
||||
return ""
|
||||
if(file.startsWith(libraryFilePrefix))
|
||||
if(file.startsWith(LIBRARYFILEPREFIX))
|
||||
return "$file:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
|
||||
@@ -54,12 +54,14 @@ sealed class SourceCode {
|
||||
/**
|
||||
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
||||
*/
|
||||
const val libraryFilePrefix = "library:"
|
||||
const val stringSourcePrefix = "string:"
|
||||
const val LIBRARYFILEPREFIX = "library:"
|
||||
const val STRINGSOURCEPREFIX = "string:"
|
||||
val curdir: Path = Path(".").toAbsolutePath()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
||||
fun isRegularFilesystemPath(pathString: String) =
|
||||
!(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix))
|
||||
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
|
||||
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +71,7 @@ sealed class SourceCode {
|
||||
class Text(override val text: String): SourceCode() {
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}"
|
||||
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||
override val name = "<unnamed-text>"
|
||||
}
|
||||
|
||||
@@ -110,7 +112,7 @@ sealed class SourceCode {
|
||||
|
||||
override val isFromResources = true
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$libraryFilePrefix$normalized"
|
||||
override val origin = "$LIBRARYFILEPREFIX$normalized"
|
||||
override val text: String
|
||||
override val name: String
|
||||
|
||||
@@ -139,3 +141,33 @@ sealed class SourceCode {
|
||||
override val text: String = "<generated code node, no text representation>"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object SourceLineCache {
|
||||
private val cache = mutableMapOf<String, List<String>>()
|
||||
|
||||
private fun getCachedFile(file: String): List<String> {
|
||||
val existing = cache[file]
|
||||
if(existing!=null)
|
||||
return existing
|
||||
if (SourceCode.isRegularFilesystemPath(file)) {
|
||||
val source = SourceCode.File(Path(file))
|
||||
cache[file] = source.text.split('\n', '\r').map { it.trim() }
|
||||
return cache.getValue(file)
|
||||
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
|
||||
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
|
||||
cache[file] = source.text.split('\n', '\r').map { it.trim()}
|
||||
return cache.getValue(file)
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
fun retrieveLine(position: Position): String? {
|
||||
if (position.line>0) {
|
||||
val lines = getCachedFile(position.file)
|
||||
if(lines.isNotEmpty())
|
||||
return lines[position.line-1]
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.target.cbm.CbmMemorySizer
|
||||
import prog8.code.target.pet.PETMachineDefinition
|
||||
|
||||
|
||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
|
||||
override val name = NAME
|
||||
override val machine = PETMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
|
||||
override val defaultEncoding = Encoding.PETSCII
|
||||
|
||||
companion object {
|
||||
const val NAME = "pet32"
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,6 @@ class AtariMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = 6
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive // TODO
|
||||
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive // TODO
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@ class C128MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive
|
||||
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive
|
||||
|
||||
override val BSSHIGHRAM_START = 0u // TODO
|
||||
override val BSSHIGHRAM_END = 0u // TODO
|
||||
|
||||
|
||||
@@ -16,12 +16,8 @@ class C64MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0xcf00u // $cf00-$cf7f inclusive
|
||||
override var ESTACK_HI = 0xcf80u // $cf80-$cfff inclusive
|
||||
|
||||
override val BSSHIGHRAM_START = 0xc000u
|
||||
override val BSSHIGHRAM_END = ESTACK_LO
|
||||
override val BSSHIGHRAM_END = 0xd000u
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
@@ -62,7 +58,7 @@ class C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0xc000u until ESTACK_LO)
|
||||
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,11 +53,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
).map{it.toUInt()})
|
||||
}
|
||||
|
||||
if(options.zeropage!= ZeropageType.DONTUSE) {
|
||||
if(options.zeropage != ZeropageType.DONTUSE) {
|
||||
// add the free Zp addresses
|
||||
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
||||
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
|
||||
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
|
||||
} else {
|
||||
// don't use the zeropage at all
|
||||
|
||||
@@ -24,7 +24,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\r' , // 0x0D -> CARRIAGE RETURN
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
'\u000e', // 0x0E -> SHIFT OUT
|
||||
'\ufffe', // 0x0F -> UNDEFINED
|
||||
'\ufffe', // 0x10 -> UNDEFINED
|
||||
@@ -152,7 +152,7 @@ object PetsciiEncoding {
|
||||
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
||||
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
||||
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
||||
'\n' , // 0x8D -> LINE FEED
|
||||
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
|
||||
'\u000f', // 0x8E -> SHIFT IN
|
||||
'\ufffe', // 0x8F -> UNDEFINED
|
||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||
@@ -283,7 +283,7 @@ object PetsciiEncoding {
|
||||
'\ufffe', // 0x0A -> UNDEFINED
|
||||
'\ufffe', // 0x0B -> UNDEFINED
|
||||
'\ufffe', // 0x0C -> UNDEFINED
|
||||
'\r' , // 0x0D -> CARRIAGE RETURN
|
||||
'\n' , // 0x0D -> LINE FEED (RETURN)
|
||||
'\u000e', // 0x0E -> SHIFT OUT
|
||||
'\ufffe', // 0x0F -> UNDEFINED
|
||||
'\ufffe', // 0x10 -> UNDEFINED
|
||||
@@ -411,7 +411,7 @@ object PetsciiEncoding {
|
||||
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
|
||||
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
|
||||
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
|
||||
'\n' , // 0x8D -> LINE FEED
|
||||
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
|
||||
'\u000f', // 0x8E -> SHIFT IN
|
||||
'\ufffe', // 0x8F -> UNDEFINED
|
||||
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
|
||||
@@ -1061,6 +1061,7 @@ object PetsciiEncoding {
|
||||
'}' -> '├'
|
||||
'|' -> '│'
|
||||
'\\' -> '╲'
|
||||
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
|
||||
else -> chr
|
||||
}
|
||||
|
||||
@@ -1076,7 +1077,10 @@ object PetsciiEncoding {
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Petscii character for '${chr}' (${chr.code})")
|
||||
if(chr.isISOControl())
|
||||
throw CharConversionException("no ${case}Petscii character for char #${chr.code}")
|
||||
else
|
||||
throw CharConversionException("no ${case}Petscii character for char #${chr.code} '${chr}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1119,7 +1123,10 @@ object PetsciiEncoding {
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Screencode character for '${chr}' (${chr.code})")
|
||||
if(chr.isISOControl())
|
||||
throw CharConversionException("no ${case}Screencode character for char #${chr.code}")
|
||||
else
|
||||
throw CharConversionException("no ${case}Screencode character for char #${chr.code} '${chr}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,6 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||
|
||||
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||
override var ESTACK_LO = 0x0700u // $0700-$077f inclusive
|
||||
override var ESTACK_HI = 0x0780u // $0780-$07ff inclusive
|
||||
|
||||
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
|
||||
|
||||
@@ -64,7 +60,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,6 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package prog8.code.target.pet
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class PETMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
|
||||
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
|
||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||
override val PROGRAM_LOAD_ADDRESS = 0x0401u
|
||||
|
||||
override val BSSHIGHRAM_START = 0xffffu
|
||||
override val BSSHIGHRAM_END = 0xffffu
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
override lateinit var golden: GoldenRam
|
||||
|
||||
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting PET emulator...")
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = PETZeropage(compilerOptions)
|
||||
// there's no golden ram.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package prog8.code.target.pet
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
|
||||
// reference: http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
||||
|
||||
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
|
||||
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("PET target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
ZeropageType.DONTUSE
|
||||
))
|
||||
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x00u..0xffu)
|
||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x00u..0xffu)
|
||||
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
|
||||
}
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0xb3u..0xbau) // TODO more?
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
|
||||
override fun allocateCx16VirtualRegisters() {
|
||||
TODO("Not known if PET can put the virtual regs in ZP")
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ class VirtualMachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
|
||||
override var ESTACK_LO = 0u // not actually used
|
||||
override var ESTACK_HI = 0u // not actually used
|
||||
override val BSSHIGHRAM_START = 0u // not actually used
|
||||
override val BSSHIGHRAM_END = 0u // not actually used
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
|
||||
@@ -30,16 +30,16 @@ dependencies {
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.7.2'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
test {
|
||||
|
||||
@@ -172,8 +172,6 @@ private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
|
||||
call.children.addAll(children)
|
||||
call.children.forEach { it.parent = call }
|
||||
call.parent = parent
|
||||
if(name.endsWith("concat_string"))
|
||||
println("CONCAT ${this.position}")
|
||||
return call
|
||||
}
|
||||
|
||||
@@ -203,12 +201,12 @@ class AsmGen6502Internal (
|
||||
private val allocator = VariableAllocator(symbolTable, options, errors)
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
private val breakpointLabels = mutableListOf<String>()
|
||||
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)
|
||||
private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage)
|
||||
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, symbolTable, this, allocator)
|
||||
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
|
||||
private val anyExprGen = AnyExprAsmGen(this)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
|
||||
fun compileToAssembly(): IAssemblyProgram? {
|
||||
@@ -240,8 +238,19 @@ class AsmGen6502Internal (
|
||||
|
||||
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
|
||||
|
||||
private var lastSourceLineNumber: Int = -1
|
||||
|
||||
internal fun outputSourceLine(node: PtNode) {
|
||||
out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
|
||||
if(!options.includeSourcelines || node.position===Position.DUMMY || node.position.line==lastSourceLineNumber)
|
||||
return
|
||||
|
||||
lastSourceLineNumber = node.position.line
|
||||
val srcComment = "\t; source: ${node.position.file}:${node.position.line}"
|
||||
val line = SourceLineCache.retrieveLine(node.position)
|
||||
if(line==null)
|
||||
out(srcComment, false)
|
||||
else
|
||||
out("$srcComment $line", false)
|
||||
}
|
||||
|
||||
internal fun out(str: String, splitlines: Boolean = true) {
|
||||
@@ -394,32 +403,6 @@ class AsmGen6502Internal (
|
||||
return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars -> reference zp symbols directly
|
||||
}
|
||||
|
||||
internal fun saveRegisterLocal(register: CpuRegister, scope: IPtSubroutine) {
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
// just use the cpu's stack for all registers, shorter code
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
CpuRegister.X -> out(" phx")
|
||||
CpuRegister.Y -> out(" phy")
|
||||
}
|
||||
} else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
// just use the stack, only for A
|
||||
out(" pha")
|
||||
}
|
||||
CpuRegister.X -> {
|
||||
out(" stx prog8_regsaveX")
|
||||
subroutineExtra(scope).usedRegsaveX = true
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
out(" sty prog8_regsaveY")
|
||||
subroutineExtra(scope).usedRegsaveY = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun saveRegisterStack(register: CpuRegister, keepA: Boolean) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
@@ -444,24 +427,6 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreRegisterLocal(register: CpuRegister) {
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
when (register) {
|
||||
// this just used the stack, for all registers. Shorter code.
|
||||
CpuRegister.A -> out(" pla")
|
||||
CpuRegister.X -> out(" plx")
|
||||
CpuRegister.Y -> out(" ply")
|
||||
}
|
||||
|
||||
} else {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pla") // this just used the stack but only for A
|
||||
CpuRegister.X -> out(" ldx prog8_regsaveX")
|
||||
CpuRegister.Y -> out(" ldy prog8_regsaveY")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreRegisterStack(register: CpuRegister, keepA: Boolean) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
@@ -581,22 +546,12 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||
internal fun translateExpression(expression: PtExpression) =
|
||||
expressionsAsmGen.translateExpression(expression)
|
||||
|
||||
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? =
|
||||
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
|
||||
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? =
|
||||
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultRegister)
|
||||
|
||||
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCallExpr)
|
||||
|
||||
internal fun saveXbeforeCall(functionCall: PtFunctionCall) =
|
||||
functioncallAsmGen.saveXbeforeCall(functionCall)
|
||||
|
||||
internal fun restoreXafterCall(functionCall: PtFunctionCall) =
|
||||
functioncallAsmGen.restoreXafterCall(functionCall)
|
||||
|
||||
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) =
|
||||
assignmentAsmGen.translateNormalAssignment(assign, scope)
|
||||
|
||||
@@ -625,7 +580,6 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) {
|
||||
// don't use translateExpression() to avoid evalstack
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
@@ -907,7 +861,7 @@ $repeatLabel""")
|
||||
DataType.UBYTE, DataType.UWORD -> {
|
||||
val result = zeropage.allocate(counterVar, dt, null, stmt.position, errors)
|
||||
result.fold(
|
||||
success = { (address, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
|
||||
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
|
||||
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
|
||||
)
|
||||
return counterVar
|
||||
@@ -1047,7 +1001,10 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
private fun translate(asm: PtInlineAssembly) {
|
||||
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||
if(asm.isIR)
|
||||
throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly")
|
||||
else
|
||||
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||
}
|
||||
|
||||
private fun translate(incbin: PtIncludeBinary) {
|
||||
@@ -1069,6 +1026,15 @@ $repeatLabel""")
|
||||
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
||||
breakpointLabels.add(label)
|
||||
out(label)
|
||||
if(options.breakpointCpuInstruction) {
|
||||
val instruction =
|
||||
when(options.compTarget.machine.cpu) {
|
||||
CpuType.CPU6502 -> "brk"
|
||||
CpuType.CPU65c02 -> "stp"
|
||||
else -> throw AssemblyError("invalid cpu type")
|
||||
}
|
||||
out(" $instruction")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun signExtendAYlsb(valueDt: DataType) {
|
||||
@@ -1086,20 +1052,6 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun signExtendStackLsb(valueDt: DataType) {
|
||||
// sign extend signed byte on stack to signed word on stack
|
||||
when(valueDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(isTargetCpu(CpuType.CPU65c02))
|
||||
out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte")
|
||||
else -> throw AssemblyError("need byte type")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
|
||||
// sign extend signed byte in a var to a full word in that variable
|
||||
when(valueDt) {
|
||||
@@ -1728,7 +1680,7 @@ $repeatLabel""")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
return if(rightConstVal.number.toInt()!=0) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
}
|
||||
else
|
||||
@@ -1875,7 +1827,7 @@ $repeatLabel""")
|
||||
out(" beq $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@@ -2036,7 +1988,7 @@ $repeatLabel""")
|
||||
out(" bne $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@@ -2200,7 +2152,7 @@ $repeatLabel""")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
if(rightConstVal.number.toInt()!=0) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
}
|
||||
return
|
||||
@@ -2363,7 +2315,7 @@ $repeatLabel""")
|
||||
out(" bne $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@@ -2409,7 +2361,7 @@ $repeatLabel""")
|
||||
out(" beq $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegAorStack(left, false)
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
@@ -2474,8 +2426,7 @@ $repeatLabel""")
|
||||
cmp #<$number
|
||||
bne $jumpIfFalseLabel
|
||||
cpy #>$number
|
||||
bne $jumpIfFalseLabel
|
||||
""")
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
@@ -2483,18 +2434,21 @@ $repeatLabel""")
|
||||
cmp ${asmVariableName(right)}
|
||||
bne $jumpIfFalseLabel
|
||||
cpy ${asmVariableName(right)}+1
|
||||
bne $jumpIfFalseLabel
|
||||
""")
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(right.identifier)
|
||||
out("""
|
||||
cmp #<$name
|
||||
bne $jumpIfFalseLabel
|
||||
cpy #>$name
|
||||
bne $jumpIfFalseLabel
|
||||
""")
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${right.position}")
|
||||
// assignmentAsmGen.assignAddressOf(target, name, right.arrayIndexExpr)
|
||||
} else {
|
||||
out("""
|
||||
cmp #<$name
|
||||
bne $jumpIfFalseLabel
|
||||
cpy #>$name
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if(left.isSimple()) {
|
||||
@@ -2575,12 +2529,16 @@ $repeatLabel""")
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(right.identifier)
|
||||
out("""
|
||||
cmp #<$name
|
||||
bne +
|
||||
cpy #>$name
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${right.position}")
|
||||
} else {
|
||||
out("""
|
||||
cmp #<$name
|
||||
bne +
|
||||
cpy #>$name
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if(left.isSimple()) {
|
||||
@@ -2865,22 +2823,14 @@ $repeatLabel""")
|
||||
+""")
|
||||
}
|
||||
|
||||
internal fun translateDirectMemReadExpressionToRegAorStack(expr: PtMemoryByte, pushResultOnEstack: Boolean) {
|
||||
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
|
||||
|
||||
fun assignViaExprEval() {
|
||||
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
if (pushResultOnEstack) {
|
||||
out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
out(" lda (P8ZP_SCRATCH_W2)")
|
||||
}
|
||||
out(" lda (P8ZP_SCRATCH_W2)")
|
||||
} else {
|
||||
if (pushResultOnEstack) {
|
||||
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y | dex | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2888,21 +2838,14 @@ $repeatLabel""")
|
||||
is PtNumber -> {
|
||||
val address = (expr.address as PtNumber).number.toInt()
|
||||
out(" lda ${address.toHex()}")
|
||||
if(pushResultOnEstack)
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
// the identifier is a pointer variable, so read the value from the address in it
|
||||
loadByteFromPointerIntoA(expr.address as PtIdentifier)
|
||||
if(pushResultOnEstack)
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = expr.address as PtBinaryExpression
|
||||
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(pushResultOnEstack)
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
if(!tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
assignViaExprEval()
|
||||
}
|
||||
}
|
||||
@@ -2921,8 +2864,12 @@ $repeatLabel""")
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(left.identifier)
|
||||
code("#>$name", "#<$name")
|
||||
return true
|
||||
if(left.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${left.position}")
|
||||
} else {
|
||||
code("#>$name", "#<$name")
|
||||
return true
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
@@ -2970,8 +2917,12 @@ $repeatLabel""")
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(right.identifier)
|
||||
code("#>$name", "#<$name")
|
||||
return true
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${right.position}")
|
||||
} else {
|
||||
code("#>$name", "#<$name")
|
||||
return true
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
@@ -3098,6 +3049,18 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun pushFAC1() {
|
||||
out(" jsr floats.pushFAC1")
|
||||
}
|
||||
|
||||
internal fun popFAC1() {
|
||||
out(" clc | jsr floats.popFAC")
|
||||
}
|
||||
|
||||
internal fun popFAC2() {
|
||||
out(" sec | jsr floats.popFAC")
|
||||
}
|
||||
|
||||
internal fun needAsaveForExpr(arg: PtExpression): Boolean =
|
||||
arg !is PtNumber && arg !is PtIdentifier && (arg !is PtMemoryByte || !arg.isSimple())
|
||||
|
||||
@@ -3130,9 +3093,6 @@ $repeatLabel""")
|
||||
* it's more consistent to only define these attributes on a Subroutine node.
|
||||
*/
|
||||
internal class SubroutineExtraAsmInfo {
|
||||
var usedRegsaveA = false
|
||||
var usedRegsaveX = false
|
||||
var usedRegsaveY = false
|
||||
var usedFloatEvalResultVar1 = false
|
||||
var usedFloatEvalResultVar2 = false
|
||||
|
||||
|
||||
@@ -15,21 +15,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
||||
|
||||
var linesByFour = getLinesBy(lines, 4)
|
||||
|
||||
var mods = optimizeUselessStackByteWrites(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeIncDec(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeCmpSequence(linesByFour)
|
||||
var mods = optimizeIncDec(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
@@ -57,6 +43,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
mods = optimizeUnneededTempvarInAdd(linesByFour)
|
||||
if(mods.isNotEmpty()) {
|
||||
apply(mods, lines)
|
||||
linesByFour = getLinesBy(lines, 4)
|
||||
numberOfOptimizations++
|
||||
}
|
||||
|
||||
var linesByFourteen = getLinesBy(lines, 14)
|
||||
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
|
||||
if(mods.isNotEmpty()) {
|
||||
@@ -97,44 +90,6 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||
|
||||
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// when statement (on bytes) generates a sequence of:
|
||||
// lda $ce01,x
|
||||
// cmp #$20
|
||||
// beq check_prog8_s72choice_32
|
||||
// lda $ce01,x
|
||||
// cmp #$21
|
||||
// beq check_prog8_s73choice_33
|
||||
// the repeated lda can be removed
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||
lines[1].value.trim().startsWith("cmp ") &&
|
||||
lines[2].value.trim().startsWith("beq ") &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||
// this is a lot harder for word values because the instruction sequence varies.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||
lines[1].value.trim()=="dex" &&
|
||||
lines[2].value.trim()=="inx" &&
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
private fun optimizeSameAssignments(
|
||||
linesByFourteen: List<List<IndexedValue<String>>>,
|
||||
machine: IMachineDefinition,
|
||||
@@ -560,4 +515,27 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
|
||||
optimize('y', lines)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
|
||||
// this can be performed without the scratch variable: clc / adc something
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
||||
for(lines in linesByFour) {
|
||||
val first = lines[0].value.trimStart()
|
||||
val second = lines[1].value.trimStart()
|
||||
val third = lines[2].value.trimStart()
|
||||
val fourth = lines[3].value.trimStart()
|
||||
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
|
||||
if(fourth.substring(4)==first.substring(4)) {
|
||||
mods.add(Modification(lines[0].index, false, " clc"))
|
||||
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
@@ -1,14 +1,8 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.mapError
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
|
||||
internal class AssemblyProgram(
|
||||
@@ -28,15 +22,20 @@ internal class AssemblyProgram(
|
||||
val assemblerCommand: List<String>
|
||||
|
||||
when (compTarget.name) {
|
||||
in setOf("c64", "c128", "cx16") -> {
|
||||
in setOf("c64", "c128", "cx16", "pet32") -> {
|
||||
// CBM machines .prg generation.
|
||||
|
||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||
"-Wall", // "-Wno-strict-bool", "-Werror",
|
||||
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
|
||||
)
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
@@ -65,10 +64,15 @@ internal class AssemblyProgram(
|
||||
|
||||
// TODO are these options okay for atari?
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||
"-Wall", // "-Werror", "-Wno-strict-bool"
|
||||
"--no-monitor"
|
||||
)
|
||||
|
||||
if(options.warnSymbolShadowing)
|
||||
command.add("-Wshadow")
|
||||
else
|
||||
command.add("-Wno-shadow")
|
||||
|
||||
if(options.asmQuiet)
|
||||
command.add("--quiet")
|
||||
|
||||
@@ -130,18 +134,3 @@ internal class AssemblyProgram(
|
||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
|
||||
return com.github.michaelbull.result.runCatching {
|
||||
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").text
|
||||
}.mapError { NoSuchFileException(File(filename)) }
|
||||
} else {
|
||||
val sib = Path(source.origin).resolveSibling(filename)
|
||||
if (sib.isRegularFile())
|
||||
Ok(SourceCode.File(sib).text)
|
||||
else
|
||||
Ok(SourceCode.File(Path(filename)).text)
|
||||
}
|
||||
}
|
||||
@@ -10,44 +10,43 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val assignAsmGen: AssignmentAsmGen) {
|
||||
|
||||
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||
return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
|
||||
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? {
|
||||
return translateFunctioncall(fcall, discardResult = false, resultRegister = resultRegister)
|
||||
}
|
||||
|
||||
internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) {
|
||||
translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null)
|
||||
translateFunctioncall(fcall, discardResult = true, resultRegister = null)
|
||||
}
|
||||
|
||||
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||
if (discardResult && fcall.hasNoSideEffects)
|
||||
return null // can just ignore the whole function call altogether
|
||||
|
||||
if(discardResult && resultToStack)
|
||||
throw AssemblyError("cannot both discard the result AND put it onto stack")
|
||||
|
||||
val sscope = fcall.definingISub()
|
||||
|
||||
when (fcall.name) {
|
||||
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
|
||||
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
||||
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
||||
"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)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope)
|
||||
"msb" -> funcMsb(fcall, resultRegister)
|
||||
"lsb" -> funcLsb(fcall, resultRegister)
|
||||
"mkword" -> funcMkword(fcall, resultRegister)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultRegister)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
|
||||
"any", "all" -> funcAnyAll(fcall, resultRegister, sscope)
|
||||
"sgn" -> funcSgn(fcall, resultRegister, sscope)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
|
||||
"divmod__ubyte" -> funcDivmod(fcall)
|
||||
"divmod__uword" -> funcDivmodW(fcall)
|
||||
"rol" -> funcRol(fcall)
|
||||
"rol2" -> funcRol2(fcall)
|
||||
"ror" -> funcRor(fcall)
|
||||
"ror2" -> funcRor2(fcall)
|
||||
"setlsb" -> funcSetLsbMsb(fcall, false)
|
||||
"setmsb" -> funcSetLsbMsb(fcall, true)
|
||||
"sort" -> funcSort(fcall)
|
||||
"reverse" -> funcReverse(fcall)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultToStack, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultToStack, resultRegister)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokemon" -> { /* meme function */ }
|
||||
@@ -71,18 +70,35 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
|
||||
}
|
||||
"rsave" -> funcRsave()
|
||||
"rsavex" -> funcRsaveX()
|
||||
"rrestore" -> funcRrestore()
|
||||
"rrestorex" -> funcRrestoreX()
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultToStack)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD)
|
||||
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||
}
|
||||
|
||||
return BuiltinFunctions.getValue(fcall.name).returnType
|
||||
}
|
||||
|
||||
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType) {
|
||||
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
|
||||
when (resultType) {
|
||||
DataType.UBYTE -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A)
|
||||
asmgen.out(" tay | jsr math.multiply_bytes")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr math.square")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("optimized square only for integer types")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcDivmod(fcall: PtBuiltinFunctionCall) {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
@@ -115,11 +131,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
sty $remainderVar+1""")
|
||||
}
|
||||
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall) {
|
||||
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")
|
||||
}
|
||||
|
||||
private fun funcRsave() {
|
||||
@@ -142,13 +156,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
}
|
||||
|
||||
private fun funcRsaveX() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" phx")
|
||||
else
|
||||
asmgen.out(" txa | pha")
|
||||
}
|
||||
|
||||
private fun funcRrestore() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out("""
|
||||
@@ -166,13 +173,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
plp""")
|
||||
}
|
||||
|
||||
private fun funcRrestoreX() {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" plx")
|
||||
else
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
private fun funcCallFar(fcall: PtBuiltinFunctionCall) {
|
||||
if(asmgen.options.compTarget.name != "cx16")
|
||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||
@@ -183,7 +183,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" sta (+)+0 | sty (+)+1")
|
||||
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.AY) // uword argument
|
||||
asmgen.out("""
|
||||
jsr cx16.jsrfar
|
||||
jsr cx16.JSRFAR
|
||||
+ .word 0
|
||||
+ .byte 0""")
|
||||
// note that by convention the values in A+Y registers are now the return value of the call.
|
||||
@@ -253,7 +253,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
@@ -263,41 +263,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
val target =
|
||||
if(resultToStack)
|
||||
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null, fcall.position)
|
||||
else
|
||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
|
||||
if(resultToStack)
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
|
||||
else {
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -466,16 +450,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -571,16 +552,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -633,88 +611,135 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
|
||||
}
|
||||
|
||||
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
val dt = fcall.args.single().type
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_stack")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_stack")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
private fun funcSetLsbMsb(fcall: PtBuiltinFunctionCall, msb: Boolean) {
|
||||
val target: AsmAssignTarget
|
||||
when(fcall.args[0]) {
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
}
|
||||
is PtNumber -> {
|
||||
val num = (fcall.args[0] as PtNumber).number + if(msb) 1 else 0
|
||||
val mem = PtMemoryByte(fcall.position)
|
||||
mem.add(PtNumber(DataType.UBYTE, num, fcall.position))
|
||||
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
val mem = PtMemoryByte(fcall.position)
|
||||
if(msb) {
|
||||
val address = PtBinaryExpression("+", DataType.UWORD, fcall.args[0].position)
|
||||
address.add(fcall.args[0])
|
||||
address.add(PtNumber(address.type, 1.0, fcall.args[0].position))
|
||||
mem.add(address)
|
||||
} else {
|
||||
mem.add(fcall.args[0])
|
||||
}
|
||||
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
require(!indexer.usesPointerVariable)
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
val arrayVariable = indexer.variable
|
||||
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.ARRAY_UB, arrayVariable.position)
|
||||
indexer.children[0].parent = indexer
|
||||
elementSize = 1
|
||||
msbAdd = 0
|
||||
} else {
|
||||
elementSize = 2
|
||||
msbAdd = if(msb) 1 else 0
|
||||
}
|
||||
|
||||
// double the index because of word array (if not split), add one if msb (if not split)
|
||||
val constIndexNum = (indexer.index as? PtNumber)?.number
|
||||
if(constIndexNum!=null) {
|
||||
indexer.children[1] = PtNumber(indexer.index.type, constIndexNum*elementSize + msbAdd, indexer.position)
|
||||
indexer.children[1].parent = indexer
|
||||
} else {
|
||||
val multipliedIndex: PtExpression
|
||||
if(elementSize==1) {
|
||||
multipliedIndex = indexer.index
|
||||
} else {
|
||||
multipliedIndex = PtBinaryExpression("<<", indexer.index.type, indexer.position)
|
||||
multipliedIndex.add(indexer.index)
|
||||
multipliedIndex.add(PtNumber(DataType.UBYTE, 1.0, indexer.position))
|
||||
}
|
||||
if(msbAdd>0) {
|
||||
val msbIndex = PtBinaryExpression("+", indexer.index.type, indexer.position)
|
||||
msbIndex.add(multipliedIndex)
|
||||
msbIndex.add(PtNumber(DataType.UBYTE, msbAdd.toDouble(), indexer.position))
|
||||
indexer.children[1] = msbIndex
|
||||
msbIndex.parent = indexer
|
||||
} else {
|
||||
indexer.children[1] = multipliedIndex
|
||||
multipliedIndex.parent=indexer
|
||||
}
|
||||
}
|
||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
|
||||
}
|
||||
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
|
||||
}
|
||||
|
||||
if(fcall.args[1].asConstInteger() == 0) {
|
||||
assignAsmGen.assignConstantByte(target, 0)
|
||||
} else {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
|
||||
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
val dt = fcall.args.single().type
|
||||
when (dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
|
||||
}
|
||||
|
||||
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||
val dt = fcall.args.single().type
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
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 {
|
||||
when (dt) {
|
||||
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)
|
||||
when (dt) {
|
||||
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)
|
||||
}
|
||||
|
||||
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
val dt = fcall.args.single().type
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
|
||||
else -> {
|
||||
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
|
||||
}
|
||||
when (dt) {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
when (dt) {
|
||||
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")
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -730,7 +755,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val varname = asmgen.asmVariableName(addrExpr)
|
||||
if(asmgen.isZpVar(addrExpr)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
asmgen.out("""
|
||||
@@ -746,7 +770,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
}
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -756,18 +779,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||
// can do ZP,Y indexing
|
||||
val varname = asmgen.asmVariableName(pointer)
|
||||
val scope = fcall.definingISub()!!
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.saveRegisterLocal(CpuRegister.Y, scope)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
asmgen.restoreRegisterLocal(CpuRegister.Y)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out("""
|
||||
sta ($varname),y
|
||||
txa
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -779,7 +799,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||
}
|
||||
|
||||
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
fun fallback() {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||
@@ -803,11 +823,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($varname),y
|
||||
pha
|
||||
tax
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
}
|
||||
} else fallback()
|
||||
}
|
||||
@@ -820,33 +840,29 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||
asmgen.out("""
|
||||
lda ($varname),y
|
||||
pha
|
||||
tax
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
pla""")
|
||||
txa""")
|
||||
} else fallback()
|
||||
}
|
||||
else -> fallback()
|
||||
}
|
||||
|
||||
if(resultToStack){
|
||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
when(resultRegister ?: RegisterOrPair.AY) {
|
||||
RegisterOrPair.AY -> {}
|
||||
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
||||
RegisterOrPair.XY -> asmgen.out(" tax")
|
||||
in Cx16VirtualRegisters -> asmgen.out(
|
||||
" sta cx16.${
|
||||
resultRegister.toString().lowercase()
|
||||
} | sty cx16.${resultRegister.toString().lowercase()}+1")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
when(resultRegister ?: RegisterOrPair.AY) {
|
||||
RegisterOrPair.AY -> {}
|
||||
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
||||
RegisterOrPair.XY -> asmgen.out(" tax")
|
||||
in Cx16VirtualRegisters -> asmgen.out(
|
||||
" sta cx16.${
|
||||
resultRegister.toString().lowercase()
|
||||
} | sty cx16.${resultRegister.toString().lowercase()}+1")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
when(fcall.type) {
|
||||
in ByteDatatypes -> {
|
||||
@@ -854,30 +870,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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?) {
|
||||
private fun funcMin(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
if(fcall.type in ByteDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
|
||||
@@ -887,12 +895,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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)
|
||||
}
|
||||
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
|
||||
@@ -928,18 +932,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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)
|
||||
}
|
||||
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?) {
|
||||
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type in SignedDatatypes
|
||||
if(fcall.type in ByteDatatypes) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||
@@ -949,12 +949,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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)
|
||||
}
|
||||
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
|
||||
@@ -990,72 +986,61 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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)
|
||||
}
|
||||
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
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta P8ESTACK_LO,x | pla | sta P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val reg = resultRegister ?: RegisterOrPair.AY
|
||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
if(!needAsave) {
|
||||
val mr0 = fcall.args[0] as? PtMemoryByte
|
||||
val mr1 = fcall.args[1] as? PtMemoryByte
|
||||
if (mr0 != null)
|
||||
needAsave = mr0.address !is PtNumber
|
||||
if (mr1 != null)
|
||||
needAsave = needAsave or (mr1.address !is PtNumber)
|
||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val reg = resultRegister ?: RegisterOrPair.AY
|
||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
if(!needAsave) {
|
||||
val mr0 = fcall.args[0] as? PtMemoryByte
|
||||
val mr1 = fcall.args[1] as? PtMemoryByte
|
||||
if (mr0 != null)
|
||||
needAsave = mr0.address !is PtNumber
|
||||
if (mr1 != null)
|
||||
needAsave = needAsave or (mr1.address !is PtNumber)
|
||||
}
|
||||
when(reg) {
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when(reg) {
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" tax")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid mkword target reg")
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
if(needAsave)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
if(needAsave)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" tax")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid mkword target reg")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.type !in WordDatatypes)
|
||||
throw AssemblyError("msb required word argument")
|
||||
@@ -1063,53 +1048,44 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
if(resultToStack) {
|
||||
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
if(resultToStack) {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tya")
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
|
||||
asmgen.out(" tya")
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AX)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.type !in WordDatatypes)
|
||||
throw AssemblyError("lsb required word argument")
|
||||
@@ -1118,72 +1094,60 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
if(resultToStack) {
|
||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
|
||||
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
|
||||
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
if(resultToStack) {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
|
||||
// NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what X is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" tay | cpy #0")
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
|
||||
asmgen.out(" ldy #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
|
||||
asmgen.out(" ldx #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
|
||||
asmgen.out(" ldy #0 | cpx #0")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister)
|
||||
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
zero.parent=fcall
|
||||
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
|
||||
asmgen.out(" lda cx16.r0L")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
when(resultRegister) {
|
||||
null, RegisterOrPair.A -> {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
|
||||
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.X -> {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.XY)
|
||||
// NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction.
|
||||
// this is required because the compiler assumes the status bits are set according to what X is (lsb)
|
||||
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
|
||||
}
|
||||
RegisterOrPair.Y -> {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
|
||||
asmgen.out(" tay | cpy #0")
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
|
||||
asmgen.out(" ldy #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AX)
|
||||
asmgen.out(" ldx #0 | cmp #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
asmgen.assignExpressionToRegister(arg, RegisterOrPair.XY)
|
||||
asmgen.out(" ldy #0 | cpx #0")
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(arg, resultRegister)
|
||||
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
zero.parent=fcall
|
||||
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
|
||||
asmgen.out(" lda cx16.r0L")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,933 +0,0 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
internal class ExpressionsAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val allocator: VariableAllocator) {
|
||||
|
||||
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||
internal fun translateExpression(expression: PtExpression) {
|
||||
if (this.asmgen.options.slowCodegenWarnings) {
|
||||
asmgen.errors.warn("slow stack evaluation used for expression", expression.position)
|
||||
}
|
||||
translateExpressionInternal(expression)
|
||||
}
|
||||
|
||||
|
||||
// the rest of the methods are all PRIVATE
|
||||
|
||||
|
||||
private fun translateExpressionInternal(expression: PtExpression) {
|
||||
|
||||
when(expression) {
|
||||
is PtPrefix -> translateExpression(expression)
|
||||
is PtBinaryExpression -> translateExpression(expression)
|
||||
is PtArrayIndexer -> translateExpression(expression)
|
||||
is PtTypeCast -> translateExpression(expression)
|
||||
is PtAddressOf -> translateExpression(expression)
|
||||
is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
|
||||
is PtNumber -> translateExpression(expression)
|
||||
is PtIdentifier -> translateExpression(expression)
|
||||
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||
is PtContainmentCheck -> translateContainmentCheck(expression)
|
||||
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
|
||||
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
|
||||
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
|
||||
else -> TODO("missing expression asmgen for $expression")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateContainmentCheck(check: PtContainmentCheck) {
|
||||
asmgen.assignExpressionToRegister(check, RegisterOrPair.A)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
|
||||
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
|
||||
// only for use in nested expression evaluation
|
||||
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
asmgen.saveXbeforeCall(call)
|
||||
asmgen.translateFunctionCall(call)
|
||||
if(sub.regXasResult()) {
|
||||
// store the return value in X somewhere that we can access again below
|
||||
asmgen.out(" stx P8ZP_SCRATCH_REG")
|
||||
}
|
||||
asmgen.restoreXafterCall(call)
|
||||
|
||||
val returns: List<Pair<RegisterOrStatusflag, DataType>> = sub.returnsWhatWhere()
|
||||
for ((reg, _) in returns) {
|
||||
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
|
||||
if (reg.registerOrPair != null) {
|
||||
when (reg.registerOrPair!!) {
|
||||
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
|
||||
RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
|
||||
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
RegisterOrPair.R2,
|
||||
RegisterOrPair.R3,
|
||||
RegisterOrPair.R4,
|
||||
RegisterOrPair.R5,
|
||||
RegisterOrPair.R6,
|
||||
RegisterOrPair.R7,
|
||||
RegisterOrPair.R8,
|
||||
RegisterOrPair.R9,
|
||||
RegisterOrPair.R10,
|
||||
RegisterOrPair.R11,
|
||||
RegisterOrPair.R12,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
asmgen.out(
|
||||
"""
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
}
|
||||
}
|
||||
} else when(reg.statusflag) {
|
||||
Statusflag.Pc -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
rol a
|
||||
sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pz -> {
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pv -> {
|
||||
asmgen.out("""
|
||||
bvs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
Statusflag.Pn -> {
|
||||
asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(typecast: PtTypeCast) {
|
||||
translateExpressionInternal(typecast.value)
|
||||
when(typecast.value.type) {
|
||||
DataType.UBYTE, DataType.BOOL -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when(typecast.type) {
|
||||
DataType.BYTE, DataType.UBYTE -> {}
|
||||
DataType.WORD, DataType.UWORD -> {}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(typecast.type) {
|
||||
DataType.BYTE, DataType.UBYTE -> {}
|
||||
DataType.WORD, DataType.UWORD -> {}
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
|
||||
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
|
||||
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
|
||||
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
|
||||
DataType.FLOAT -> {}
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.STR -> {
|
||||
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
|
||||
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
||||
}
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtAddressOf) {
|
||||
val name = asmgen.asmVariableName(expr.identifier)
|
||||
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtNumber) {
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||
lda #<${expr.number.toHex()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda #>${expr.number.toHex()}
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
DataType.FLOAT -> {
|
||||
val floatConst = allocator.getFloatAsmConst(expr.number)
|
||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
||||
}
|
||||
in 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")
|
||||
}
|
||||
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtBinaryExpression) {
|
||||
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
|
||||
return
|
||||
|
||||
val leftDt = expr.left.type
|
||||
val rightDt = expr.right.type
|
||||
|
||||
// compare with zero
|
||||
if(expr.operator in ComparisonOperators) {
|
||||
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||
val rightVal = expr.right.asConstInteger()
|
||||
if(rightVal==0)
|
||||
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
||||
}
|
||||
}
|
||||
|
||||
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators)
|
||||
return translateCompareStrings(expr.left, expr.operator, expr.right)
|
||||
|
||||
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
||||
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
||||
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
||||
|
||||
// the general, non-optimized cases
|
||||
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
||||
translateExpressionInternal(expr.left)
|
||||
translateExpressionInternal(expr.right)
|
||||
when (leftDt) {
|
||||
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||
else -> throw AssemblyError("non-numerical datatype")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateSomewhatOptimized(left: PtExpression, operator: String, right: PtExpression): Boolean {
|
||||
val leftDt = left.type
|
||||
val rightDt = right.type
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val leftVal = left.asConstInteger()
|
||||
val rightVal = right.asConstInteger()
|
||||
if (leftVal!=null && leftVal in -4..4) {
|
||||
translateExpressionInternal(right)
|
||||
if(rightDt in ByteDatatypes) {
|
||||
val incdec = if(leftVal<0) "dec" else "inc"
|
||||
repeat(leftVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(leftVal<0) {
|
||||
repeat(leftVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(leftVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
else if (rightVal!=null && rightVal in -4..4)
|
||||
{
|
||||
translateExpressionInternal(left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
val incdec = if(rightVal<0) "dec" else "inc"
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(rightVal<0) {
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(rightVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val rightVal = right.asConstInteger()
|
||||
if (rightVal!=null && rightVal in -4..4)
|
||||
{
|
||||
translateExpressionInternal(left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
val incdec = if(rightVal<0) "inc" else "dec"
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
// word
|
||||
if(rightVal>0) {
|
||||
repeat(rightVal.absoluteValue) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne +
|
||||
dec P8ESTACK_HI+1,x
|
||||
+ dec P8ESTACK_LO+1,x""")
|
||||
}
|
||||
} else {
|
||||
repeat(rightVal) {
|
||||
asmgen.out("""
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+""")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
">>" -> {
|
||||
val amount = right.asConstInteger()
|
||||
if(amount!=null) {
|
||||
translateExpressionInternal(left)
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" lsr a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
|
||||
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount>=16) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||
return true
|
||||
}
|
||||
var amountLeft = amount
|
||||
while (amountLeft >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_uw_7")
|
||||
amountLeft -= 7
|
||||
}
|
||||
if (amountLeft in 0..2)
|
||||
repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_uw_$amountLeft")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount>=16) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi +
|
||||
lda #0
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
beq ++
|
||||
+ lda #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
+""")
|
||||
return true
|
||||
}
|
||||
var amountLeft = amount
|
||||
while (amountLeft >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_w_7")
|
||||
amountLeft -= 7
|
||||
}
|
||||
if (amountLeft in 0..2)
|
||||
repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w_$amountLeft")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
"<<" -> {
|
||||
val amount = right.asConstInteger()
|
||||
if(amount!=null) {
|
||||
translateExpressionInternal(left)
|
||||
if (leftDt in ByteDatatypes) {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" asl a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
var amountLeft = amount
|
||||
while (amountLeft >= 7) {
|
||||
asmgen.out(" jsr math.shift_left_w_7")
|
||||
amountLeft -= 7
|
||||
}
|
||||
if (amountLeft in 0..2)
|
||||
repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_left_w_$amountLeft")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val leftVar = left as? PtIdentifier
|
||||
val rightVar = right as? PtIdentifier
|
||||
if(leftVar!=null && rightVar!=null && leftVar==rightVar) {
|
||||
translateSquared(leftVar, leftDt)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
val value = right as? PtNumber
|
||||
if(value!=null) {
|
||||
if(rightDt in IntegerDatatypes) {
|
||||
val amount = value.number.toInt()
|
||||
if(amount==2) {
|
||||
// optimize x*2 common case
|
||||
translateExpressionInternal(left)
|
||||
if(leftDt in ByteDatatypes) {
|
||||
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
||||
}
|
||||
return true
|
||||
}
|
||||
when(rightDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return true
|
||||
}
|
||||
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return true
|
||||
}
|
||||
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||
translateExpressionInternal(left)
|
||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||
return true
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/" -> {
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val rightVal = right.asConstInteger()
|
||||
if(rightVal!=null && rightVal==2) {
|
||||
translateExpressionInternal(left)
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
bpl +
|
||||
inc P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_LO+1,x
|
||||
+ asl a
|
||||
ror P8ESTACK_LO+1,x""")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl ++
|
||||
inc P8ESTACK_LO+1,x
|
||||
bne +
|
||||
inc P8ESTACK_HI+1,x
|
||||
+ lda P8ESTACK_HI+1,x
|
||||
+ asl a
|
||||
ror P8ESTACK_HI+1,x
|
||||
ror P8ESTACK_LO+1,x""")
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
|
||||
if(expr.isSimple()) {
|
||||
if(operator=="!=") {
|
||||
when (dt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
return
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
|
||||
asmgen.out("""
|
||||
sty P8ZP_SCRATCH_B1
|
||||
ora P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
lda #1
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
return
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
|
||||
asmgen.out("""
|
||||
jsr floats.SIGN
|
||||
sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
return
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
/* operator == is not worth it to special case, the code is mostly larger */
|
||||
}
|
||||
translateExpressionInternal(expr)
|
||||
when(operator) {
|
||||
"==" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.equal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
"!=" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.notequalzero_b")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.notequalzero_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.notequal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
"<" -> {
|
||||
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||
return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position))
|
||||
when(dt) {
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.less_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
">" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.greaterzero_ub")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterzero_sb")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.greaterzero_uw")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterzero_sw")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.greater_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
"<=" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzero_sb")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
">=" -> {
|
||||
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||
return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position))
|
||||
when(dt) {
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
|
||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.greaterequal_zero")
|
||||
else -> throw AssemblyError("wrong dt")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateSquared(variable: PtIdentifier, dt: DataType) {
|
||||
val asmVar = asmgen.asmVariableName(variable)
|
||||
when(dt) {
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" lda $asmVar")
|
||||
asmgen.signExtendAYlsb(dt)
|
||||
asmgen.out(" jsr math.square")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out(" lda $asmVar | ldy $asmVar+1 | jsr math.square")
|
||||
}
|
||||
else -> throw AssemblyError("require integer dt for square")
|
||||
}
|
||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: PtPrefix) {
|
||||
translateExpressionInternal(expr.value)
|
||||
when(expr.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes ->
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
eor #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(arrayExpr: PtArrayIndexer) {
|
||||
val elementDt = arrayExpr.type
|
||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
|
||||
|
||||
if(arrayExpr.variable.type==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(elementDt !in ByteDatatypes)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(arrayExpr.index.type != DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
if(asmgen.isZpVar(arrayExpr.variable)) {
|
||||
asmgen.out(" lda ($arrayVarName),y")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
return
|
||||
}
|
||||
|
||||
if(arrayExpr.splitWords)
|
||||
TODO("split words expression ${arrayExpr.position}")
|
||||
|
||||
val constIndexNum = arrayExpr.index.asConstInteger()
|
||||
if(constIndexNum!=null) {
|
||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue) | jsr floats.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird element type")
|
||||
}
|
||||
} else {
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>$arrayVarName
|
||||
clc
|
||||
adc #<$arrayVarName
|
||||
bcc +
|
||||
iny
|
||||
+ jsr floats.push_float""")
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||
when(operator) {
|
||||
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
|
||||
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
||||
"%" -> {
|
||||
if(types==DataType.BYTE)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||
}
|
||||
"+" -> asmgen.out("""
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
adc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"-" -> asmgen.out("""
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
|
||||
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
|
||||
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
||||
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
|
||||
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
|
||||
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
|
||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
|
||||
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
|
||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
|
||||
when(operator) {
|
||||
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
||||
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||
"%" -> {
|
||||
if(dt==DataType.WORD)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||
}
|
||||
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||
"<<" -> asmgen.out(" jsr math.shift_left_w")
|
||||
">>" -> {
|
||||
if(dt==DataType.UWORD)
|
||||
asmgen.out(" jsr math.shift_right_uw")
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w")
|
||||
}
|
||||
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
|
||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
|
||||
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorFloats(operator: String) {
|
||||
when(operator) {
|
||||
"*" -> asmgen.out(" jsr floats.mul_f")
|
||||
"/" -> asmgen.out(" jsr floats.div_f")
|
||||
"+" -> asmgen.out(" jsr floats.add_f")
|
||||
"-" -> asmgen.out(" jsr floats.sub_f")
|
||||
"<" -> asmgen.out(" jsr floats.less_f")
|
||||
">" -> asmgen.out(" jsr floats.greater_f")
|
||||
"<=" -> asmgen.out(" jsr floats.lesseq_f")
|
||||
">=" -> asmgen.out(" jsr floats.greatereq_f")
|
||||
"==" -> asmgen.out(" jsr floats.equal_f")
|
||||
"!=" -> asmgen.out(" jsr floats.notequal_f")
|
||||
"%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
|
||||
else -> throw AssemblyError("invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
|
||||
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
|
||||
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||
compareStringsProcessResultInA(operator)
|
||||
}
|
||||
|
||||
private fun compareStringsProcessResultInA(operator: String) {
|
||||
when(operator) {
|
||||
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
||||
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
||||
"<=" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
">=" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #1
|
||||
bne ++
|
||||
+ lda #0
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
"<" -> asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
">" -> asmgen.out("""
|
||||
bpl +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta P8ESTACK_LO,x""")
|
||||
}
|
||||
asmgen.out(" dex")
|
||||
}
|
||||
}
|
||||
@@ -6,26 +6,6 @@ import prog8.code.ast.PtSub
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal fun IPtSubroutine.regXasResult(): Boolean =
|
||||
(this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
|
||||
internal fun IPtSubroutine.shouldSaveX(): Boolean =
|
||||
this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam()))
|
||||
|
||||
internal fun PtAsmSub.regXasParam(): Boolean =
|
||||
parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
|
||||
internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
|
||||
|
||||
internal fun PtAsmSub.shouldKeepA(): KeepAresult {
|
||||
// determine if A's value should be kept when preparing for calling the subroutine, and when returning from it
|
||||
|
||||
// it seems that we never have to save A when calling? will be loaded correctly after setup.
|
||||
// but on return it depends on wether the routine returns something in A.
|
||||
val saveAonReturn = returns.any { it.first.registerOrPair==RegisterOrPair.A || it.first.registerOrPair==RegisterOrPair.AY || it.first.registerOrPair==RegisterOrPair.AX }
|
||||
return KeepAresult(false, saveAonReturn)
|
||||
}
|
||||
|
||||
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
|
||||
when(this) {
|
||||
is PtAsmSub -> {
|
||||
|
||||
@@ -5,9 +5,10 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
internal class ForLoopsAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val zeropage: Zeropage) {
|
||||
internal class ForLoopsAsmGen(
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val zeropage: Zeropage
|
||||
) {
|
||||
|
||||
internal fun translate(stmt: PtForLoop) {
|
||||
val iterableDt = stmt.iterable.type
|
||||
@@ -254,11 +255,11 @@ $loopLabel""")
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
pha
|
||||
tax
|
||||
lda $varname+1
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
pla
|
||||
txa
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
lda $varname+1
|
||||
$modifiedLabel sbc #0 ; modified
|
||||
@@ -382,7 +383,7 @@ $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 {
|
||||
|
||||
@@ -11,42 +11,10 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||
|
||||
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
|
||||
saveXbeforeCall(stmt)
|
||||
translateFunctionCall(stmt)
|
||||
restoreXafterCall(stmt)
|
||||
// just ignore any result values from the function call.
|
||||
}
|
||||
|
||||
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
|
||||
val symbol = asmgen.symbolTable.lookup(stmt.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
if(sub.shouldSaveX()) {
|
||||
if(sub is PtAsmSub) {
|
||||
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||
if (regSaveOnStack)
|
||||
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
|
||||
else
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
|
||||
} else
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreXafterCall(stmt: PtFunctionCall) {
|
||||
val symbol = asmgen.symbolTable.lookup(stmt.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
if(sub.shouldSaveX()) {
|
||||
if(sub is PtAsmSub) {
|
||||
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||
if (regSaveOnStack)
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
|
||||
else
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
||||
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|
||||
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
|
||||
@@ -173,6 +141,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val sourceName = asmgen.asmVariableName(value)
|
||||
// note: cannot use X register here to store A because it might be used for other arguments
|
||||
asmgen.out("""
|
||||
pha
|
||||
clc
|
||||
|
||||
@@ -13,7 +13,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
val targetIdent = stmt.target.identifier
|
||||
val targetMemory = stmt.target.memory
|
||||
val targetArrayIdx = stmt.target.array
|
||||
val scope = stmt.definingISub()
|
||||
when {
|
||||
targetIdent!=null -> {
|
||||
val what = asmgen.asmVariableName(targetIdent)
|
||||
@@ -76,7 +75,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
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 |+")
|
||||
@@ -86,14 +84,30 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
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) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
in ByteDatatypes -> {
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname
|
||||
clc
|
||||
adc #$indexValue
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||
@@ -113,11 +127,25 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
else
|
||||
{
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
txa
|
||||
clc
|
||||
adc $asmArrayvarname
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
@@ -127,8 +155,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
lda $asmArrayvarname,x
|
||||
bne +
|
||||
dec $asmArrayvarname+1,x
|
||||
+ dec $asmArrayvarname,x
|
||||
""")
|
||||
+ dec $asmArrayvarname,x""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
@@ -141,7 +168,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,6 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
|
||||
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
|
||||
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
|
||||
asmgen.out(".endweak")
|
||||
|
||||
if(options.symbolDefs.isNotEmpty()) {
|
||||
@@ -371,12 +369,6 @@ internal class ProgramAndVarsGen(
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
||||
asmgen.out("prog8_regsaveA .byte ?")
|
||||
if(asmGenInfo.usedRegsaveX)
|
||||
asmgen.out("prog8_regsaveX .byte ?")
|
||||
if(asmGenInfo.usedRegsaveY)
|
||||
asmgen.out("prog8_regsaveY .byte ?")
|
||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||
@@ -436,12 +428,12 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out("""
|
||||
lda #<${name}_init_value
|
||||
ldy #>${name}_init_value
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
lda #<${name}
|
||||
ldy #>${name}
|
||||
sta cx16.r1L
|
||||
sty cx16.r1H
|
||||
sta cx16.r1
|
||||
sty cx16.r1+1
|
||||
lda #<$size
|
||||
ldy #>$size
|
||||
jsr sys.memcopy""")
|
||||
@@ -460,7 +452,6 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
|
||||
asmgen.out("""+
|
||||
ldx #127 ; init estack ptr (half page)
|
||||
clv
|
||||
clc""")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
//
|
||||
// This contains codegen for stack-based evaluation of binary expressions.
|
||||
// It uses the CPU stack so depth is limited.
|
||||
// It is called "as a last resort" if the optimized codegen path is unable
|
||||
// to come up with a special case of the expression.
|
||||
//
|
||||
internal class AnyExprAsmGen(
|
||||
private val asmgen: AsmGen6502Internal
|
||||
) {
|
||||
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.type) {
|
||||
in ByteDatatypes -> {
|
||||
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
|
||||
return assignByteBinExpr(expr, assign)
|
||||
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
TODO("words operands comparison -> byte")
|
||||
}
|
||||
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
require(expr.operator in ComparisonOperators)
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
TODO("weird expr operand types")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
"both operands must be words"
|
||||
}
|
||||
return assignWordBinExpr(expr)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
|
||||
"both operands must be floats"
|
||||
}
|
||||
return assignFloatBinExpr(expr, assign)
|
||||
}
|
||||
else -> throw AssemblyError("weird expression type in assignment")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
TODO("word + at ${expr.position}")
|
||||
}
|
||||
"-" -> {
|
||||
TODO("word - at ${expr.position}")
|
||||
}
|
||||
"*" -> {
|
||||
TODO("word * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("word / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("word << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("word >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("word % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
TODO("word and at ${expr.position}")
|
||||
}
|
||||
"|", "or" -> {
|
||||
TODO("word or at ${expr.position}")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
TODO("word xor at ${expr.position}")
|
||||
}
|
||||
"==" -> {
|
||||
TODO("word == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("word != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("word < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("word <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("word > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("word >= at ${expr.position}")
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
TODO("byte * at ${expr.position}")
|
||||
}
|
||||
"/" -> {
|
||||
TODO("byte / at ${expr.position}")
|
||||
}
|
||||
"<<" -> {
|
||||
TODO("byte << at ${expr.position}")
|
||||
}
|
||||
">>" -> {
|
||||
TODO("byte >> at ${expr.position}")
|
||||
}
|
||||
"%" -> {
|
||||
TODO("byte % at ${expr.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"==" -> {
|
||||
TODO("byte == at ${expr.position}")
|
||||
}
|
||||
"!=" -> {
|
||||
TODO("byte != at ${expr.position}")
|
||||
}
|
||||
"<" -> {
|
||||
TODO("byte < at ${expr.position}")
|
||||
}
|
||||
"<=" -> {
|
||||
TODO("byte <= at ${expr.position}")
|
||||
}
|
||||
">" -> {
|
||||
TODO("byte > at ${expr.position}")
|
||||
}
|
||||
">=" -> {
|
||||
TODO("byte >= at ${expr.position}")
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
assignFloatOperandsToFACandARG(expr.left, expr.right)
|
||||
asmgen.out(" jsr floats.FADDT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
assignFloatOperandsToFACandARG(expr.right, expr.left)
|
||||
asmgen.out(" jsr floats.FSUBT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"*" -> {
|
||||
assignFloatOperandsToFACandARG(expr.left, expr.right)
|
||||
asmgen.out(" jsr floats.FMULTT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"/" -> {
|
||||
assignFloatOperandsToFACandARG(expr.right, expr.left)
|
||||
asmgen.out(" jsr floats.FDIVT")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
|
||||
return true
|
||||
}
|
||||
"==" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_equal_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"!=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_notequal_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"<" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_less_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
">" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_greater_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
"<=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_lesseq_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
">=" -> {
|
||||
setupFloatComparisonFAC1vsVarAY(expr)
|
||||
asmgen.out(" jsr floats.var_fac1_greatereq_f")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
}
|
||||
else -> TODO("float expression operator ${expr.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
|
||||
when(asmgen.options.compTarget.name) {
|
||||
C64Target.NAME -> {
|
||||
// c64 has a quirk: always make sure FAC2 is loaded last (done using CONUPK) otherwise the result will be corrupt on C64
|
||||
// this requires some more forced copying around of float values in certain cases
|
||||
if (right.isSimple()) {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
|
||||
asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
asmgen.popFAC2()
|
||||
}
|
||||
}
|
||||
Cx16Target.NAME -> {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
|
||||
if (!right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||
if (!right.isSimple()) asmgen.popFAC1()
|
||||
}
|
||||
else -> TODO("don't know how to evaluate float expression for selected compilation target")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,7 @@ internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK
|
||||
REGISTER
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@@ -20,7 +19,6 @@ internal enum class SourceStorageKind {
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK, // value is already present on stack
|
||||
EXPRESSION, // expression in ast-form, still to be evaluated
|
||||
}
|
||||
|
||||
@@ -120,7 +118,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.REGISTER, TargetStorageKind.STACK -> {
|
||||
TargetStorageKind.REGISTER -> {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+738
-541
File diff suppressed because it is too large.
Load diff
@@ -44,7 +44,7 @@ class TestCodegen: FunSpec({
|
||||
//}
|
||||
val codegen = AsmGen6502(prefixSymbols = false)
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||
@@ -94,7 +94,7 @@ class TestCodegen: FunSpec({
|
||||
program.add(block)
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
@@ -106,7 +106,7 @@ class TestCodegen: FunSpec({
|
||||
Files.deleteIfExists(Path("${result.name}.asm"))
|
||||
}
|
||||
|
||||
test("64tass assembler available? - if this fails you need to install 64tass in the path") {
|
||||
test("64tass assembler available? - if this fails you need to install 64tass version 1.58 or newer in the path") {
|
||||
val command = mutableListOf("64tass", "--version")
|
||||
shouldNotThrowAny {
|
||||
val proc = ProcessBuilder(command).start()
|
||||
|
||||
@@ -36,10 +36,10 @@ dependencies {
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
symbolTable: SymbolTable,
|
||||
options: CompilationOptions,
|
||||
errors: IErrorReporter
|
||||
): IAssemblyProgram? {
|
||||
): IAssemblyProgram {
|
||||
|
||||
// you could write a code generator directly on the PtProgram AST,
|
||||
// but you can also use the Intermediate Representation to build a codegen on:
|
||||
|
||||
@@ -30,16 +30,16 @@ dependencies {
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.7.2'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
test {
|
||||
|
||||
@@ -13,7 +13,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(assignment.target.children.single() is PtMachineRegister)
|
||||
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||
|
||||
return translateRegularAssign(assignment)
|
||||
val chunks = translateRegularAssign(assignment)
|
||||
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(assignment.position)
|
||||
return chunks
|
||||
}
|
||||
|
||||
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
@@ -24,7 +26,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val memory = augAssign.target.memory
|
||||
val array = augAssign.target.array
|
||||
|
||||
return if(ident!=null) {
|
||||
val chunks = if(ident!=null) {
|
||||
assignVarAugmented(ident.name, augAssign)
|
||||
} else if(memory != null) {
|
||||
if(memory.address is PtNumber)
|
||||
@@ -39,6 +41,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
} else {
|
||||
fallbackAssign(augAssign)
|
||||
}
|
||||
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun assignMemoryAugmented(
|
||||
@@ -48,15 +52,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val value = assignment.value
|
||||
val vmDt = irType(value.type)
|
||||
return when(assignment.operator) {
|
||||
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
||||
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
||||
"*" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
|
||||
"/" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"|" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
|
||||
"&" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
|
||||
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
||||
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
||||
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"+=" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"|=" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
|
||||
"^=" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||
"%=" -> expressionEval.operatorModuloInplace(address, null, vmDt, value)
|
||||
"==" -> expressionEval.operatorEqualsInplace(address, null, vmDt, value)
|
||||
"!=" -> expressionEval.operatorNotEqualsInplace(address, null, vmDt, value)
|
||||
@@ -96,8 +100,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
|
||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
if (codeGen.options.slowCodegenWarnings)
|
||||
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
||||
val value: PtExpression
|
||||
if(origAssign.operator in PrefixOperators) {
|
||||
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||
@@ -200,8 +202,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val variable = targetArray.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
|
||||
|
||||
if(targetArray.variable.type==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(targetArray.usesPointerVariable) {
|
||||
if(itemsize!=1)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(targetArray.index.type!=DataType.UBYTE)
|
||||
|
||||
@@ -24,10 +24,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"popw" -> funcPopw(call)
|
||||
"push" -> funcPush(call)
|
||||
"pushw" -> funcPushw(call)
|
||||
"rsave",
|
||||
"rsavex",
|
||||
"rrestore",
|
||||
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||
"callfar" -> funcCallfar(call)
|
||||
"msb" -> funcMsb(call)
|
||||
"lsb" -> funcLsb(call)
|
||||
@@ -43,17 +40,38 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
|
||||
"sort" -> funcSort(call)
|
||||
"reverse" -> funcReverse(call)
|
||||
"setlsb" -> funcSetLsbMsb(call, false)
|
||||
"setmsb" -> funcSetLsbMsb(call, true)
|
||||
"rol" -> funcRolRor(Opcode.ROXL, call)
|
||||
"ror" -> funcRolRor(Opcode.ROXR, call)
|
||||
"rol2" -> funcRolRor(Opcode.ROL, call)
|
||||
"ror2" -> funcRolRor(Opcode.ROR, call)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
|
||||
return if(resultType==IRDataType.FLOAT) {
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null)
|
||||
ExpressionCodeResult(result, resultType, -1, resultFpReg)
|
||||
}
|
||||
else {
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null)
|
||||
ExpressionCodeResult(result, resultType, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||
@@ -95,6 +113,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
@@ -129,6 +148,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
else -> throw IllegalArgumentException("weird type")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
@@ -150,6 +170,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
else -> throw IllegalArgumentException("weird type")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
@@ -293,6 +314,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
else -> throw IllegalArgumentException("weird type to reverse")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
@@ -316,6 +338,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
else -> throw IllegalArgumentException("weird type to sort")
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
@@ -339,6 +362,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val type = irType(call.type)
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||
@@ -534,6 +558,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
return exprGen.translateExpression(call.args.single())
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
// TODO to be more strict, maybe we *should* introduce a new result register that is of type .b?
|
||||
}
|
||||
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -560,6 +585,122 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||
}
|
||||
|
||||
private fun funcSetLsbMsb(call: PtBuiltinFunctionCall, msb: Boolean): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val target = call.args[0]
|
||||
val isConstZeroValue = call.args[1].asConstInteger()==0
|
||||
when(target) {
|
||||
is PtIdentifier -> {
|
||||
if(isConstZeroValue) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val pointerReg = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
||||
if (msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
||||
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = pointerReg)
|
||||
}
|
||||
} else {
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val pointerReg = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
|
||||
if (msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
|
||||
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = pointerReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
require(!target.usesPointerVariable)
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = varName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = varName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(target.type)
|
||||
val constIndex = target.index.asConstInteger()
|
||||
if(isConstZeroValue) {
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
if(constIndex!=null) {
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
val offset = eltSize*constIndex + if(msb) 1 else 0
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
} else {
|
||||
val indexTr = exprGen.translateExpression(target.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(eltSize>1)
|
||||
it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize)
|
||||
if(msb)
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird target for setlsb/setmsb: $target")
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, -1, -1)
|
||||
}
|
||||
|
||||
|
||||
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
|
||||
val assignment = PtAssignment(target.position)
|
||||
val assignTarget = PtAssignTarget(target.position)
|
||||
|
||||
@@ -3,10 +3,7 @@ package prog8.codegen.intermediate
|
||||
import prog8.code.StRomSub
|
||||
import prog8.code.StSub
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.PassByValueDatatypes
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
||||
internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) {
|
||||
@@ -69,10 +66,28 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
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)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol)
|
||||
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
|
||||
if(expr.isFromArrayElement) {
|
||||
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
if(expr.identifier.type in SplitWordArrayTypes) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
// multiply indexTr resultreg by the eltSize and add this to the resultRegister.
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.BYTE, reg1=resultRegister, reg2=indexTr.resultReg)
|
||||
}
|
||||
} else {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
// multiply indexTr resultreg by the eltSize and add this to the resultRegister.
|
||||
if(eltSize>1)
|
||||
it += IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=indexTr.resultReg, immediate = eltSize)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.BYTE, reg1=resultRegister, reg2=indexTr.resultReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExpressionCodeResult(result, vmDt, resultRegister, -1)
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@@ -107,6 +122,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
when(check.iterable.type) {
|
||||
DataType.STR -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
@@ -115,6 +131,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
@@ -126,6 +143,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
@@ -147,8 +165,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arrayVarSymbol = arrayIx.variable.name
|
||||
|
||||
if(arrayIx.variable.type==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(arrayIx.usesPointerVariable) {
|
||||
if(eltSize!=1)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(arrayIx.index.type!=DataType.UBYTE)
|
||||
@@ -367,6 +384,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@@ -387,8 +405,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
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)
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec)), null)
|
||||
return if(fcall.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else if(fcall.type==DataType.FLOAT)
|
||||
@@ -398,6 +415,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
is StRomSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@@ -544,14 +562,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
val label = codeGen.createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 1), null)
|
||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
if (notEquals) {
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=valueReg, immediate = 0, labelSymbol = label), null)
|
||||
} else {
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=valueReg, immediate = 0, labelSymbol = label), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 1)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg)
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=valueReg, immediate = 0)
|
||||
it += if (notEquals)
|
||||
IRInstruction(Opcode.BSTNE, labelSymbol = label)
|
||||
else
|
||||
IRInstruction(Opcode.BSTEQ, labelSymbol = label)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0)
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0), null)
|
||||
result += IRCodeChunk(label, null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
|
||||
@@ -29,9 +29,6 @@ class IRCodeGen(
|
||||
irSymbolTable = IRSymbolTable(symbolTable)
|
||||
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||
|
||||
if(options.evalStackBaseAddress!=null)
|
||||
throw AssemblyError("IR doesn't use eval-stack")
|
||||
|
||||
// collect global variables initializers
|
||||
program.allBlocks().forEach {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@@ -292,6 +289,8 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
|
||||
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(node.position)
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
||||
@@ -416,13 +415,19 @@ class IRCodeGen(
|
||||
if(choice.statements.children.isEmpty()) {
|
||||
// no statements for this choice value, jump to the end immediately
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = endLabel), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val choiceLabel = createLabelName()
|
||||
choices.add(choiceLabel to choice)
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,7 +470,8 @@ class IRCodeGen(
|
||||
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.CMPI, IRDataType.BYTE, reg1 = tmpReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
result += translateNode(forLoop.statements)
|
||||
@@ -493,7 +499,8 @@ class IRCodeGen(
|
||||
result += translateNode(forLoop.statements)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
|
||||
it += IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(iterableLength==256) 0 else iterableLength, labelSymbol = loopLabel)
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=indexReg, immediate = if(iterableLength==256) 0 else iterableLength)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -508,7 +515,10 @@ class IRCodeGen(
|
||||
}
|
||||
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)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=indexReg, immediate = if(lengthBytes==256) 0 else lengthBytes)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,8 +566,11 @@ class IRCodeGen(
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
if(step==1 || step==-1) {
|
||||
// if endvalue == loopvar, stop loop, else iterate
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.BEQR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol = labelAfterFor), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.CMP, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = labelAfterFor)
|
||||
}
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = loopLabel), null)
|
||||
} else {
|
||||
@@ -587,25 +600,23 @@ class IRCodeGen(
|
||||
else -> throw AssemblyError("invalid loopvar node type")
|
||||
}
|
||||
val loopvarDtIr = irType(loopvarDt)
|
||||
val iterable = forLoop.iterable as PtRange
|
||||
val step = iterable.step.number.toInt()
|
||||
val rangeStart = (iterable.from as PtNumber).number.toInt()
|
||||
val rangeEndUntyped = (iterable.to as PtNumber).number.toInt() + step
|
||||
if(step==0)
|
||||
throw AssemblyError("step 0")
|
||||
if(step>0 && rangeEndUntyped<rangeStart || step<0 && rangeEndUntyped>rangeStart)
|
||||
val iterable = (forLoop.iterable as PtRange).toConstantIntegerRange()!!
|
||||
if(iterable.isEmpty())
|
||||
throw AssemblyError("empty range")
|
||||
val rangeEndWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndUntyped and 255 else rangeEndUntyped and 65535
|
||||
if(iterable.step==0)
|
||||
throw AssemblyError("step 0")
|
||||
val rangeEndExclusiveUntyped = iterable.last + iterable.step
|
||||
val rangeEndExclusiveWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndExclusiveUntyped and 255 else rangeEndExclusiveUntyped and 65535
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = rangeStart)
|
||||
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = iterable.first)
|
||||
chunk += IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||
result += chunk
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
val chunk2 = IRCodeChunk(null, null)
|
||||
val chunk2 = addConstMem(loopvarDtIr, null, loopvarSymbol, iterable.step)
|
||||
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
chunk2 += IRInstruction(Opcode.BNE, loopvarDtIr, reg1 = indexReg, immediate = rangeEndWrapped, labelSymbol = loopLabel)
|
||||
chunk2 += IRInstruction(Opcode.CMPI, loopvarDtIr, reg1 = indexReg, immediate = rangeEndExclusiveWrapped)
|
||||
chunk2 += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
result += chunk2
|
||||
return result
|
||||
}
|
||||
@@ -964,21 +975,33 @@ class IRCodeGen(
|
||||
fpReg1 = leftTr.resultFpReg,
|
||||
fpReg2 = rightTr.resultFpReg
|
||||
)
|
||||
val gotoOpcode = when (condition.operator) {
|
||||
"==" -> Opcode.BEQ
|
||||
"!=" -> Opcode.BNE
|
||||
"<" -> Opcode.BLTS
|
||||
">" -> Opcode.BGTS
|
||||
"<=" -> Opcode.BLES
|
||||
">=" -> Opcode.BGES
|
||||
else -> throw AssemblyError("weird operator")
|
||||
when(condition.operator) {
|
||||
// TODO: the converted list of operators
|
||||
"==" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += branchInstr(goto, Opcode.BSTEQ)
|
||||
}
|
||||
"!=" -> {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += branchInstr(goto, Opcode.BSTNE)
|
||||
}
|
||||
else -> {
|
||||
// TODO: the old list of operators, still to be converted
|
||||
val gotoOpcode = when (condition.operator) {
|
||||
"<" -> Opcode.BLTS
|
||||
">" -> Opcode.BGTS
|
||||
"<=" -> Opcode.BLES
|
||||
">=" -> Opcode.BGES
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
it += if (goto.address != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
|
||||
else if (goto.generatedLabel != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.generatedLabel)
|
||||
else
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
|
||||
}
|
||||
}
|
||||
it += if (goto.address != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
|
||||
else if (goto.generatedLabel != null)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.generatedLabel)
|
||||
else
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
@@ -993,6 +1016,14 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO use this everywhere
|
||||
private fun branchInstr(goto: PtJump, branchOpcode: Opcode) = if (goto.address != null)
|
||||
IRInstruction(branchOpcode, address = goto.address?.toInt())
|
||||
else if (goto.generatedLabel != null)
|
||||
IRInstruction(branchOpcode, labelSymbol = goto.generatedLabel)
|
||||
else
|
||||
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
|
||||
|
||||
private fun ifZeroIntThenJump(
|
||||
result: MutableList<IRCodeChunkBase>,
|
||||
ifElse: PtIfElse,
|
||||
@@ -1003,21 +1034,33 @@ class IRCodeGen(
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val opcode = when (condition.operator) {
|
||||
"==" -> Opcode.BEQ
|
||||
"!=" -> Opcode.BNE
|
||||
"<" -> if (signed) Opcode.BLTS else Opcode.BLT
|
||||
">" -> if (signed) Opcode.BGTS else Opcode.BGT
|
||||
"<=" -> if (signed) Opcode.BLES else Opcode.BLE
|
||||
">=" -> if (signed) Opcode.BGES else Opcode.BGE
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
when(condition.operator) {
|
||||
// TODO: converted list of operators
|
||||
"==" -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = leftTr.resultReg, immediate = 0), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTEQ), null)
|
||||
}
|
||||
"!=" -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = leftTr.resultReg, immediate = 0), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
|
||||
}
|
||||
else -> {
|
||||
// TODO: to-be converted operators
|
||||
val opcode = when (condition.operator) {
|
||||
"<" -> if (signed) Opcode.BLTS else Opcode.BLT
|
||||
">" -> if (signed) Opcode.BGTS else Opcode.BGT
|
||||
"<=" -> if (signed) Opcode.BLES else Opcode.BLE
|
||||
">=" -> if (signed) Opcode.BGES else Opcode.BGE
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
|
||||
private fun ifNonZeroIntThenJump(
|
||||
@@ -1031,47 +1074,60 @@ class IRCodeGen(
|
||||
if(condition==null) {
|
||||
val tr = expressionEval.translateExpression(ifElse.condition)
|
||||
result += tr.chunks
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, irDtLeft, reg1 = tr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, irDtLeft, reg1 = tr.resultReg, immediate = 0, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.BNE, irDtLeft, reg1 = tr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, irDtLeft, reg1 = tr.resultReg, immediate = 0)
|
||||
it += branchInstr(goto, Opcode.BSTNE)
|
||||
}
|
||||
} else {
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val opcode: Opcode
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
if(number!=null) {
|
||||
val firstReg = leftTr.resultReg
|
||||
opcode = when (condition.operator) {
|
||||
"==" -> Opcode.BEQ
|
||||
"!=" -> Opcode.BNE
|
||||
"<" -> if(signed) Opcode.BLTS else Opcode.BLT
|
||||
">" -> if(signed) Opcode.BGTS else Opcode.BGT
|
||||
"<=" -> if(signed) Opcode.BLES else Opcode.BLE
|
||||
">=" -> if(signed) Opcode.BGES else Opcode.BGE
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
when(condition.operator) {
|
||||
// TODO: the converted operators
|
||||
"==" -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = firstReg, immediate = number), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTEQ), null)
|
||||
}
|
||||
"!=" -> {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, irDtLeft, reg1 = firstReg, immediate = number), null)
|
||||
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
|
||||
}
|
||||
else -> {
|
||||
// TODO: to-be converted operators
|
||||
val opcode = when (condition.operator) {
|
||||
"<" -> if(signed) Opcode.BLTS else Opcode.BLT
|
||||
">" -> if(signed) Opcode.BGTS else Opcode.BGT
|
||||
"<=" -> if(signed) Opcode.BLES else Opcode.BLE
|
||||
">=" -> if(signed) Opcode.BGES else Opcode.BGE
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
|
||||
} else {
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val firstReg: Int
|
||||
val secondReg: Int
|
||||
val opcode: Opcode
|
||||
var useCmp = false
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
opcode = Opcode.BEQR
|
||||
useCmp = true
|
||||
opcode = Opcode.BSTEQ
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
"!=" -> {
|
||||
opcode = Opcode.BNER
|
||||
useCmp = true
|
||||
opcode = Opcode.BSTNE
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
@@ -1099,12 +1155,20 @@ class IRCodeGen(
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMP, irDtLeft, reg1 = firstReg, reg2 = secondReg)
|
||||
it += branchInstr(goto, opcode)
|
||||
}
|
||||
} else {
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1112,6 +1176,7 @@ class IRCodeGen(
|
||||
private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val elseBranch: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
val compResultReg: Int
|
||||
val branchDt: IRDataType
|
||||
val condition = ifElse.condition as PtBinaryExpression
|
||||
@@ -1125,13 +1190,19 @@ class IRCodeGen(
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = rightFpReg, immediateFp = 0f)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightFpReg)
|
||||
}
|
||||
elseBranch = when (condition.operator) {
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> Opcode.BGES
|
||||
">" -> Opcode.BLES
|
||||
"<=" -> Opcode.BGTS
|
||||
">=" -> Opcode.BLTS
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = Opcode.BGES
|
||||
">" -> elseBranch = Opcode.BLES
|
||||
"<=" -> elseBranch = Opcode.BGTS
|
||||
">=" -> elseBranch = Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
} else {
|
||||
@@ -1140,13 +1211,19 @@ class IRCodeGen(
|
||||
val tr = expressionEval.translateExpression(condition.left)
|
||||
compResultReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
elseBranch = when (condition.operator) {
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> if (signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> if (signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> if (signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> if (signed) Opcode.BLTS else Opcode.BLT
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = if (signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> elseBranch = if (signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> elseBranch = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> elseBranch = if (signed) Opcode.BLTS else Opcode.BLT
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
}
|
||||
@@ -1154,14 +1231,26 @@ class IRCodeGen(
|
||||
if(ifElse.elseScope.children.isEmpty()) {
|
||||
// just if
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1=compResultReg, immediate = 0)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// if and else
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1=compResultReg, immediate = 0)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
@@ -1172,31 +1261,39 @@ class IRCodeGen(
|
||||
|
||||
private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val elseBranchOpcode: Opcode
|
||||
val elseBranchFirstReg: Int
|
||||
val elseBranchSecondReg: Int
|
||||
val branchDt: IRDataType
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null) {
|
||||
if(irDtLeft==IRDataType.FLOAT)
|
||||
throw AssemblyError("condition value should not be float")
|
||||
val tr = expressionEval.translateExpression(ifElse.condition)
|
||||
result += tr.chunks
|
||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, irDtLeft, reg1=tr.resultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, irDtLeft, reg1=tr.resultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
return result
|
||||
throw AssemblyError("if-else condition is not a binaryexpression, should have been converted?")
|
||||
// if(irDtLeft==IRDataType.FLOAT)
|
||||
// throw AssemblyError("condition value should not be float")
|
||||
// val tr = expressionEval.translateExpression(ifElse.condition)
|
||||
// result += tr.chunks
|
||||
// if(ifElse.elseScope.children.isNotEmpty()) {
|
||||
// val elseLabel = createLabelName()
|
||||
// result += IRCodeChunk(null, null).also {
|
||||
// it += IRInstruction(Opcode.CMPI, irDtLeft, reg1=tr.resultReg, immediate = 0)
|
||||
// it += IRInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
|
||||
// TODO("test this")
|
||||
// }
|
||||
// result += translateNode(ifElse.ifScope)
|
||||
// val afterIfLabel = createLabelName()
|
||||
// addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
// result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
// result += IRCodeChunk(afterIfLabel, null)
|
||||
// } else {
|
||||
// val afterIfLabel = createLabelName()
|
||||
// result += IRCodeChunk(null, null).also {
|
||||
// it += IRInstruction(Opcode.CMPI, irDtLeft, reg1=tr.resultReg, immediate = 0)
|
||||
// it += IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel)
|
||||
// TODO("test this")
|
||||
// }
|
||||
// result += translateNode(ifElse.ifScope)
|
||||
// result += IRCodeChunk(afterIfLabel, null)
|
||||
// }
|
||||
// return result
|
||||
} else {
|
||||
if (irDtLeft == IRDataType.FLOAT) {
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
@@ -1204,35 +1301,35 @@ class IRCodeGen(
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
val compResultReg = registers.nextFree()
|
||||
addInstr(
|
||||
result,
|
||||
IRInstruction(
|
||||
Opcode.FCOMP,
|
||||
IRDataType.FLOAT,
|
||||
reg1 = compResultReg,
|
||||
fpReg1 = leftTr.resultFpReg,
|
||||
fpReg2 = rightTr.resultFpReg
|
||||
),
|
||||
null
|
||||
)
|
||||
val elseBranch = when (condition.operator) {
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> Opcode.BGES
|
||||
">" -> Opcode.BLES
|
||||
"<=" -> Opcode.BGTS
|
||||
">=" -> Opcode.BLTS
|
||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1 = compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
val elseBranch: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = Opcode.BGES
|
||||
">" -> elseBranch = Opcode.BLES
|
||||
"<=" -> elseBranch = Opcode.BGTS
|
||||
">=" -> elseBranch = Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result,
|
||||
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = elseLabel),
|
||||
null
|
||||
)
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
@@ -1240,11 +1337,13 @@ class IRCodeGen(
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result,
|
||||
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel),
|
||||
null
|
||||
)
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = compResultReg, immediate = 0)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
@@ -1255,26 +1354,34 @@ class IRCodeGen(
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
if (number!=null) {
|
||||
elseBranchOpcode = when (condition.operator) {
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> if(signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> if(signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> if(signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> if(signed) Opcode.BLTS else Opcode.BLT
|
||||
val elseBranch: Opcode
|
||||
var useCmpi = false // for the branch opcodes that have been converted to CMPI + BSTxx form already
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranch = Opcode.BSTNE
|
||||
useCmpi = true
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranch = Opcode.BSTEQ
|
||||
useCmpi = true
|
||||
}
|
||||
"<" -> elseBranch = if(signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> elseBranch = if(signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> elseBranch = if(signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> elseBranch = if(signed) Opcode.BLTS else Opcode.BLT
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = leftTr.resultReg, immediate = number,
|
||||
labelSymbol = elseLabel
|
||||
), null
|
||||
)
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1 = leftTr.resultReg, immediate = number)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = leftTr.resultReg, immediate = number, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
@@ -1282,52 +1389,56 @@ class IRCodeGen(
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = leftTr.resultReg, immediate = number,
|
||||
labelSymbol = afterIfLabel
|
||||
), null
|
||||
)
|
||||
if(useCmpi) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, branchDt, reg1 = leftTr.resultReg, immediate = number)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = leftTr.resultReg, immediate = number, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
} else {
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
val elseBranch: Opcode
|
||||
var useCmp = false
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranchOpcode = Opcode.BNER
|
||||
useCmp = true
|
||||
elseBranch = Opcode.BSTNE
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranchOpcode = Opcode.BEQR
|
||||
useCmp = true
|
||||
elseBranch = Opcode.BSTEQ
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
"<" -> {
|
||||
// else part when left >= right
|
||||
elseBranchOpcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranch = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
">" -> {
|
||||
// else part when left <= right --> right >= left
|
||||
elseBranchOpcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranch = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
"<=" -> {
|
||||
// else part when left > right
|
||||
elseBranchOpcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranch = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
|
||||
">=" -> {
|
||||
// else part when left < right --> right > left
|
||||
elseBranchOpcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranch = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
@@ -1337,13 +1448,14 @@ class IRCodeGen(
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||
labelSymbol = elseLabel
|
||||
), null
|
||||
)
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.CMP, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg)
|
||||
it += IRInstruction(elseBranch, labelSymbol = elseLabel)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg, labelSymbol = elseLabel), null)
|
||||
}
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
@@ -1351,13 +1463,14 @@ class IRCodeGen(
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||
labelSymbol = afterIfLabel
|
||||
), null
|
||||
)
|
||||
if(useCmp) {
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.CMP, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg)
|
||||
it += IRInstruction(elseBranch, labelSymbol = afterIfLabel)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg, labelSymbol = afterIfLabel), null)
|
||||
}
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
@@ -1472,7 +1585,19 @@ class IRCodeGen(
|
||||
val fixedIndex = constIntValue(array.index)
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
|
||||
val indexReg = registers.nextFree()
|
||||
val dataReg = registers.nextFree()
|
||||
if(array.usesPointerVariable) {
|
||||
// we don't have an indirect dec/inc so do it via an intermediate register
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.LOADIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=dataReg)
|
||||
it += IRInstruction(Opcode.STOREIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = "$variable+$offset"), null)
|
||||
}
|
||||
} else {
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
@@ -1480,9 +1605,15 @@ class IRCodeGen(
|
||||
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)
|
||||
if(array.usesPointerVariable) {
|
||||
it += IRInstruction(Opcode.LOADIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
} else {
|
||||
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
|
||||
@@ -1510,12 +1641,15 @@ class IRCodeGen(
|
||||
addToResult(result, countTr, countTr.resultReg, -1)
|
||||
if(constIntValue(repeat.count)==null) {
|
||||
// check if the counter is already zero
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = skipRepeatLabel), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, irDt, reg1=countTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipRepeatLabel)
|
||||
}
|
||||
}
|
||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||
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)
|
||||
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg) // sets status bits
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = repeatLabel)
|
||||
}
|
||||
result += IRCodeChunk(skipRepeatLabel, null)
|
||||
return result
|
||||
@@ -1655,24 +1789,6 @@ class IRCodeGen(
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
|
||||
|
||||
fun getReusableTempvar(scope: PtNamedNode, type: DataType): PtIdentifier {
|
||||
val uniqueId = Pair(scope, type).hashCode().toUInt()
|
||||
val tempvarname = "${scope.scopedName}.tempvar_${uniqueId}"
|
||||
val tempvar = PtIdentifier(tempvarname, type, Position.DUMMY)
|
||||
val staticVar = StStaticVariable(
|
||||
tempvarname,
|
||||
type,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
ZeropageWish.DONTCARE,
|
||||
tempvar
|
||||
)
|
||||
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)->
|
||||
|
||||
@@ -105,7 +105,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|
||||
relabelChunks.forEach { (index, label) ->
|
||||
val chunk = IRCodeChunk(label, null)
|
||||
chunk.instructions += sub.chunks[index].instructions
|
||||
val subChunk = sub.chunks[index]
|
||||
chunk.instructions += subChunk.instructions
|
||||
if(subChunk is IRCodeChunk)
|
||||
chunk.appendSrcPositions(subChunk.sourceLinesPositions)
|
||||
sub.chunks[index] = chunk
|
||||
}
|
||||
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
|
||||
@@ -140,6 +143,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(mayJoinCodeChunks(lastChunk, candidate)) {
|
||||
lastChunk.instructions += candidate.instructions
|
||||
lastChunk.next = candidate.next
|
||||
if(lastChunk is IRCodeChunk)
|
||||
lastChunk.appendSrcPositions(candidate.sourceLinesPositions)
|
||||
}
|
||||
else
|
||||
chunks += candidate
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@@ -25,18 +25,29 @@ class IRUnusedCodeRemover(
|
||||
}
|
||||
|
||||
private fun pruneSymboltable(blockLabel: String) {
|
||||
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction
|
||||
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
|
||||
val prefix = "$blockLabel."
|
||||
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
|
||||
blockVars.forEach { stVar ->
|
||||
irprog. allSubs().flatMap { it.chunks }.forEach { chunk ->
|
||||
irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
|
||||
chunk.instructions.forEach { ins ->
|
||||
if(ins.labelSymbol == stVar.name) {
|
||||
return
|
||||
return // symbol occurs in an instruction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
irprog.st.allVariables().forEach { stVar->
|
||||
val initValue = stVar.onetimeInitializationArrayValue
|
||||
if(initValue!=null && !initValue.isEmpty()) {
|
||||
if(initValue.any {
|
||||
it.addressOfSymbol?.startsWith(blockLabel)==true
|
||||
})
|
||||
return // symbol occurs in an initializer value (address-of this symbol)_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
irprog.st.removeTree(blockLabel)
|
||||
}
|
||||
|
||||
@@ -50,7 +61,7 @@ class IRUnusedCodeRemover(
|
||||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(libraryFilePrefix)) {
|
||||
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
|
||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
@@ -71,7 +82,7 @@ class IRUnusedCodeRemover(
|
||||
irprog.blocks.forEach { block ->
|
||||
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(libraryFilePrefix)) {
|
||||
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
|
||||
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
@@ -207,9 +218,7 @@ class IRUnusedCodeRemover(
|
||||
return removeUnlinkedChunks(linkedChunks)
|
||||
}
|
||||
|
||||
private fun removeUnlinkedChunks(
|
||||
linkedChunks: Set<IRCodeChunkBase>
|
||||
): Int {
|
||||
private fun removeUnlinkedChunks(linkedChunks: Set<IRCodeChunkBase>): Int {
|
||||
var numRemoved = 0
|
||||
irprog.foreachSub { sub ->
|
||||
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
|
||||
|
||||
@@ -16,7 +16,7 @@ class VmCodeGen: ICodeGeneratorBackend {
|
||||
symbolTable: SymbolTable,
|
||||
options: CompilationOptions,
|
||||
errors: IErrorReporter
|
||||
): IAssemblyProgram? {
|
||||
): IAssemblyProgram {
|
||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||
val irProgram = irCodeGen.generate()
|
||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||
|
||||
@@ -41,7 +41,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||
@@ -91,7 +91,7 @@ class TestVmCodeGen: FunSpec({
|
||||
program.add(block)
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
@@ -120,7 +120,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
@@ -183,7 +183,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
@@ -242,7 +242,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
@@ -289,7 +289,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
@@ -352,7 +352,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
@@ -411,7 +411,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
@@ -451,7 +451,7 @@ class TestVmCodeGen: FunSpec({
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), 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)
|
||||
@@ -466,7 +466,11 @@ class TestVmCodeGen: FunSpec({
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
val callInstr = irChunks.single().instructions.single()
|
||||
irChunks[0].instructions.size shouldBe 2
|
||||
val preparecallInstr = irChunks[0].instructions[0]
|
||||
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
|
||||
preparecallInstr.immediate shouldBe 0
|
||||
val callInstr = irChunks[0].instructions[1]
|
||||
callInstr.opcode shouldBe Opcode.CALL
|
||||
callInstr.address shouldBe 0x5000
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ dependencies {
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.AssignmentOrigin
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.AugmentAssignmentOperators
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
class BinExprSplitter(private val program: Program, private val options: CompilationOptions) : AstWalker() {
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(options.compTarget.name == VMTarget.NAME)
|
||||
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
|
||||
|
||||
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
|
||||
return noModifications
|
||||
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if (binExpr != null) {
|
||||
|
||||
if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
|
||||
if(assignment.target isSameAs binExpr.right)
|
||||
return noModifications
|
||||
if(assignment.target isSameAs binExpr.left) {
|
||||
if(binExpr.right.isSimple)
|
||||
return noModifications
|
||||
val leftBx = binExpr.left as? BinaryExpression
|
||||
if(leftBx!=null && (!leftBx.left.isSimple || !leftBx.right.isSimple))
|
||||
return noModifications
|
||||
val rightBx = binExpr.right as? BinaryExpression
|
||||
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(binExpr.right.isSimple) {
|
||||
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, AssignmentOrigin.OPTIMIZER, binExpr.left.position)
|
||||
val targetExpr = assignment.target.toExpression()
|
||||
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
|
||||
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Further unraveling of binary expressions is really complicated here and
|
||||
// often results in much bigger code, thereby defeating the purpose a bit.
|
||||
// All in all this should probably be fixed in a better code generation backend
|
||||
// that doesn't require this at all.
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun isSimpleTarget(target: AssignTarget) =
|
||||
if (target.identifier!=null || target.memoryAddress!=null)
|
||||
!target.isIOAddress(options.compTarget.machine)
|
||||
else
|
||||
false
|
||||
|
||||
}
|
||||
@@ -19,12 +19,13 @@ import kotlin.math.floor
|
||||
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
|
||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
// @( &thing ) --> thing
|
||||
// @( &thing ) --> thing (but only if thing is a byte type!)
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
return if(addrOf!=null)
|
||||
listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||
else
|
||||
noModifications
|
||||
if(addrOf!=null) {
|
||||
if(addrOf.identifier.inferType(program).isBytes)
|
||||
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
||||
@@ -235,6 +236,47 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
}
|
||||
}
|
||||
|
||||
if(rightconst!=null && (expr.operator=="<<" || expr.operator==">>")) {
|
||||
val dt = expr.left.inferType(program)
|
||||
if(dt.isBytes && rightconst.number>=8) {
|
||||
if(dt.istype(DataType.UBYTE)) {
|
||||
val zeroUB = NumericLiteral(DataType.UBYTE, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUB, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroB = NumericLiteral(DataType.BYTE, 0.0, expr.position)
|
||||
val minusoneB = NumericLiteral(DataType.BYTE, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneB, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(dt.isWords && rightconst.number>=16) {
|
||||
if(dt.istype(DataType.UWORD)) {
|
||||
val zeroUW = NumericLiteral(DataType.UWORD, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUW, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroW = NumericLiteral(DataType.WORD, 0.0, expr.position)
|
||||
val minusoneW = NumericLiteral(DataType.WORD, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneW, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modifications
|
||||
}
|
||||
@@ -587,5 +629,4 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -276,101 +276,10 @@ 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_W_SPLIT, DataType.ARRAY_UW_SPLIT -> {
|
||||
val rangeExpr = decl.value as? RangeExpression
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
if(constRange!=null) {
|
||||
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||
val newValue = if(eltType in ByteDatatypes) {
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
} else {
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
}
|
||||
val numericLv = decl.value as? NumericLiteral
|
||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||
errors.err("arraysize requires only integers here", numericLv.position)
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
if (rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||
val fillvalue = numericLv.number.toInt()
|
||||
when(decl.datatype){
|
||||
DataType.ARRAY_UB -> {
|
||||
if(fillvalue !in 0..255)
|
||||
errors.err("ubyte value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
if(fillvalue !in -128..127)
|
||||
errors.err("byte value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
if(fillvalue !in 0..65535)
|
||||
errors.err("uword value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
if(fillvalue !in -32768..32767)
|
||||
errors.err("word value overflow", numericLv.position)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val rangeExpr = decl.value as? RangeExpression
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array of floats
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
if(constRange!=null) {
|
||||
val newValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||
constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
}
|
||||
|
||||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
if(rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = numericLv.number
|
||||
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
errors.err("float value overflow", numericLv.position)
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_BOOL -> {
|
||||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
if(numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
val replacedArrayInitializer = createConstArrayInitializerValue(decl)
|
||||
if(replacedArrayInitializer!=null)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, replacedArrayInitializer, decl))
|
||||
}
|
||||
else -> {
|
||||
// nothing to do for this type
|
||||
@@ -380,4 +289,109 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun createConstArrayInitializerValue(decl: VarDecl): ArrayLiteral? {
|
||||
// convert the initializer range expression from a range or int, to an actual array.
|
||||
|
||||
when(decl.datatype) {
|
||||
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) {
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
if(constRange?.isEmpty()==true) {
|
||||
if(constRange.first>constRange.last && constRange.step>=0)
|
||||
errors.err("descending range with positive step", decl.value?.position!!)
|
||||
else if(constRange.first<constRange.last && constRange.step<=0)
|
||||
errors.err("ascending range with negative step", decl.value?.position!!)
|
||||
}
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
if(constRange!=null) {
|
||||
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||
return if(eltType in ByteDatatypes) {
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
} else {
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
val numericLv = decl.value as? NumericLiteral
|
||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||
errors.err("arraysize requires only integers here", numericLv.position)
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if (rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||
val fillvalue = numericLv.number.toInt()
|
||||
when(decl.datatype){
|
||||
DataType.ARRAY_UB -> {
|
||||
if(fillvalue !in 0..255)
|
||||
errors.err("ubyte value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
if(fillvalue !in -128..127)
|
||||
errors.err("byte value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
if(fillvalue !in 0..65535)
|
||||
errors.err("uword value overflow", numericLv.position)
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
if(fillvalue !in -32768..32767)
|
||||
errors.err("word value overflow", numericLv.position)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val rangeExpr = decl.value as? RangeExpression
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array of floats
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
if(constRange!=null) {
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||
constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
}
|
||||
}
|
||||
|
||||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if(rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = numericLv.number
|
||||
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
errors.err("float value overflow", numericLv.position)
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_BOOL -> {
|
||||
val numericLv = decl.value as? NumericLiteral
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if(numericLv!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray<Expression>()
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
|
||||
}
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,3 @@ fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarg
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.splitBinaryExpressions(options: CompilationOptions) : Int {
|
||||
val opti = BinExprSplitter(this, options)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
arg.identifier
|
||||
if(arg.arrayIndex==null) arg.identifier else null
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
@@ -111,8 +111,6 @@ class StatementOptimizer(private val program: Program,
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
if(options.slowCodegenWarnings)
|
||||
errors.warn("else can be omitted", ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
@@ -386,7 +384,8 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
override fun before(unrollLoop: UnrollLoop, parent: Node): Iterable<IAstModification> {
|
||||
return if(unrollLoop.iterations<1)
|
||||
val iterations = unrollLoop.iterations.constValue(program)?.number?.toInt()
|
||||
return if(iterations!=null && iterations<1)
|
||||
listOf(IAstModification.Remove(unrollLoop, parent as IStatementContainer))
|
||||
else
|
||||
noModifications
|
||||
|
||||
@@ -33,14 +33,14 @@ dependencies {
|
||||
implementation project(':codeGenIntermediate')
|
||||
implementation project(':codeGenExperimental')
|
||||
implementation project(':virtualmachine')
|
||||
implementation "org.antlr:antlr4-runtime:4.13.0"
|
||||
implementation "org.antlr:antlr4-runtime:4.13.1"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6'
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||
|
||||
testImplementation project(':intermediate')
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.7.2'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
@@ -59,10 +59,10 @@ configurations {
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
srcDir "${project.projectDir}/src"
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
srcDir "${project.projectDir}/res"
|
||||
}
|
||||
}
|
||||
test {
|
||||
|
||||
@@ -32,7 +32,7 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
; (assumes screen and color matrix are at their default addresses)
|
||||
; TODO
|
||||
@@ -42,7 +42,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
; TODO
|
||||
@@ -114,10 +114,10 @@ asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
}
|
||||
|
||||
|
||||
romsub $F2B0 = outchar(ubyte char @ A)
|
||||
romsub $F2B0 = outchar(ubyte character @ A)
|
||||
romsub $F2Fd = waitkey()
|
||||
|
||||
asmsub chrout(ubyte char @ A) {
|
||||
asmsub chrout(ubyte character @ A) {
|
||||
%asm {{
|
||||
sta _tmp_outchar+1
|
||||
txa
|
||||
@@ -153,10 +153,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@@ -164,16 +163,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr chrout
|
||||
txa
|
||||
jsr chrout
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@@ -189,16 +185,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr chrout
|
||||
_ones txa
|
||||
jsr chrout
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@@ -210,10 +203,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'$'
|
||||
@@ -222,16 +214,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr chrout
|
||||
tya
|
||||
jsr chrout
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp chrout
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'%'
|
||||
@@ -244,12 +233,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr chrout
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,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 +249,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -274,10 +262,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@@ -285,17 +272,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr chrout
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@@ -316,7 +300,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
@@ -380,7 +364,7 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte char, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; TODO
|
||||
%asm {{
|
||||
@@ -388,7 +372,7 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) {
|
||||
; ---- set cursor at specific position
|
||||
; TODO
|
||||
%asm {{
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
%import syslib
|
||||
%import textio
|
||||
|
||||
; Bitmap pixel graphics module for the Commodore 128
|
||||
|
||||
; TODO c128 actually implement the graphics routines. Ideally a way to 'borrow' the code form the C64 version without just copy-pasting that here?
|
||||
|
||||
graphics {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const uword WIDTH = 320
|
||||
const ubyte HEIGHT = 200
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
; TODO
|
||||
}
|
||||
|
||||
|
||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub fillrect(uword x, uword y, uword width, uword height) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub rect(uword x, uword y, uword width, uword height) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, uword y, uword length) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword height) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
nop ; TODO
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798
|
||||
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 $FFD2 = CHROUT(ubyte character @ 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
|
||||
@@ -104,34 +104,26 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
asmsub STOP2() clobbers(X) -> 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
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
asmsub RDTIM16() clobbers(X) -> 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
|
||||
}}
|
||||
}
|
||||
@@ -343,7 +335,7 @@ asmsub init_system() {
|
||||
; 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.
|
||||
; Uppercase charset is activated.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
@@ -446,10 +438,6 @@ _irq_handler_init
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||
ldx #32
|
||||
cld
|
||||
rts
|
||||
|
||||
@@ -774,111 +762,108 @@ cx16 {
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the C128 as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack.
|
||||
; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless
|
||||
; you're doing insane nesting of expressions...)
|
||||
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
|
||||
&uword r0 = $1b00
|
||||
&uword r1 = $1b02
|
||||
&uword r2 = $1b04
|
||||
&uword r3 = $1b06
|
||||
&uword r4 = $1b08
|
||||
&uword r5 = $1b0a
|
||||
&uword r6 = $1b0c
|
||||
&uword r7 = $1b0e
|
||||
&uword r8 = $1b10
|
||||
&uword r9 = $1b12
|
||||
&uword r10 = $1b14
|
||||
&uword r11 = $1b16
|
||||
&uword r12 = $1b18
|
||||
&uword r13 = $1b1a
|
||||
&uword r14 = $1b1c
|
||||
&uword r15 = $1b1e
|
||||
; $1300-$1bff is unused RAM on C128.
|
||||
&uword r0 = $1be0
|
||||
&uword r1 = $1be2
|
||||
&uword r2 = $1be4
|
||||
&uword r3 = $1be6
|
||||
&uword r4 = $1be8
|
||||
&uword r5 = $1bea
|
||||
&uword r6 = $1bec
|
||||
&uword r7 = $1bee
|
||||
&uword r8 = $1bf0
|
||||
&uword r9 = $1bf2
|
||||
&uword r10 = $1bf4
|
||||
&uword r11 = $1bf6
|
||||
&uword r12 = $1bf8
|
||||
&uword r13 = $1bfa
|
||||
&uword r14 = $1bfc
|
||||
&uword r15 = $1bfe
|
||||
|
||||
&word r0s = $1b00
|
||||
&word r1s = $1b02
|
||||
&word r2s = $1b04
|
||||
&word r3s = $1b06
|
||||
&word r4s = $1b08
|
||||
&word r5s = $1b0a
|
||||
&word r6s = $1b0c
|
||||
&word r7s = $1b0e
|
||||
&word r8s = $1b10
|
||||
&word r9s = $1b12
|
||||
&word r10s = $1b14
|
||||
&word r11s = $1b16
|
||||
&word r12s = $1b18
|
||||
&word r13s = $1b1a
|
||||
&word r14s = $1b1c
|
||||
&word r15s = $1b1e
|
||||
&word r0s = $1be0
|
||||
&word r1s = $1be2
|
||||
&word r2s = $1be4
|
||||
&word r3s = $1be6
|
||||
&word r4s = $1be8
|
||||
&word r5s = $1bea
|
||||
&word r6s = $1bec
|
||||
&word r7s = $1bee
|
||||
&word r8s = $1bf0
|
||||
&word r9s = $1bf2
|
||||
&word r10s = $1bf4
|
||||
&word r11s = $1bf6
|
||||
&word r12s = $1bf8
|
||||
&word r13s = $1bfa
|
||||
&word r14s = $1bfc
|
||||
&word r15s = $1bfe
|
||||
|
||||
&ubyte r0L = $1b00
|
||||
&ubyte r1L = $1b02
|
||||
&ubyte r2L = $1b04
|
||||
&ubyte r3L = $1b06
|
||||
&ubyte r4L = $1b08
|
||||
&ubyte r5L = $1b0a
|
||||
&ubyte r6L = $1b0c
|
||||
&ubyte r7L = $1b0e
|
||||
&ubyte r8L = $1b10
|
||||
&ubyte r9L = $1b12
|
||||
&ubyte r10L = $1b14
|
||||
&ubyte r11L = $1b16
|
||||
&ubyte r12L = $1b18
|
||||
&ubyte r13L = $1b1a
|
||||
&ubyte r14L = $1b1c
|
||||
&ubyte r15L = $1b1e
|
||||
&ubyte r0L = $1be0
|
||||
&ubyte r1L = $1be2
|
||||
&ubyte r2L = $1be4
|
||||
&ubyte r3L = $1be6
|
||||
&ubyte r4L = $1be8
|
||||
&ubyte r5L = $1bea
|
||||
&ubyte r6L = $1bec
|
||||
&ubyte r7L = $1bee
|
||||
&ubyte r8L = $1bf0
|
||||
&ubyte r9L = $1bf2
|
||||
&ubyte r10L = $1bf4
|
||||
&ubyte r11L = $1bf6
|
||||
&ubyte r12L = $1bf8
|
||||
&ubyte r13L = $1bfa
|
||||
&ubyte r14L = $1bfc
|
||||
&ubyte r15L = $1bfe
|
||||
|
||||
&ubyte r0H = $1b01
|
||||
&ubyte r1H = $1b03
|
||||
&ubyte r2H = $1b05
|
||||
&ubyte r3H = $1b07
|
||||
&ubyte r4H = $1b09
|
||||
&ubyte r5H = $1b0b
|
||||
&ubyte r6H = $1b0d
|
||||
&ubyte r7H = $1b0f
|
||||
&ubyte r8H = $1b11
|
||||
&ubyte r9H = $1b13
|
||||
&ubyte r10H = $1b15
|
||||
&ubyte r11H = $1b17
|
||||
&ubyte r12H = $1b19
|
||||
&ubyte r13H = $1b1b
|
||||
&ubyte r14H = $1b1d
|
||||
&ubyte r15H = $1b1f
|
||||
&ubyte r0H = $1be1
|
||||
&ubyte r1H = $1be3
|
||||
&ubyte r2H = $1be5
|
||||
&ubyte r3H = $1be7
|
||||
&ubyte r4H = $1be9
|
||||
&ubyte r5H = $1beb
|
||||
&ubyte r6H = $1bed
|
||||
&ubyte r7H = $1bef
|
||||
&ubyte r8H = $1bf1
|
||||
&ubyte r9H = $1bf3
|
||||
&ubyte r10H = $1bf5
|
||||
&ubyte r11H = $1bf7
|
||||
&ubyte r12H = $1bf9
|
||||
&ubyte r13H = $1bfb
|
||||
&ubyte r14H = $1bfd
|
||||
&ubyte r15H = $1bff
|
||||
|
||||
&byte r0sL = $1b00
|
||||
&byte r1sL = $1b02
|
||||
&byte r2sL = $1b04
|
||||
&byte r3sL = $1b06
|
||||
&byte r4sL = $1b08
|
||||
&byte r5sL = $1b0a
|
||||
&byte r6sL = $1b0c
|
||||
&byte r7sL = $1b0e
|
||||
&byte r8sL = $1b10
|
||||
&byte r9sL = $1b12
|
||||
&byte r10sL = $1b14
|
||||
&byte r11sL = $1b16
|
||||
&byte r12sL = $1b18
|
||||
&byte r13sL = $1b1a
|
||||
&byte r14sL = $1b1c
|
||||
&byte r15sL = $1b1e
|
||||
&byte r0sL = $1be0
|
||||
&byte r1sL = $1be2
|
||||
&byte r2sL = $1be4
|
||||
&byte r3sL = $1be6
|
||||
&byte r4sL = $1be8
|
||||
&byte r5sL = $1bea
|
||||
&byte r6sL = $1bec
|
||||
&byte r7sL = $1bee
|
||||
&byte r8sL = $1bf0
|
||||
&byte r9sL = $1bf2
|
||||
&byte r10sL = $1bf4
|
||||
&byte r11sL = $1bf6
|
||||
&byte r12sL = $1bf8
|
||||
&byte r13sL = $1bfa
|
||||
&byte r14sL = $1bfc
|
||||
&byte r15sL = $1bfe
|
||||
|
||||
&byte r0sH = $1b01
|
||||
&byte r1sH = $1b03
|
||||
&byte r2sH = $1b05
|
||||
&byte r3sH = $1b07
|
||||
&byte r4sH = $1b09
|
||||
&byte r5sH = $1b0b
|
||||
&byte r6sH = $1b0d
|
||||
&byte r7sH = $1b0f
|
||||
&byte r8sH = $1b11
|
||||
&byte r9sH = $1b13
|
||||
&byte r10sH = $1b15
|
||||
&byte r11sH = $1b17
|
||||
&byte r12sH = $1b19
|
||||
&byte r13sH = $1b1b
|
||||
&byte r14sH = $1b1d
|
||||
&byte r15sH = $1b1f
|
||||
&byte r0sH = $1be1
|
||||
&byte r1sH = $1be3
|
||||
&byte r2sH = $1be5
|
||||
&byte r3sH = $1be7
|
||||
&byte r4sH = $1be9
|
||||
&byte r5sH = $1beb
|
||||
&byte r6sH = $1bed
|
||||
&byte r7sH = $1bef
|
||||
&byte r8sH = $1bf1
|
||||
&byte r9sH = $1bf3
|
||||
&byte r10sH = $1bf5
|
||||
&byte r11sH = $1bf7
|
||||
&byte r12sH = $1bf9
|
||||
&byte r13sH = $1bfb
|
||||
&byte r14sH = $1bfd
|
||||
&byte r15sH = $1bff
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-128
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
@@ -97,13 +97,12 @@ sub uppercase() {
|
||||
c128.VM1 &= ~2
|
||||
}
|
||||
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, X, 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
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -133,17 +132,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dey
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- 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
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -169,17 +166,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- 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
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -205,17 +200,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- 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
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -241,7 +234,6 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -266,10 +258,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@@ -277,16 +268,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@@ -302,16 +290,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@@ -323,10 +308,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'$'
|
||||
@@ -335,16 +319,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'%'
|
||||
@@ -357,12 +338,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
@@ -374,7 +354,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -387,10 +367,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@@ -398,17 +377,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@@ -429,7 +405,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
@@ -490,7 +466,7 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word $0400 + range(0, 1000, 40)
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -557,7 +533,7 @@ _mod lda $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
lda row
|
||||
@@ -569,13 +545,13 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sta _colormod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc column
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
+ lda char
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
@@ -583,15 +559,10 @@ _colormod sta $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr cbm.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,11 @@ FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
|
||||
|
||||
floats_store_reg .byte 0 ; temp storage
|
||||
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
|
||||
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers A, X, Y
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
@@ -19,15 +17,12 @@ ub2float .proc
|
||||
jsr GIVAYF
|
||||
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
b2float .proc
|
||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers A, X, Y
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
@@ -37,7 +32,7 @@ b2float .proc
|
||||
|
||||
uw2float .proc
|
||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
@@ -48,7 +43,7 @@ uw2float .proc
|
||||
|
||||
w2float .proc
|
||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_W1
|
||||
@@ -60,7 +55,7 @@ w2float .proc
|
||||
|
||||
cast_from_uw .proc
|
||||
; -- uword in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
@@ -68,7 +63,7 @@ cast_from_uw .proc
|
||||
|
||||
cast_from_w .proc
|
||||
; -- word in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr GIVAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
@@ -76,7 +71,7 @@ cast_from_w .proc
|
||||
|
||||
cast_from_ub .proc
|
||||
; -- ubyte in Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr FREADUY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
@@ -84,169 +79,41 @@ cast_from_ub .proc
|
||||
|
||||
cast_from_b .proc
|
||||
; -- byte in A into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
cast_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast float at A/Y to uword into Y/A
|
||||
; clobbers X
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_uw_into_ya
|
||||
.pend
|
||||
|
||||
cast_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast float at A/Y to word into A/Y
|
||||
; clobbers X
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_w_into_ay
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast fac1 to uword into Y/A
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR ; into Y/A
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
; clobbers X
|
||||
jmp GETADR ; into Y/A
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast fac1 to word into A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; clobbers X
|
||||
jsr AYINT
|
||||
ldy floats.AYINT_facmo
|
||||
lda floats.AYINT_facmo+1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
stack_b2float .proc
|
||||
; -- b2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADSA
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_w2float .proc
|
||||
; -- w2float operating on the stack
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
lda P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_ub2float .proc
|
||||
; -- ub2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tay
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_uw2float .proc
|
||||
; -- uw2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_float2w .proc ; also used for float2b
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr AYINT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
lda floats.AYINT_facmo
|
||||
sta P8ESTACK_HI,x
|
||||
lda floats.AYINT_facmo+1
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_float2uw .proc ; also used for float2ub
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
sta P8ESTACK_HI,x
|
||||
tya
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
push_float .proc
|
||||
; ---- push mflpt5 in A/Y onto stack
|
||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float .proc
|
||||
; ---- pops mflpt5 from stack to memory A/Y
|
||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #4
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float_fac1 .proc
|
||||
; -- pops float from stack into FAC1
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
@@ -272,25 +139,23 @@ copy_float .proc
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
dec_var_f .proc
|
||||
; -- subtract 1 from float pointed to by A/Y
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr MOVFM
|
||||
@@ -299,23 +164,7 @@ dec_var_f .proc
|
||||
jsr FSUB
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
pop_2_floats_f2_in_fac1 .proc
|
||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jmp MOVFM
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
|
||||
@@ -323,71 +172,9 @@ fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
|
||||
|
||||
push_fac1 .proc
|
||||
; -- push the float in FAC1 onto the stack
|
||||
stx P8ZP_SCRATCH_REG
|
||||
_internal ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
div_f .proc
|
||||
; -- push f1/f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FDIV
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
add_f .proc
|
||||
; -- push f1+f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FADD
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
sub_f .proc
|
||||
; -- push f1-f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FSUB
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
mul_f .proc
|
||||
; -- push f1*f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FMULT
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
neg_f .proc
|
||||
; -- toggle the sign bit on the stack
|
||||
lda P8ESTACK_HI+3,x
|
||||
eor #$80
|
||||
sta P8ESTACK_HI+3,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_less_f .proc
|
||||
; -- is the float in FAC1 < the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
beq +
|
||||
lda #0
|
||||
@@ -397,10 +184,8 @@ var_fac1_less_f .proc
|
||||
.pend
|
||||
|
||||
var_fac1_lesseq_f .proc
|
||||
; -- is the float in FAC1 <= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 <= the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #255
|
||||
@@ -412,10 +197,8 @@ var_fac1_lesseq_f .proc
|
||||
.pend
|
||||
|
||||
var_fac1_greater_f .proc
|
||||
; -- is the float in FAC1 > the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 > the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #1
|
||||
beq +
|
||||
lda #0
|
||||
@@ -425,10 +208,8 @@ var_fac1_greater_f .proc
|
||||
.pend
|
||||
|
||||
var_fac1_greatereq_f .proc
|
||||
; -- is the float in FAC1 >= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- is the float in FAC1 >= the variable AY? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #1
|
||||
@@ -439,17 +220,23 @@ var_fac1_greatereq_f .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_equal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
and #1
|
||||
eor #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_notequal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
and #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_equal_f .proc
|
||||
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical?
|
||||
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical? Result in A
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
@@ -478,51 +265,13 @@ _false lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
equal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack identical?
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
lda P8ESTACK_LO-3,x
|
||||
cmp P8ESTACK_LO,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bne _equals_false
|
||||
_equals_true lda #1
|
||||
_equals_store inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_equals_false lda #0
|
||||
beq _equals_store
|
||||
.pend
|
||||
|
||||
notequal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack different?
|
||||
jsr equal_f
|
||||
eor #1 ; invert the result
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_less_f .proc
|
||||
; -- is float in AY < float in P8ZP_SCRATCH_W2 ?
|
||||
; -- is float in AY < float in P8ZP_SCRATCH_W2 ? Result in A. Clobbers X.
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
lda #1
|
||||
@@ -532,13 +281,11 @@ vars_less_f .proc
|
||||
.pend
|
||||
|
||||
vars_lesseq_f .proc
|
||||
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ?
|
||||
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ? Result in A. Clobbers X.
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
- lda #1
|
||||
@@ -550,7 +297,7 @@ vars_lesseq_f .proc
|
||||
.pend
|
||||
|
||||
less_f .proc
|
||||
; -- is f1 < f2?
|
||||
; -- is f1 < f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
@@ -559,7 +306,7 @@ less_f .proc
|
||||
|
||||
|
||||
lesseq_f .proc
|
||||
; -- is f1 <= f2?
|
||||
; -- is f1 <= f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
@@ -569,7 +316,7 @@ lesseq_f .proc
|
||||
.pend
|
||||
|
||||
greater_f .proc
|
||||
; -- is f1 > f2?
|
||||
; -- is f1 > f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
@@ -577,7 +324,7 @@ greater_f .proc
|
||||
.pend
|
||||
|
||||
greatereq_f .proc
|
||||
; -- is f1 >= f2?
|
||||
; -- is f1 >= f2? Result in A. Clobbers X.
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
@@ -586,32 +333,9 @@ greatereq_f .proc
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
compare_floats .proc
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVFM ; fac1 = flt1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_return_false lda #0
|
||||
_return_result sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_return_true lda #1
|
||||
bne _return_result
|
||||
.pend
|
||||
|
||||
set_array_float_from_fac1 .proc
|
||||
; -- set the float in FAC1 in the array (index in A, array in P8ZP_SCRATCH_W1)
|
||||
; clobbers X
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
@@ -622,11 +346,8 @@ set_array_float_from_fac1 .proc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
iny
|
||||
+ stx floats_store_reg
|
||||
tax
|
||||
jsr MOVMF
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
+ tax
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
|
||||
@@ -669,54 +390,63 @@ set_array_float .proc
|
||||
.pend
|
||||
|
||||
|
||||
equal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
beq _true
|
||||
bne _false
|
||||
_true lda #1
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_false lda #0
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
pushFAC1 .proc
|
||||
;-- push floating point in FAC onto the cpu stack
|
||||
; save return address
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
ldx #<floats.floats_temp_var
|
||||
ldy #>floats.floats_temp_var
|
||||
jsr floats.MOVMF
|
||||
lda floats.floats_temp_var
|
||||
pha
|
||||
lda floats.floats_temp_var+1
|
||||
pha
|
||||
lda floats.floats_temp_var+2
|
||||
pha
|
||||
lda floats.floats_temp_var+3
|
||||
pha
|
||||
lda floats.floats_temp_var+4
|
||||
pha
|
||||
; re-push return address
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
pha
|
||||
lda P8ZP_SCRATCH_W2
|
||||
pha
|
||||
rts
|
||||
.pend
|
||||
|
||||
notequal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
bne equal_zero._true
|
||||
beq equal_zero._false
|
||||
.pend
|
||||
popFAC .proc
|
||||
; -- pop floating point value from cpu stack into FAC1 or FAC2 (
|
||||
; carry flag clear=FAC1, carry set=FAC2
|
||||
; save return address
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2
|
||||
pla
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
pla
|
||||
sta floats.floats_temp_var+4
|
||||
pla
|
||||
sta floats.floats_temp_var+3
|
||||
pla
|
||||
sta floats.floats_temp_var+2
|
||||
pla
|
||||
sta floats.floats_temp_var+1
|
||||
pla
|
||||
sta floats.floats_temp_var
|
||||
lda #<floats.floats_temp_var
|
||||
ldy #>floats.floats_temp_var
|
||||
bcs +
|
||||
jsr floats.MOVFM
|
||||
jmp ++
|
||||
+ jsr floats.CONUPK
|
||||
+ ; re-push return address
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
pha
|
||||
lda P8ZP_SCRATCH_W2
|
||||
pha
|
||||
rts
|
||||
.pend
|
||||
|
||||
greater_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
beq equal_zero._false
|
||||
bpl equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
|
||||
less_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
bmi equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
|
||||
greaterequal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
bpl equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
|
||||
lessequal_zero .proc
|
||||
jsr floats.pop_float_fac1
|
||||
jsr floats.SIGN
|
||||
beq equal_zero._true
|
||||
bmi equal_zero._true
|
||||
jmp equal_zero._false
|
||||
.pend
|
||||
@@ -46,7 +46,7 @@ romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||
romsub $bc3c = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length
|
||||
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length. Also see parse_f()
|
||||
romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||
romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100)
|
||||
|
||||
@@ -159,12 +159,19 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
|
||||
sub rndf() -> float {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #1
|
||||
jsr FREADSA
|
||||
jsr RND ; rng into fac1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp RND ; rng into fac1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub parse_f(str value @AY) -> float @FAC1 {
|
||||
%asm {{
|
||||
sta $22
|
||||
sty $23
|
||||
jsr prog8_lib.strlen
|
||||
tya
|
||||
jmp FREADSTR
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
; --- floating point builtin functions
|
||||
|
||||
|
||||
func_sign_f_stack .proc
|
||||
jsr func_sign_f_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_f_into_A .proc
|
||||
jsr MOVFM
|
||||
jmp SIGN
|
||||
@@ -146,17 +139,11 @@ func_all_f_stack .proc
|
||||
.pend
|
||||
|
||||
func_abs_f_into_FAC1 .proc
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
jsr ABS
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp ABS
|
||||
.pend
|
||||
|
||||
func_sqrt_into_FAC1 .proc
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
jsr SQR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp SQR
|
||||
.pend
|
||||
@@ -1,35 +1,43 @@
|
||||
%import textio
|
||||
|
||||
; bitmap pixel graphics module for the C64
|
||||
; only black/white monochrome 320x200 for now
|
||||
; assumes bitmap screen memory is $2000-$3fff
|
||||
;
|
||||
; Sets character matrix and bitmap screen memory at a higher memory location $5c00-$7fff
|
||||
; so that the program itself can be larger without starting to overwrite the graphics memory.
|
||||
|
||||
graphics {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const uword BITMAP_ADDRESS = $2000
|
||||
const uword WIDTH = 320
|
||||
const ubyte HEIGHT = 200
|
||||
|
||||
const uword BITMAP_ADDRESS = $6000 ; MUST BE IN REGULAR RAM if you are not messing with ROM/RAM banking. (and $2000-aligned)
|
||||
; note: this constant is also used in a asm multiplication table below!
|
||||
const uword CHARS_ADDRESS = $5c00 ; must be in same vic memory bank as the bitmap.
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
c64.SCROLY = %00111011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||
clear_screen(1, 0)
|
||||
c64.SCROLY = %00111011 ; enable bitmap graphics mode
|
||||
c64.SCROLX = %00001000 ; 40 column mode, no scrolling, multicolor bitmap off
|
||||
c64.VMCSB = (lsb(CHARS_ADDRESS >> 6) & $F0) | (lsb((BITMAP_ADDRESS & $3fff) / $0800) << 1) ; set bitmap address
|
||||
c64.CIA2DDRA |= %11
|
||||
c64.CIA2PRA = lsb(BITMAP_ADDRESS >> 14) ^ 3 ; set VIC bank.
|
||||
}
|
||||
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
c64.SCROLY = %00011011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
|
||||
txt.fill_screen(' ', 1)
|
||||
; enables erase the text screen, text mode
|
||||
sys.memset(CHARS_ADDRESS, 40*25, sc:' ')
|
||||
c64.SCROLY = %00011011 ; disable bitmap graphics mode
|
||||
c64.SCROLX = %00001000 ; 40 column mode, no scrolling
|
||||
c64.VMCSB = %00010100 ; screen addresses back to defaults
|
||||
c64.CIA2DDRA |= %11
|
||||
c64.CIA2PRA = %11 ; back to VIC bank 0.
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||
sys.memset(BITMAP_ADDRESS, 320*200/8, 0)
|
||||
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
||||
sys.memset(CHARS_ADDRESS, 40*25, pixelcolor << 4 | bgcolor)
|
||||
sys.memset(cbm.Colors, 40*25, 255)
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||
@@ -127,46 +135,46 @@ graphics {
|
||||
}
|
||||
}
|
||||
|
||||
sub rect(uword x, ubyte y, uword width, ubyte height) {
|
||||
sub rect(uword xx, ubyte yy, uword width, ubyte height) {
|
||||
if width==0 or height==0
|
||||
return
|
||||
horizontal_line(x, y, width)
|
||||
horizontal_line(xx, yy, width)
|
||||
if height==1
|
||||
return
|
||||
horizontal_line(x, y+height-1, width)
|
||||
vertical_line(x, y+1, height-2)
|
||||
horizontal_line(xx, yy+height-1, width)
|
||||
vertical_line(xx, yy+1, height-2)
|
||||
if width==1
|
||||
return
|
||||
vertical_line(x+width-1, y+1, height-2)
|
||||
vertical_line(xx+width-1, yy+1, height-2)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, ubyte y, uword width, ubyte height) {
|
||||
sub fillrect(uword xx, ubyte yy, uword width, ubyte height) {
|
||||
if width==0
|
||||
return
|
||||
repeat height {
|
||||
horizontal_line(x, y, width)
|
||||
y++
|
||||
horizontal_line(xx, yy, width)
|
||||
yy++
|
||||
}
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, ubyte y, uword length) {
|
||||
sub horizontal_line(uword xx, ubyte yy, uword length) {
|
||||
if length<8 {
|
||||
internal_plotx=x
|
||||
internal_plotx=xx
|
||||
repeat lsb(length) {
|
||||
internal_plot(y)
|
||||
internal_plot(yy)
|
||||
internal_plotx++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ubyte separate_pixels = lsb(x) & 7
|
||||
uword addr = get_y_lookup(y) + (x&$fff8)
|
||||
ubyte separate_pixels = lsb(xx) & 7
|
||||
uword pixaddr = get_y_lookup(yy) + (xx&$fff8)
|
||||
|
||||
if separate_pixels {
|
||||
%asm {{
|
||||
lda addr
|
||||
lda pixaddr
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda addr+1
|
||||
lda pixaddr+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
ldy separate_pixels
|
||||
lda hline_filled_right,y
|
||||
@@ -175,7 +183,7 @@ graphics {
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
}}
|
||||
addr += 8
|
||||
pixaddr += 8
|
||||
length += separate_pixels
|
||||
length -= 8
|
||||
}
|
||||
@@ -185,16 +193,15 @@ graphics {
|
||||
lda length
|
||||
and #7
|
||||
sta separate_pixels
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda addr
|
||||
lda pixaddr
|
||||
sta _modified+1
|
||||
lda addr+1
|
||||
lda pixaddr+1
|
||||
sta _modified+2
|
||||
lda length
|
||||
ora length+1
|
||||
@@ -210,8 +217,7 @@ _modified stx $ffff ; modified
|
||||
inc _modified+2
|
||||
+ dey
|
||||
bne _modified
|
||||
_zero ldx P8ZP_SCRATCH_REG
|
||||
|
||||
_zero
|
||||
ldy separate_pixels
|
||||
beq hline_zero2
|
||||
lda _modified+1
|
||||
@@ -229,11 +235,11 @@ hline_zero2
|
||||
}
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, ubyte y, ubyte height) {
|
||||
internal_plotx = x
|
||||
sub vertical_line(uword xx, ubyte yy, ubyte height) {
|
||||
internal_plotx = xx
|
||||
repeat height {
|
||||
internal_plot(y)
|
||||
y++
|
||||
internal_plot(yy)
|
||||
yy++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,8 +308,8 @@ hline_zero2
|
||||
; here is the non-asm code for the plot routine below:
|
||||
; sub plot_nonasm(uword px, ubyte py) {
|
||||
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
; uword addr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||
; @(addr) |= ormask[lsb(px) & 7]
|
||||
; uword pixaddr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||
; @(pixaddr) |= ormask[lsb(px) & 7]
|
||||
; }
|
||||
|
||||
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
||||
@@ -354,7 +360,7 @@ _ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||
|
||||
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
||||
_plot_y_values := BITMAP_ADDRESS + 320*(range(200)>>3) + (range(200) & 7)
|
||||
|
||||
_y_lookup_lo .byte <_plot_y_values
|
||||
_y_lookup_hi .byte >_plot_y_values
|
||||
@@ -362,7 +368,7 @@ _y_lookup_hi .byte >_plot_y_values
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_y_lookup(ubyte y @Y) -> uword @AY {
|
||||
asmsub get_y_lookup(ubyte yy @Y) -> uword @AY {
|
||||
%asm {{
|
||||
lda internal_plot._y_lookup_lo,y
|
||||
pha
|
||||
|
||||
+139
-117
@@ -91,7 +91,7 @@ romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798
|
||||
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 $FFD2 = CHROUT(ubyte character @ 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
|
||||
@@ -104,34 +104,26 @@ romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of
|
||||
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 {
|
||||
asmsub STOP2() clobbers(X) -> 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
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
asmsub RDTIM16() clobbers(X) -> 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
|
||||
}}
|
||||
}
|
||||
@@ -144,6 +136,8 @@ c64 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||
; (depending on the VIC bank and screen ram address selection these can be shifted around though,
|
||||
; see the two routines after this for a dynamic way of determining the correct memory location)
|
||||
&ubyte SPRPTR0 = 2040
|
||||
&ubyte SPRPTR1 = 2041
|
||||
&ubyte SPRPTR2 = 2042
|
||||
@@ -292,6 +286,39 @@ c64 {
|
||||
|
||||
; ---- end of SID registers ----
|
||||
|
||||
|
||||
sub get_vic_memory_base() -> uword {
|
||||
; one of the 4 possible banks. $0000/$4000/$8000/$c000.
|
||||
c64.CIA2DDRA |= %11
|
||||
return ((c64.CIA2PRA & 3) ^ 3) as uword << 14
|
||||
}
|
||||
|
||||
sub get_char_matrix_ptr() -> uword {
|
||||
; Usually the character screen matrix is at 1024-2039 (see above)
|
||||
; However the vic memory configuration can be altered and this moves these registers with it.
|
||||
; So this routine determines it dynamically from the VIC memory setup.
|
||||
uword chars_matrix_offset = (c64.VMCSB & $f0) as uword << 6
|
||||
return get_vic_memory_base() + chars_matrix_offset
|
||||
}
|
||||
|
||||
sub get_bitmap_ptr() -> uword {
|
||||
return get_vic_memory_base() + ((c64.VMCSB & %00001000) as uword << 10)
|
||||
}
|
||||
|
||||
sub get_sprite_addr_ptrs() -> uword {
|
||||
; Usually the sprite address pointers are at addresses 2040-2047 (see above)
|
||||
; However the vic memory configuration can be altered and this moves these registers with it.
|
||||
; So this routine determines it dynamically from the VIC memory setup.
|
||||
return get_char_matrix_ptr() + 1016
|
||||
}
|
||||
|
||||
sub set_sprite_ptr(ubyte sprite_num, uword sprite_data_address) {
|
||||
; Sets the sprite data pointer to the given address.
|
||||
; Because it takes some time to calculate things based on the vic memory setup,
|
||||
; its only suitable if you're not continuously changing the data address.
|
||||
; Otherwise store the correct sprite data pointer location somewhere yourself and reuse it.
|
||||
@(get_sprite_addr_ptrs() + sprite_num) = lsb(sprite_data_address / 64)
|
||||
}
|
||||
}
|
||||
|
||||
sys {
|
||||
@@ -308,7 +335,7 @@ asmsub init_system() {
|
||||
; 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.
|
||||
; Uppercase charset is activated.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
@@ -413,10 +440,6 @@ _irq_handler_init
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||
ldx #32
|
||||
cld
|
||||
rts
|
||||
|
||||
@@ -742,111 +765,110 @@ cx16 {
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the C64 as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
; they are allocated at the bottom of the eval-stack (should be ample space unless
|
||||
; you're doing insane nesting of expressions...)
|
||||
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
|
||||
&uword r0 = $cf00
|
||||
&uword r1 = $cf02
|
||||
&uword r2 = $cf04
|
||||
&uword r3 = $cf06
|
||||
&uword r4 = $cf08
|
||||
&uword r5 = $cf0a
|
||||
&uword r6 = $cf0c
|
||||
&uword r7 = $cf0e
|
||||
&uword r8 = $cf10
|
||||
&uword r9 = $cf12
|
||||
&uword r10 = $cf14
|
||||
&uword r11 = $cf16
|
||||
&uword r12 = $cf18
|
||||
&uword r13 = $cf1a
|
||||
&uword r14 = $cf1c
|
||||
&uword r15 = $cf1e
|
||||
; (because there's no room for them in the zeropage in the default configuration)
|
||||
; Note that when using ZP options that free up more of the zeropage (such as %zeropage kernalsafe)
|
||||
; there might be enough space to put them there after all, and the compiler will change these addresses!
|
||||
&uword r0 = $cfe0
|
||||
&uword r1 = $cfe2
|
||||
&uword r2 = $cfe4
|
||||
&uword r3 = $cfe6
|
||||
&uword r4 = $cfe8
|
||||
&uword r5 = $cfea
|
||||
&uword r6 = $cfec
|
||||
&uword r7 = $cfee
|
||||
&uword r8 = $cff0
|
||||
&uword r9 = $cff2
|
||||
&uword r10 = $cff4
|
||||
&uword r11 = $cff6
|
||||
&uword r12 = $cff8
|
||||
&uword r13 = $cffa
|
||||
&uword r14 = $cffc
|
||||
&uword r15 = $cffe
|
||||
|
||||
&word r0s = $cf00
|
||||
&word r1s = $cf02
|
||||
&word r2s = $cf04
|
||||
&word r3s = $cf06
|
||||
&word r4s = $cf08
|
||||
&word r5s = $cf0a
|
||||
&word r6s = $cf0c
|
||||
&word r7s = $cf0e
|
||||
&word r8s = $cf10
|
||||
&word r9s = $cf12
|
||||
&word r10s = $cf14
|
||||
&word r11s = $cf16
|
||||
&word r12s = $cf18
|
||||
&word r13s = $cf1a
|
||||
&word r14s = $cf1c
|
||||
&word r15s = $cf1e
|
||||
&word r0s = $cfe0
|
||||
&word r1s = $cfe2
|
||||
&word r2s = $cfe4
|
||||
&word r3s = $cfe6
|
||||
&word r4s = $cfe8
|
||||
&word r5s = $cfea
|
||||
&word r6s = $cfec
|
||||
&word r7s = $cfee
|
||||
&word r8s = $cff0
|
||||
&word r9s = $cff2
|
||||
&word r10s = $cff4
|
||||
&word r11s = $cff6
|
||||
&word r12s = $cff8
|
||||
&word r13s = $cffa
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
&ubyte r0L = $cf00
|
||||
&ubyte r1L = $cf02
|
||||
&ubyte r2L = $cf04
|
||||
&ubyte r3L = $cf06
|
||||
&ubyte r4L = $cf08
|
||||
&ubyte r5L = $cf0a
|
||||
&ubyte r6L = $cf0c
|
||||
&ubyte r7L = $cf0e
|
||||
&ubyte r8L = $cf10
|
||||
&ubyte r9L = $cf12
|
||||
&ubyte r10L = $cf14
|
||||
&ubyte r11L = $cf16
|
||||
&ubyte r12L = $cf18
|
||||
&ubyte r13L = $cf1a
|
||||
&ubyte r14L = $cf1c
|
||||
&ubyte r15L = $cf1e
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
&ubyte r2L = $cfe4
|
||||
&ubyte r3L = $cfe6
|
||||
&ubyte r4L = $cfe8
|
||||
&ubyte r5L = $cfea
|
||||
&ubyte r6L = $cfec
|
||||
&ubyte r7L = $cfee
|
||||
&ubyte r8L = $cff0
|
||||
&ubyte r9L = $cff2
|
||||
&ubyte r10L = $cff4
|
||||
&ubyte r11L = $cff6
|
||||
&ubyte r12L = $cff8
|
||||
&ubyte r13L = $cffa
|
||||
&ubyte r14L = $cffc
|
||||
&ubyte r15L = $cffe
|
||||
|
||||
&ubyte r0H = $cf01
|
||||
&ubyte r1H = $cf03
|
||||
&ubyte r2H = $cf05
|
||||
&ubyte r3H = $cf07
|
||||
&ubyte r4H = $cf09
|
||||
&ubyte r5H = $cf0b
|
||||
&ubyte r6H = $cf0d
|
||||
&ubyte r7H = $cf0f
|
||||
&ubyte r8H = $cf11
|
||||
&ubyte r9H = $cf13
|
||||
&ubyte r10H = $cf15
|
||||
&ubyte r11H = $cf17
|
||||
&ubyte r12H = $cf19
|
||||
&ubyte r13H = $cf1b
|
||||
&ubyte r14H = $cf1d
|
||||
&ubyte r15H = $cf1f
|
||||
&ubyte r0H = $cfe1
|
||||
&ubyte r1H = $cfe3
|
||||
&ubyte r2H = $cfe5
|
||||
&ubyte r3H = $cfe7
|
||||
&ubyte r4H = $cfe9
|
||||
&ubyte r5H = $cfeb
|
||||
&ubyte r6H = $cfed
|
||||
&ubyte r7H = $cfef
|
||||
&ubyte r8H = $cff1
|
||||
&ubyte r9H = $cff3
|
||||
&ubyte r10H = $cff5
|
||||
&ubyte r11H = $cff7
|
||||
&ubyte r12H = $cff9
|
||||
&ubyte r13H = $cffb
|
||||
&ubyte r14H = $cffd
|
||||
&ubyte r15H = $cfff
|
||||
|
||||
&byte r0sL = $cf00
|
||||
&byte r1sL = $cf02
|
||||
&byte r2sL = $cf04
|
||||
&byte r3sL = $cf06
|
||||
&byte r4sL = $cf08
|
||||
&byte r5sL = $cf0a
|
||||
&byte r6sL = $cf0c
|
||||
&byte r7sL = $cf0e
|
||||
&byte r8sL = $cf10
|
||||
&byte r9sL = $cf12
|
||||
&byte r10sL = $cf14
|
||||
&byte r11sL = $cf16
|
||||
&byte r12sL = $cf18
|
||||
&byte r13sL = $cf1a
|
||||
&byte r14sL = $cf1c
|
||||
&byte r15sL = $cf1e
|
||||
&byte r0sL = $cfe0
|
||||
&byte r1sL = $cfe2
|
||||
&byte r2sL = $cfe4
|
||||
&byte r3sL = $cfe6
|
||||
&byte r4sL = $cfe8
|
||||
&byte r5sL = $cfea
|
||||
&byte r6sL = $cfec
|
||||
&byte r7sL = $cfee
|
||||
&byte r8sL = $cff0
|
||||
&byte r9sL = $cff2
|
||||
&byte r10sL = $cff4
|
||||
&byte r11sL = $cff6
|
||||
&byte r12sL = $cff8
|
||||
&byte r13sL = $cffa
|
||||
&byte r14sL = $cffc
|
||||
&byte r15sL = $cffe
|
||||
|
||||
&byte r0sH = $cf01
|
||||
&byte r1sH = $cf03
|
||||
&byte r2sH = $cf05
|
||||
&byte r3sH = $cf07
|
||||
&byte r4sH = $cf09
|
||||
&byte r5sH = $cf0b
|
||||
&byte r6sH = $cf0d
|
||||
&byte r7sH = $cf0f
|
||||
&byte r8sH = $cf11
|
||||
&byte r9sH = $cf13
|
||||
&byte r10sH = $cf15
|
||||
&byte r11sH = $cf17
|
||||
&byte r12sH = $cf19
|
||||
&byte r13sH = $cf1b
|
||||
&byte r14sH = $cf1d
|
||||
&byte r15sH = $cf1f
|
||||
&byte r0sH = $cfe1
|
||||
&byte r1sH = $cfe3
|
||||
&byte r2sH = $cfe5
|
||||
&byte r3sH = $cfe7
|
||||
&byte r4sH = $cfe9
|
||||
&byte r5sH = $cfeb
|
||||
&byte r6sH = $cfed
|
||||
&byte r7sH = $cfef
|
||||
&byte r8sH = $cff1
|
||||
&byte r9sH = $cff3
|
||||
&byte r10sH = $cff5
|
||||
&byte r11sH = $cff7
|
||||
&byte r12sH = $cff9
|
||||
&byte r13sH = $cffb
|
||||
&byte r14sH = $cffd
|
||||
&byte r15sH = $cfff
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
|
||||
@@ -39,7 +39,7 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
; (assumes screen and color matrix are at their default addresses)
|
||||
|
||||
@@ -54,7 +54,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
@@ -96,13 +96,12 @@ sub uppercase() {
|
||||
c64.VMCSB &= ~2
|
||||
}
|
||||
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, X, 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
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -132,17 +131,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dey
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- 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
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -168,17 +165,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- 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
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -204,17 +199,15 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A,X) {
|
||||
; ---- 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
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the screen and the color memory
|
||||
@@ -240,12 +233,11 @@ _scroll_screen ; scroll only the screen memory
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
romsub $FFD2 = chrout(ubyte character @ 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
|
||||
@@ -265,10 +257,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@@ -276,16 +267,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@@ -301,16 +289,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@@ -322,10 +307,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'$'
|
||||
@@ -334,16 +318,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'%'
|
||||
@@ -356,12 +337,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,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 +353,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -386,10 +366,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@@ -397,17 +376,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@@ -428,7 +404,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
@@ -489,7 +465,7 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word $0400 + range(0, 1000, 40)
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -556,7 +532,7 @@ _mod lda $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
lda row
|
||||
@@ -568,13 +544,13 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sta _colormod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc column
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
+ lda char
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
@@ -582,15 +558,10 @@ _colormod sta $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr cbm.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,25 +8,22 @@ conv {
|
||||
|
||||
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
|
||||
|
||||
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
sty string_out
|
||||
sta string_out+1
|
||||
stx string_out+2
|
||||
lda #0
|
||||
sta string_out+3
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_B1
|
||||
jsr conv.ubyte2decimal
|
||||
@@ -52,15 +49,13 @@ _output_byte_digits
|
||||
iny
|
||||
lda #0
|
||||
sta string_out,y
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub str_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the byte in A in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_B1
|
||||
cmp #0
|
||||
@@ -75,7 +70,7 @@ asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ubhex (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in hex string form
|
||||
%asm {{
|
||||
jsr conv.ubyte2hex
|
||||
@@ -87,7 +82,7 @@ asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_ubbin (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub str_ubbin (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- convert the ubyte in A in binary string form
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@@ -106,7 +101,7 @@ _digit sta string_out,y
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uwbin (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub str_uwbin (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the uword in A/Y in binary string form
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_REG
|
||||
@@ -145,10 +140,9 @@ asmsub str_uwhex (uword value @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub str_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@@ -156,15 +150,13 @@ asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
beq +
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub str_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx #0
|
||||
_output_digits
|
||||
@@ -182,7 +174,6 @@ _gotdigit sta string_out,x
|
||||
bne _gotdigit
|
||||
_end lda #0
|
||||
sta string_out,x
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
_allzero lda #'0'
|
||||
@@ -192,12 +183,11 @@ _allzero lda #'0'
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str_w (word value @ AY) clobbers(A,Y) {
|
||||
asmsub str_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl str_uw
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
lda #'-'
|
||||
sta string_out
|
||||
@@ -700,10 +690,9 @@ asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
|
||||
asmsub ubyte2hex (ubyte value @A) clobbers(X) -> ubyte @A, ubyte @Y {
|
||||
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
and #$0f
|
||||
tax
|
||||
@@ -715,7 +704,6 @@ asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
|
||||
lsr a
|
||||
tax
|
||||
lda _hex_digits,x
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
||||
|
||||
@@ -8,22 +8,33 @@
|
||||
diskio {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
|
||||
ubyte drivenumber = 8
|
||||
|
||||
sub set_drive(ubyte number) {
|
||||
drivenumber = number
|
||||
}
|
||||
|
||||
sub reset_read_channel() {
|
||||
void cbm.CHKIN(READ_IO_CHANNEL)
|
||||
}
|
||||
|
||||
sub reset_write_channel() {
|
||||
cbm.CHKOUT(WRITE_IO_CHANNEL)
|
||||
}
|
||||
|
||||
sub directory() -> bool {
|
||||
; -- Prints the directory contents to the screen. Returns success.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
cbm.SETLFS(READ_IO_CHANNEL, 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
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -43,12 +54,12 @@ diskio {
|
||||
ubyte high = cbm.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.spc()
|
||||
ubyte @zp char
|
||||
ubyte @zp character
|
||||
repeat {
|
||||
char = cbm.CHRIN()
|
||||
if char==0
|
||||
character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
txt.chrout(char)
|
||||
txt.chrout(character)
|
||||
}
|
||||
txt.nl()
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
@@ -61,7 +72,7 @@ diskio {
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
|
||||
if status and status & $40 == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
@@ -76,12 +87,12 @@ io_error:
|
||||
sub diskname() -> uword {
|
||||
; returns disk label name or 0 if error
|
||||
cbm.SETNAM(3, "$")
|
||||
cbm.SETLFS(12, diskio.drivenumber, 0)
|
||||
cbm.SETLFS(READ_IO_CHANNEL, diskio.drivenumber, 0)
|
||||
ubyte status = 1
|
||||
void cbm.OPEN() ; open 12,8,0,"$=c"
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -106,7 +117,7 @@ io_error:
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if status and status & $40 == 0
|
||||
return 0
|
||||
return diskio.list_filename
|
||||
@@ -124,7 +135,7 @@ io_error:
|
||||
|
||||
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.
|
||||
; Files in the buffer are separated 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.
|
||||
@@ -150,7 +161,7 @@ io_error:
|
||||
return files_found
|
||||
}
|
||||
|
||||
; ----- iterative file lister functions (uses io channel 12) -----
|
||||
; ----- iterative file lister functions (uses the read io channel) -----
|
||||
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
@@ -161,11 +172,11 @@ io_error:
|
||||
iteration_in_progress = true
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, 0)
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -190,7 +201,7 @@ io_error:
|
||||
return false
|
||||
|
||||
repeat {
|
||||
void cbm.CHKIN(12) ; use #12 as input channel again
|
||||
reset_read_channel() ; use the input io channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
@@ -209,12 +220,12 @@ io_error:
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = cbm.CHRIN()
|
||||
if char==0
|
||||
ubyte character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
if char=='\"'
|
||||
if character=='\"'
|
||||
break
|
||||
@(nameptr) = char
|
||||
@(nameptr) = character
|
||||
nameptr++
|
||||
}
|
||||
|
||||
@@ -251,13 +262,13 @@ close_end:
|
||||
; -- end an iterative file listing session (close channels).
|
||||
if iteration_in_progress {
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file loader functions (uses io channel 12) -----
|
||||
; ----- iterative file loader functions (uses the read io channel) -----
|
||||
|
||||
sub f_open(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative reading with f_read
|
||||
@@ -265,18 +276,18 @@ close_end:
|
||||
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
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL) ; note: has to be Channel,x,Channel 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
|
||||
reset_read_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
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
void cbm.CHKIN(12)
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -290,26 +301,27 @@ 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: cannot be used to load into VRAM. Use vload() or call cx16.macptr() yourself with the vera data register as address.
|
||||
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
|
||||
if not iteration_in_progress or not num_bytes
|
||||
return 0
|
||||
|
||||
reset_read_channel()
|
||||
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
|
||||
; commander X16 supports fast block-read via MACPTR() kernal call
|
||||
uword readsize
|
||||
while num_bytes {
|
||||
size = 255
|
||||
if num_bytes<size
|
||||
size = num_bytes
|
||||
size = cx16.macptr(lsb(size), bufferpointer, false)
|
||||
readsize = 255
|
||||
if num_bytes<readsize
|
||||
readsize = num_bytes
|
||||
readsize = cx16.MACPTR(lsb(readsize), bufferpointer, false)
|
||||
if_cs
|
||||
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
||||
list_blocks += size
|
||||
bufferpointer += size
|
||||
goto byte_read_loop ; MACPTR block read not supported, do fallback loop
|
||||
list_blocks += readsize
|
||||
bufferpointer += readsize
|
||||
if msb(bufferpointer) == $c0
|
||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||
num_bytes -= size
|
||||
num_bytes -= readsize
|
||||
if cbm.READST() & $40 {
|
||||
f_close() ; end of file, close it
|
||||
break
|
||||
@@ -317,7 +329,7 @@ close_end:
|
||||
}
|
||||
return list_blocks ; number of bytes read
|
||||
|
||||
byte_read_loop: ; fallback if macptr() isn't supported on the device
|
||||
byte_read_loop: ; fallback if MACPTR isn't supported on the device
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
@@ -348,10 +360,11 @@ m_in_buffer sta $ffff
|
||||
; 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.
|
||||
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.macptr() yourself with the vera data register as address.
|
||||
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
reset_read_channel()
|
||||
uword total_read = 0
|
||||
while not cbm.READST() {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
@@ -370,8 +383,7 @@ m_in_buffer sta $ffff
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx #12
|
||||
jsr cbm.CHKIN ; use channel 12 again for input
|
||||
jsr reset_read_channel
|
||||
ldy #0
|
||||
_loop jsr cbm.CHRIN
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
@@ -392,25 +404,23 @@ _end rts
|
||||
; -- end an iterative file loading session (close channels).
|
||||
if iteration_in_progress {
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file writing functions (uses io channel 13) -----
|
||||
; ----- iterative file writing functions (uses write io channel) -----
|
||||
|
||||
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)
|
||||
cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, 1)
|
||||
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||
if_cc {
|
||||
cbm.CHKOUT(13) ; use #13 as output channel
|
||||
if_cc
|
||||
return not cbm.READST()
|
||||
}
|
||||
f_close_w()
|
||||
return false
|
||||
}
|
||||
@@ -418,7 +428,7 @@ _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 {
|
||||
cbm.CHKOUT(13) ; use #13 as output channel again
|
||||
reset_write_channel()
|
||||
repeat num_bytes {
|
||||
cbm.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
@@ -431,7 +441,7 @@ _end rts
|
||||
sub f_close_w() {
|
||||
; -- end an iterative file writing session (close channels).
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(13)
|
||||
cbm.CLOSE(WRITE_IO_CHANNEL)
|
||||
}
|
||||
|
||||
|
||||
@@ -470,27 +480,26 @@ io_error:
|
||||
|
||||
|
||||
; 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)
|
||||
sub save(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, 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 save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool {
|
||||
return internal_save_routine(filenameptr, startaddress, savesize, true)
|
||||
}
|
||||
|
||||
sub internal_save_routine(uword filenameptr, uword address, uword size, bool headerless) -> bool {
|
||||
sub internal_save_routine(uword filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = address + size
|
||||
uword @shared end_address = startaddress + savesize
|
||||
cx16.r0L = 0
|
||||
|
||||
%asm {{
|
||||
lda address
|
||||
lda startaddress
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
lda startaddress+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
lda headerless
|
||||
@@ -501,7 +510,6 @@ io_error:
|
||||
+ lda #<P8ZP_SCRATCH_W1
|
||||
jsr cbm.SAVE
|
||||
+ php
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
plp
|
||||
}}
|
||||
|
||||
@@ -534,8 +542,8 @@ io_error:
|
||||
; 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 load_raw(uword filenameptr, uword startaddress) -> uword {
|
||||
return internal_load_routine(filenameptr, startaddress, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -549,7 +557,6 @@ io_error:
|
||||
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
|
||||
@@ -557,7 +564,7 @@ io_error:
|
||||
bcs +
|
||||
stx cx16.r1
|
||||
sty cx16.r1+1
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
+
|
||||
}}
|
||||
|
||||
cbm.CLRCHN()
|
||||
@@ -609,14 +616,13 @@ io_error:
|
||||
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
||||
}
|
||||
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> 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 +
|
||||
@@ -644,13 +650,12 @@ internal_vload:
|
||||
+ 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 {
|
||||
asmsub vload_raw(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> 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.
|
||||
@@ -698,11 +703,11 @@ internal_vload:
|
||||
cx16.r12 = reversebuffer + MAX_PATH_LEN-1
|
||||
@(cx16.r12)=0
|
||||
cbm.SETNAM(3, "$=c")
|
||||
cbm.SETLFS(12, diskio.drivenumber, 0)
|
||||
cbm.SETLFS(READ_IO_CHANNEL, diskio.drivenumber, 0)
|
||||
void cbm.OPEN() ; open 12,8,0,"$=c"
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -739,7 +744,7 @@ internal_vload:
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if status and status & $40 == 0
|
||||
return 0
|
||||
if @(cx16.r12)==0 {
|
||||
@@ -772,7 +777,7 @@ io_error:
|
||||
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[1] = READ_IO_CHANNEL ; f_open uses the read io channel
|
||||
command[2] = lsb(pos_loword)
|
||||
command[3] = msb(pos_loword)
|
||||
command[4] = lsb(pos_hiword)
|
||||
@@ -782,13 +787,14 @@ io_error:
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
cbm.CLOSE(15)
|
||||
reset_read_channel() ; back to the read io channel
|
||||
}
|
||||
|
||||
|
||||
; 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[1] = WRITE_IO_CHANNEL ; f_open_w uses the write io channel
|
||||
; diskio.f_seek.command[2] = lsb(pos_loword)
|
||||
; diskio.f_seek.command[3] = msb(pos_loword)
|
||||
; diskio.f_seek.command[4] = lsb(pos_hiword)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
; Emulator debug interface.
|
||||
; Docs: https://github.com/X16Community/x16-emulator#debug-io-registers
|
||||
|
||||
emudbg {
|
||||
|
||||
const uword EMU_BASE = $9fb0
|
||||
|
||||
&ubyte EMU_DBG_HOTKEY_ENABLED = EMU_BASE + 0
|
||||
&ubyte EMU_LOG_VIDEO = EMU_BASE + 1
|
||||
&ubyte EMU_LOG_KEYBOARD = EMU_BASE + 2
|
||||
&ubyte EMU_ECHO_MODE = EMU_BASE + 3
|
||||
&ubyte EMU_SAVE_ON_EXIT = EMU_BASE + 4
|
||||
&ubyte EMU_RECORD_GIF = EMU_BASE + 5
|
||||
&ubyte EMU_RECORD_WAV = EMU_BASE + 6
|
||||
&ubyte EMU_CMDKEYS_DISABLED = EMU_BASE + 7
|
||||
&ubyte EMU_CPUCLK_L = EMU_BASE + 8 ; write: reset cpu clock to 0
|
||||
&ubyte EMU_CPUCLK_M = EMU_BASE + 9 ; write: outputs "User debug 1: $xx"
|
||||
&ubyte EMU_CPUCLK_H = EMU_BASE + 10 ; write: outputs "User debug 2: $xx"
|
||||
&ubyte EMU_CPUCLK_U = EMU_BASE + 11 ; write: outputs as character to console
|
||||
; 12 is unused for now
|
||||
&ubyte EMU_KEYMAP = EMU_BASE + 13
|
||||
&ubyte EMU_EMU_DETECT1 = EMU_BASE + 14
|
||||
&ubyte EMU_EMU_DETECT2 = EMU_BASE + 15
|
||||
|
||||
sub is_emulator() -> bool {
|
||||
; Test for emulator presence.
|
||||
; It is recommended to only use the other debug routines if this returns true.
|
||||
return EMU_EMU_DETECT1=='1' and EMU_EMU_DETECT2=='6'
|
||||
}
|
||||
|
||||
asmsub console_write(str isoString @R0) clobbers(Y) {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda (cx16.r0),y
|
||||
beq +
|
||||
sta p8_EMU_CPUCLK_U
|
||||
iny
|
||||
bne -
|
||||
+
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub console_chrout(ubyte char @A) {
|
||||
%asm {{
|
||||
sta p8_EMU_CPUCLK_U
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub console_value1(ubyte value @A) {
|
||||
%asm {{
|
||||
sta p8_EMU_CPUCLK_M
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub console_value2(ubyte value @A) {
|
||||
%asm {{
|
||||
sta p8_EMU_CPUCLK_H
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101
|
||||
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
; romsub $fe09 = VAL_1() clobbers(A,X,Y) ; convert ASCII string to floating point [not yet implemented!!!]
|
||||
; romsub $fe09 = VAL_1() clobbers(A,X,Y) ; convert ASCII string to floating point [not yet implemented!!!] see parse_f() instead
|
||||
|
||||
; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
@@ -44,7 +44,7 @@ romsub $fe21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||
romsub $fe24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
|
||||
romsub $fe27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
|
||||
romsub $fe2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||
romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||
romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use ROUND or FADDH first to round instead of trunc
|
||||
romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||
romsub $fe33 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
||||
romsub $fe36 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** float in A/Y
|
||||
@@ -99,14 +99,11 @@ asmsub FREADSA (byte value @A) clobbers(A,X,Y) {
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
phx
|
||||
sty $c4 ; facmo ($64 on c128)
|
||||
sta $c5 ; facmo+1 ($65 on c128)
|
||||
ldx #$90
|
||||
sec
|
||||
jsr FLOATC
|
||||
plx
|
||||
rts
|
||||
jmp FLOATC
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -139,16 +136,26 @@ asmsub FREADUY (ubyte value @Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub parse_f(str value @AY) -> float @FAC1 {
|
||||
; -- parse a string value of a number to float in FAC1
|
||||
; warning: uses an internal BASIC routine that may be rom version dependent
|
||||
; ($ddf2 is inside the routine for VAL at $ddef)
|
||||
%asm {{
|
||||
sta $a9
|
||||
sty $aa
|
||||
jsr prog8_lib.strlen
|
||||
tya
|
||||
jmp $ddf2
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT
|
||||
|
||||
sub rndf() -> float {
|
||||
%asm {{
|
||||
phx
|
||||
lda #1
|
||||
jsr RND_0
|
||||
plx
|
||||
rts
|
||||
jmp RND_0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
+126
-511
@@ -4,6 +4,7 @@
|
||||
;
|
||||
; No text layer is currently shown, text can be drawn as part of the bitmap itself.
|
||||
; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead.
|
||||
; Note: for identical routines for a monochrome 1 bpp screen, use the "monogfx" module instead.
|
||||
; Note: for color palette manipulation, use the "palette" module or write Vera registers yourself.
|
||||
; Note: this library implements code for various resolutions and color depths. This takes up memory.
|
||||
; If you're memory constrained you should probably not use this built-in library,
|
||||
@@ -12,15 +13,11 @@
|
||||
;
|
||||
; SCREEN MODE LIST:
|
||||
; mode 0 = reset back to default text mode
|
||||
; mode 1 = bitmap 320 x 240 monochrome
|
||||
; mode 2 = bitmap 320 x 240 x 4c (not yet implemented: just use 256c, there's enough vram for that)
|
||||
; mode 1 = bitmap 320 x 240 x 256c (8 bpp)
|
||||
; mode 2 = bitmap 640 x 480 x 4c (2 bpp. there's not enough vram for more colors in hires mode.)
|
||||
; mode 3 = bitmap 320 x 240 x 16c (not yet implemented: just use 256c, there's enough vram for that)
|
||||
; mode 4 = bitmap 320 x 240 x 256c (like SCREEN $80 but using this api instead of kernal)
|
||||
; mode 5 = bitmap 640 x 480 monochrome
|
||||
; mode 6 = bitmap 640 x 480 x 4c
|
||||
; higher color dephts in highres are not supported due to lack of VRAM
|
||||
|
||||
; TODO remove the phx/plx pairs in non-stack compiler version
|
||||
; mode 4 = bitmap 320 x 240 x 4c (not yet implemented: just use 256c, there's enough vram for that)
|
||||
; higher color depths in highres are not supported due to lack of VRAM
|
||||
|
||||
gfx2 {
|
||||
|
||||
@@ -31,24 +28,11 @@ gfx2 {
|
||||
uword width = 0
|
||||
uword height = 0
|
||||
ubyte bpp = 0
|
||||
bool monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
|
||||
|
||||
sub screen_mode(ubyte mode) {
|
||||
cx16.VERA_CTRL=0
|
||||
when mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
cx16.VERA_DC_VSCALE = 64
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0
|
||||
width = 320
|
||||
height = 240
|
||||
bpp = 1
|
||||
}
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
@@ -60,19 +44,7 @@ gfx2 {
|
||||
height = 240
|
||||
bpp = 8
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 128
|
||||
cx16.VERA_DC_VSCALE = 128
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = %00000001
|
||||
width = 640
|
||||
height = 480
|
||||
bpp = 1
|
||||
}
|
||||
6 -> {
|
||||
2 -> {
|
||||
; highres 4c
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 128
|
||||
@@ -98,132 +70,61 @@ gfx2 {
|
||||
|
||||
active_mode = mode
|
||||
if bpp
|
||||
clear_screen()
|
||||
clear_screen(0)
|
||||
}
|
||||
|
||||
sub clear_screen() {
|
||||
monochrome_stipple(false)
|
||||
sub clear_screen(ubyte color) {
|
||||
position(0, 0)
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
repeat 240/2/8
|
||||
cs_innerloop640()
|
||||
}
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
repeat 240/2
|
||||
cs_innerloop640()
|
||||
cs_innerloop640(color)
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
repeat 480/8
|
||||
cs_innerloop640()
|
||||
}
|
||||
6 -> {
|
||||
2 -> {
|
||||
; highres 4c
|
||||
ubyte[] colors = [%00000000, %01010101, %10101010, %11111111]
|
||||
color = colors[color&3]
|
||||
repeat 480/4
|
||||
cs_innerloop640()
|
||||
cs_innerloop640(color)
|
||||
}
|
||||
; modes 7 and 8 not supported due to lack of VRAM
|
||||
}
|
||||
position(0, 0)
|
||||
}
|
||||
|
||||
sub monochrome_stipple(bool enable) {
|
||||
monochrome_dont_stipple_flag = not enable
|
||||
}
|
||||
|
||||
sub rect(uword x, uword y, uword rwidth, uword rheight, ubyte color) {
|
||||
sub rect(uword xx, uword yy, uword rwidth, uword rheight, ubyte color) {
|
||||
if rwidth==0 or rheight==0
|
||||
return
|
||||
horizontal_line(x, y, rwidth, color)
|
||||
horizontal_line(xx, yy, rwidth, color)
|
||||
if rheight==1
|
||||
return
|
||||
horizontal_line(x, y+rheight-1, rwidth, color)
|
||||
vertical_line(x, y+1, rheight-2, color)
|
||||
horizontal_line(xx, yy+rheight-1, rwidth, color)
|
||||
vertical_line(xx, yy+1, rheight-2, color)
|
||||
if rwidth==1
|
||||
return
|
||||
vertical_line(x+rwidth-1, y+1, rheight-2, color)
|
||||
vertical_line(xx+rwidth-1, yy+1, rheight-2, color)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, uword y, uword rwidth, uword rheight, ubyte color) {
|
||||
sub fillrect(uword xx, uword yy, uword rwidth, uword rheight, ubyte color) {
|
||||
; Draw a filled rectangle of the given size and color.
|
||||
; To fill the whole screen, use clear_screen(color) instead - it is much faster.
|
||||
if rwidth==0
|
||||
return
|
||||
repeat rheight {
|
||||
horizontal_line(x, y, rwidth, color)
|
||||
y++
|
||||
horizontal_line(xx, yy, rwidth, color)
|
||||
yy++
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
sub horizontal_line(uword xx, uword yy, uword length, ubyte color) {
|
||||
if length==0
|
||||
return
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome modes, either resolution
|
||||
ubyte separate_pixels = (8-lsb(x)) & 7
|
||||
if separate_pixels as uword > length
|
||||
separate_pixels = lsb(length)
|
||||
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
|
||||
}
|
||||
if length {
|
||||
position(x, y)
|
||||
separate_pixels = lsb(length) & 7
|
||||
x += length & $fff8
|
||||
%asm {{
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda color
|
||||
bne +
|
||||
ldy #0 ; black
|
||||
bra _loop
|
||||
+ lda monochrome_dont_stipple_flag
|
||||
beq _stipple
|
||||
ldy #255 ; don't stipple
|
||||
bra _loop
|
||||
_stipple lda y
|
||||
and #1 ; determine stipple pattern to use
|
||||
bne +
|
||||
ldy #%01010101
|
||||
bra _loop
|
||||
+ ldy #%10101010
|
||||
_loop lda length
|
||||
ora length+1
|
||||
beq _done
|
||||
sty cx16.VERA_DATA0
|
||||
lda length
|
||||
bne +
|
||||
dec length+1
|
||||
+ dec length
|
||||
bra _loop
|
||||
_done
|
||||
}}
|
||||
|
||||
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
|
||||
}
|
||||
4 -> {
|
||||
1 -> {
|
||||
; lores 256c
|
||||
position(x, y)
|
||||
position(xx, yy)
|
||||
%asm {{
|
||||
lda color
|
||||
phx
|
||||
ldx length+1
|
||||
beq +
|
||||
ldy #0
|
||||
@@ -237,11 +138,11 @@ _done
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
+ plx
|
||||
+
|
||||
}}
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c ....also mostly usable for mode 2, lores 4c?
|
||||
2 -> {
|
||||
; highres 4c ....also mostly usable for lores 4c?
|
||||
color &= 3
|
||||
ubyte[4] colorbits
|
||||
ubyte ii
|
||||
@@ -249,7 +150,7 @@ _done
|
||||
colorbits[ii] = color
|
||||
color <<= 2
|
||||
}
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
lda cx16.VERA_ADDR_H
|
||||
and #%00000111 ; no auto advance
|
||||
@@ -262,8 +163,7 @@ _done
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
phx
|
||||
ldx x
|
||||
ldx xx
|
||||
}}
|
||||
|
||||
repeat length {
|
||||
@@ -285,73 +185,16 @@ _done
|
||||
+ inx ; next pixel
|
||||
}}
|
||||
}
|
||||
|
||||
%asm {{
|
||||
plx
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword lheight, ubyte color) {
|
||||
sub vertical_line(uword xx, uword yy, uword lheight, ubyte color) {
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome, lo-res
|
||||
cx16.r15L = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||
if color {
|
||||
if monochrome_dont_stipple_flag {
|
||||
; draw continuous line.
|
||||
position2(x,y,true)
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
} else {
|
||||
; draw stippled line.
|
||||
if x&1 {
|
||||
y++
|
||||
lheight--
|
||||
}
|
||||
position2(x,y,true)
|
||||
if active_mode==1
|
||||
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(13) ; 160 increment = 2 line in 640 px monochrome
|
||||
repeat lheight/2 {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
position2(x,y,true)
|
||||
cx16.r15 = ~cx16.r15 ; erase pixels
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
and cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
1 -> {
|
||||
; lores 256c
|
||||
; set vera auto-increment to 320 pixel increment (=next line)
|
||||
position(x,y)
|
||||
position(xx,yy)
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
||||
%asm {{
|
||||
ldy lheight
|
||||
@@ -363,22 +206,22 @@ _done
|
||||
+
|
||||
}}
|
||||
}
|
||||
6 -> {
|
||||
2 -> {
|
||||
; highres 4c
|
||||
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
|
||||
if lheight==0
|
||||
return
|
||||
position2(x,y,true)
|
||||
position2(xx,yy,true)
|
||||
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||
;; color &= 3
|
||||
;; color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||
cx16.r2L = lsb(x) & 3
|
||||
;; color <<= gfx2.plot.shift4c[lsb(xx) & 3]
|
||||
cx16.r2L = lsb(xx) & 3
|
||||
when color & 3 {
|
||||
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
|
||||
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
|
||||
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
|
||||
}
|
||||
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||
ubyte @shared mask = gfx2.plot.mask4c[lsb(xx) & 3]
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
@@ -557,8 +400,7 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub plot(uword @zp x, uword @zp y, ubyte @zp color) {
|
||||
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
sub plot(uword @zp xx, uword @zp yy, ubyte @zp color) {
|
||||
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]
|
||||
@@ -567,43 +409,8 @@ _done
|
||||
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
ora monochrome_dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
%asm {{
|
||||
lda x
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(320/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda bits,y
|
||||
ldy color
|
||||
beq +
|
||||
tsb cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ trb cx16.VERA_DATA0
|
||||
+
|
||||
}}
|
||||
}
|
||||
}
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1
|
||||
@@ -617,44 +424,10 @@ _done
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
ora monochrome_dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
%asm {{
|
||||
lda x
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(640/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda bits,y
|
||||
ldy color
|
||||
beq +
|
||||
tsb cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ trb cx16.VERA_DATA0
|
||||
+
|
||||
}}
|
||||
}
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c ....also mostly usable for mode 2, 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
|
||||
2 -> {
|
||||
; highres 4c ....also mostly usable for lores 4c?
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = lsb(xx) & 3 ; xbits
|
||||
; color &= 3
|
||||
; color <<= shift4c[cx16.r2L]
|
||||
when color & 3 {
|
||||
@@ -680,36 +453,11 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub pget(uword @zp x, uword y) -> ubyte {
|
||||
sub pget(uword @zp xx, uword yy) -> ubyte {
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(320/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda plot.bits,y
|
||||
and cx16.VERA_DATA0
|
||||
beq +
|
||||
lda #1
|
||||
+
|
||||
}}
|
||||
}
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1
|
||||
@@ -721,33 +469,9 @@ _done
|
||||
lda cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
5 -> {
|
||||
; hires monochrome
|
||||
%asm {{
|
||||
lda x
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
x /= 8
|
||||
x += y*(640/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda plot.bits,y
|
||||
and cx16.VERA_DATA0
|
||||
beq +
|
||||
lda #1
|
||||
+
|
||||
}}
|
||||
}
|
||||
6 -> {
|
||||
2 -> {
|
||||
; hires 4c
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1L
|
||||
@@ -757,17 +481,26 @@ _done
|
||||
lda cx16.r0L
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_DATA0
|
||||
sta cx16.r0L
|
||||
pha
|
||||
lda xx
|
||||
and #3
|
||||
tay
|
||||
lda gfx2.plot.shift4c,y
|
||||
tay
|
||||
pla
|
||||
cpy #0
|
||||
beq +
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
+ and #3
|
||||
}}
|
||||
cx16.r1L = lsb(x) & 3
|
||||
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) {
|
||||
sub fill(word @zp xx, word @zp yy, 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.
|
||||
@@ -830,85 +563,76 @@ _done
|
||||
lda stack_xr_msb,y
|
||||
sta x2+1
|
||||
lda stack_y_lsb,y
|
||||
sta y
|
||||
sta yy
|
||||
lda stack_y_msb,y
|
||||
sta y+1
|
||||
sta yy+1
|
||||
ldy cx16.r12L
|
||||
lda stack_dy,y
|
||||
sta dy
|
||||
}}
|
||||
y+=dy
|
||||
yy+=dy
|
||||
}
|
||||
cx16.r11L = pget(x as uword, y as uword) ; old_color
|
||||
cx16.r11L = pget(xx as uword, yy as uword) ; old_color
|
||||
if cx16.r11L == cx16.r10L
|
||||
return
|
||||
if x<0 or x > width-1 or y<0 or y > height-1
|
||||
if xx<0 or xx>width-1 or yy<0 or yy>height-1
|
||||
return
|
||||
push_stack(x, x, y, 1)
|
||||
push_stack(x, x, y + 1, -1)
|
||||
push_stack(xx, xx, yy, 1)
|
||||
push_stack(xx, xx, yy + 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
|
||||
xx = x1
|
||||
; TODO: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
|
||||
while xx >= 0 and pget(xx as uword, yy as uword) == cx16.r11L
|
||||
xx--
|
||||
if x1!=xx
|
||||
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L)
|
||||
else
|
||||
goto skip
|
||||
|
||||
left = x + 1
|
||||
left = xx + 1
|
||||
if left < x1
|
||||
push_stack(left, x1 - 1, y, -dy)
|
||||
x = x1 + 1
|
||||
push_stack(left, x1 - 1, yy, -dy)
|
||||
xx = 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)
|
||||
cx16.r9 = xx
|
||||
; TODO: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it?
|
||||
while xx <= width-1 and pget(xx as uword, yy as uword) == cx16.r11L
|
||||
xx++
|
||||
if cx16.r9!=xx
|
||||
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L)
|
||||
|
||||
push_stack(left, xx - 1, yy, dy)
|
||||
if xx > x2 + 1
|
||||
push_stack(x2 + 1, xx - 1, yy, -dy)
|
||||
skip:
|
||||
x++
|
||||
while x <= x2 and pget(x as uword, y as uword) != cx16.r11L
|
||||
x++
|
||||
left = x
|
||||
} until x>x2
|
||||
xx++
|
||||
while xx <= x2 and pget(xx as uword, yy as uword) != cx16.r11L
|
||||
xx++
|
||||
left = xx
|
||||
} until xx>x2
|
||||
}
|
||||
}
|
||||
|
||||
sub position(uword @zp x, uword y) {
|
||||
sub position(uword @zp xx, uword yy) {
|
||||
when active_mode {
|
||||
1 -> {
|
||||
; lores monochrome
|
||||
cx16.r0 = y*(320/8) + x/8
|
||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||
}
|
||||
; TODO modes 2, 3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = cx16.r1L
|
||||
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
|
||||
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
cx16.r0 = y*(640/8) + x/8
|
||||
cx16.vaddr(0, cx16.r0, 0, 1)
|
||||
}
|
||||
6 -> {
|
||||
2 -> {
|
||||
; highres 4c
|
||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.r2L = cx16.r1L
|
||||
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
|
||||
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
}
|
||||
}
|
||||
cx16.r2L = cx16.r1L
|
||||
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
|
||||
}
|
||||
|
||||
sub position2(uword @zp x, uword y, bool also_port_1) {
|
||||
position(x, y)
|
||||
sub position2(uword @zp xx, uword yy, bool also_port_1) {
|
||||
position(xx, yy)
|
||||
if also_port_1
|
||||
cx16.vaddr_clone(0)
|
||||
}
|
||||
@@ -916,20 +640,17 @@ skip:
|
||||
inline asmsub next_pixel(ubyte color @A) {
|
||||
; -- sets the next pixel byte to the graphics chip.
|
||||
; for 8 bpp screens this will plot 1 pixel.
|
||||
; for 1 bpp screens it will plot 8 pixels at once (color = bit pattern).
|
||||
; for 2 bpp screens it will plot 4 pixels at once (color = bit pattern).
|
||||
%asm {{
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, Y) {
|
||||
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, X, Y) {
|
||||
; -- sets the next bunch of pixels from a prepared array of bytes.
|
||||
; for 8 bpp screens this will plot 1 pixel per byte.
|
||||
; for 1 bpp screens it will plot 8 pixels at once (colors are the bit patterns per byte).
|
||||
; for 2 bpp screens it will plot 4 pixels at once (colors are the bit patterns per byte).
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r0+1
|
||||
@@ -951,14 +672,13 @@ skip:
|
||||
iny
|
||||
dex
|
||||
bne -
|
||||
+ plx
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) {
|
||||
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) clobbers(X) {
|
||||
; this is only useful in 256 color mode where one pixel equals one byte value.
|
||||
%asm {{
|
||||
phx
|
||||
ldx #8
|
||||
- asl cx16.r0
|
||||
bcc +
|
||||
@@ -967,7 +687,6 @@ skip:
|
||||
+ sty cx16.VERA_DATA0
|
||||
+ dex
|
||||
bne -
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -982,7 +701,7 @@ skip:
|
||||
cx16.screen_set_charset(charset, 0)
|
||||
}
|
||||
|
||||
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
|
||||
sub text(uword @zp xx, uword yy, ubyte color, uword sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
uword chardataptr
|
||||
@@ -990,103 +709,15 @@ skip:
|
||||
ubyte[8] @shared char_bitmap_bytes_right
|
||||
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome mode, either resolution
|
||||
cx16.r3 = sctextptr
|
||||
while @(cx16.r3) {
|
||||
chardataptr = charset_addr + @(cx16.r3) * $0008
|
||||
; copy the character bitmap into RAM
|
||||
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
|
||||
%asm {{
|
||||
; pre-shift the bits
|
||||
phx ; TODO remove in non-stack version
|
||||
lda text.x
|
||||
and #7
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
- lda cx16.VERA_DATA0
|
||||
stz P8ZP_SCRATCH_REG
|
||||
ldx P8ZP_SCRATCH_B1
|
||||
cpx #0
|
||||
beq +
|
||||
- lsr a
|
||||
ror P8ZP_SCRATCH_REG
|
||||
dex
|
||||
bne -
|
||||
+ sta char_bitmap_bytes_left,y
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta char_bitmap_bytes_right,y
|
||||
iny
|
||||
cpy #8
|
||||
bne --
|
||||
plx ; TODO remove in non-stack version
|
||||
}}
|
||||
; left part of shifted char
|
||||
position2(x, y, true)
|
||||
set_autoincrs_mode1_or_5()
|
||||
if color {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_left,y
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
} else {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_left,y
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
}
|
||||
; right part of shifted char
|
||||
if lsb(x) & 7 {
|
||||
position2(x+8, y, true)
|
||||
set_autoincrs_mode1_or_5()
|
||||
if color {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_right,y
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
} else {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_right,y
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
}
|
||||
}
|
||||
cx16.r3++
|
||||
x += 8
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
1 -> {
|
||||
; lores 256c
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||
repeat 8 {
|
||||
position(x,y)
|
||||
y++
|
||||
position(xx,yy)
|
||||
yy++
|
||||
%asm {{
|
||||
phx ; TODO remove in non-stack version
|
||||
ldx color
|
||||
lda cx16.VERA_DATA1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@@ -1098,26 +729,25 @@ skip:
|
||||
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
|
||||
+ dey
|
||||
bne -
|
||||
plx ; TODO remove in non-stack version
|
||||
}}
|
||||
}
|
||||
x+=8
|
||||
y-=8
|
||||
xx+=8
|
||||
yy-=8
|
||||
sctextptr++
|
||||
}
|
||||
}
|
||||
6 -> {
|
||||
2 -> {
|
||||
; hires 4c
|
||||
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
|
||||
cx16.r11L = color
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
|
||||
position(x, y) ; only calculated once, we update vera address in the loop instead
|
||||
position(xx, yy) ; only calculated once, we update vera address in the loop instead
|
||||
cx16.VERA_ADDR_H &= $0f ; no auto increment
|
||||
repeat 8 {
|
||||
cx16.r10L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
|
||||
cx16.r7 = x
|
||||
cx16.r7 = xx
|
||||
repeat 8 {
|
||||
cx16.r10L <<= 1
|
||||
if_cs {
|
||||
@@ -1164,39 +794,24 @@ skip:
|
||||
sta cx16.VERA_ADDR_H
|
||||
}}
|
||||
}
|
||||
x+=8
|
||||
xx+=8
|
||||
sctextptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub set_autoincrs_mode1_or_5() {
|
||||
; set autoincrements to go to next pixel row (40 or 80 increment)
|
||||
if active_mode==1 {
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
|
||||
} else {
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asmsub cs_innerloop640() clobbers(Y) {
|
||||
asmsub cs_innerloop640(ubyte color @A) clobbers(Y) {
|
||||
%asm {{
|
||||
ldy #80
|
||||
- stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
stz cx16.VERA_DATA0
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
; Wraps the graphics functions that are in ROM.
|
||||
; Only lo-res 320x240 256 color mode for now.
|
||||
; Unlike graphics module on the C64, you can use colors() to set new drawing colors for every draw operation.
|
||||
; For other resolutions or other color modes, use the "gfx2" module instead. (which is Cx16-specific)
|
||||
; For other resolutions or other color modes, use the "gfx2" or "monogfx" module instead. (which is Cx16-specific)
|
||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ graphics {
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
void cx16.screen_mode(0, false)
|
||||
txt.fill_screen(' ', 1) ; doesn't seem to fully clear the text screen after returning from gfx mode
|
||||
txt.clear_screen()
|
||||
}
|
||||
|
||||
|
||||
@@ -50,22 +50,22 @@ graphics {
|
||||
cx16.GRAPH_draw_line(x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
sub fillrect(uword x, uword y, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(x, y, width, height, 0, 1)
|
||||
sub fillrect(uword xx, uword yy, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 1)
|
||||
}
|
||||
|
||||
sub rect(uword x, uword y, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(x, y, width, height, 0, 0)
|
||||
sub rect(uword xx, uword yy, uword width, uword height) {
|
||||
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 0)
|
||||
}
|
||||
|
||||
sub horizontal_line(uword x, uword y, uword length) {
|
||||
sub horizontal_line(uword xx, uword yy, uword length) {
|
||||
if length
|
||||
cx16.GRAPH_draw_line(x, y, x+length-1, y)
|
||||
cx16.GRAPH_draw_line(xx, yy, xx+length-1, yy)
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword height) {
|
||||
sub vertical_line(uword xx, uword yy, uword height) {
|
||||
if height
|
||||
cx16.GRAPH_draw_line(x, y, x, y+height-1)
|
||||
cx16.GRAPH_draw_line(xx, yy, xx, yy+height-1)
|
||||
}
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
|
||||
@@ -0,0 +1,791 @@
|
||||
; Monochrome Bitmap pixel graphics routines for the CommanderX16
|
||||
; Using the full-screen 640x480 and 320x240 screen modes, in 1 bpp mode (black/white).
|
||||
;
|
||||
; No text layer is currently shown, but text can be drawn as part of the bitmap itself.
|
||||
; For color bitmap graphics, see the gfx2 library.
|
||||
;
|
||||
; NOTE: a lot of the code here is similar or the same to that in gfx2
|
||||
|
||||
monogfx {
|
||||
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; read-only control variables:
|
||||
uword width = 0
|
||||
uword height = 0
|
||||
bool dont_stipple_flag = true ; set to false to enable stippling mode
|
||||
|
||||
sub lores() {
|
||||
; enable 320*240 bitmap mode
|
||||
cx16.VERA_CTRL=0
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
cx16.VERA_DC_VSCALE = 64
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0
|
||||
width = 320
|
||||
height = 240
|
||||
clear_screen(0)
|
||||
}
|
||||
|
||||
sub hires() {
|
||||
; enable 640*480 bitmap mode
|
||||
cx16.VERA_CTRL=0
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 128
|
||||
cx16.VERA_DC_VSCALE = 128
|
||||
cx16.VERA_L1_CONFIG = %00000100
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = %00000001
|
||||
width = 640
|
||||
height = 480
|
||||
clear_screen(0)
|
||||
}
|
||||
|
||||
sub textmode() {
|
||||
; back to normal text mode
|
||||
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
|
||||
cbm.CINT()
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte color) {
|
||||
stipple(false)
|
||||
position(0, 0)
|
||||
when width {
|
||||
320 -> {
|
||||
repeat 240/2/8
|
||||
cs_innerloop640(color)
|
||||
}
|
||||
640 -> {
|
||||
repeat 480/8
|
||||
cs_innerloop640(color)
|
||||
}
|
||||
}
|
||||
position(0, 0)
|
||||
}
|
||||
|
||||
sub stipple(bool enable) {
|
||||
dont_stipple_flag = not enable
|
||||
}
|
||||
|
||||
sub rect(uword xx, uword yy, uword rwidth, uword rheight, bool draw) {
|
||||
if rwidth==0 or rheight==0
|
||||
return
|
||||
horizontal_line(xx, yy, rwidth, draw)
|
||||
if rheight==1
|
||||
return
|
||||
horizontal_line(xx, yy+rheight-1, rwidth, draw)
|
||||
vertical_line(xx, yy+1, rheight-2, draw)
|
||||
if rwidth==1
|
||||
return
|
||||
vertical_line(xx+rwidth-1, yy+1, rheight-2, draw)
|
||||
}
|
||||
|
||||
sub fillrect(uword xx, uword yy, uword rwidth, uword rheight, bool draw) {
|
||||
; Draw a filled rectangle of the given size and color.
|
||||
; To fill the whole screen, use clear_screen(color) instead - it is much faster.
|
||||
if rwidth==0
|
||||
return
|
||||
repeat rheight {
|
||||
horizontal_line(xx, yy, rwidth, draw)
|
||||
yy++
|
||||
}
|
||||
}
|
||||
|
||||
sub horizontal_line(uword xx, uword yy, uword length, bool draw) {
|
||||
ubyte[9] masked_starts = [ 0, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111]
|
||||
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
|
||||
|
||||
if length==0
|
||||
return
|
||||
if length<=8 {
|
||||
; just use 2 byte writes with shifted mask
|
||||
position2(xx,yy,true)
|
||||
%asm {{
|
||||
ldy length
|
||||
lda masked_ends,y
|
||||
sta cx16.r0L ; save left byte
|
||||
stz P8ZP_SCRATCH_B1
|
||||
lda xx
|
||||
and #7
|
||||
beq +
|
||||
tay
|
||||
lda cx16.r0L
|
||||
- lsr a
|
||||
ror P8ZP_SCRATCH_B1
|
||||
dey
|
||||
bne -
|
||||
sta cx16.r0L ; new left byte
|
||||
+
|
||||
lda dont_stipple_flag
|
||||
bne _dontstipple
|
||||
; determine stipple pattern
|
||||
lda yy
|
||||
and #1
|
||||
beq +
|
||||
lda #%10101010
|
||||
bne ++
|
||||
+ lda #%01010101
|
||||
+ sta P8ZP_SCRATCH_REG
|
||||
lda cx16.r0L
|
||||
and P8ZP_SCRATCH_REG
|
||||
sta cx16.r0L
|
||||
lda P8ZP_SCRATCH_B1
|
||||
and P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
_dontstipple
|
||||
lda draw
|
||||
beq _clear
|
||||
lda cx16.r0L ; left byte
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
lda P8ZP_SCRATCH_B1 ; right byte
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
_clear
|
||||
lda cx16.r0L ; left byte
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
lda P8ZP_SCRATCH_B1 ; right byte
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
ubyte separate_pixels = (8-lsb(xx)) & 7
|
||||
if separate_pixels {
|
||||
if dont_stipple_flag {
|
||||
position(xx,yy)
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
|
||||
if draw
|
||||
cx16.VERA_DATA0 |= masked_starts[separate_pixels]
|
||||
else
|
||||
cx16.VERA_DATA0 &= ~masked_starts[separate_pixels]
|
||||
xx += separate_pixels
|
||||
} else {
|
||||
repeat separate_pixels {
|
||||
plot(xx, yy, draw)
|
||||
xx++
|
||||
}
|
||||
}
|
||||
length -= separate_pixels
|
||||
}
|
||||
if length {
|
||||
position(xx, yy)
|
||||
separate_pixels = lsb(length) & 7
|
||||
xx += length & $fff8
|
||||
%asm {{
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lsr length+1
|
||||
ror length
|
||||
lda draw
|
||||
bne +
|
||||
ldy #0 ; black
|
||||
bra _loop
|
||||
+ lda dont_stipple_flag
|
||||
beq _stipple
|
||||
ldy #255 ; don't stipple
|
||||
bra _loop
|
||||
_stipple lda yy
|
||||
and #1 ; determine stipple pattern to use
|
||||
bne +
|
||||
ldy #%01010101
|
||||
bra _loop
|
||||
+ ldy #%10101010
|
||||
_loop lda length
|
||||
ora length+1
|
||||
beq _done
|
||||
sty cx16.VERA_DATA0
|
||||
lda length
|
||||
bne +
|
||||
dec length+1
|
||||
+ dec length
|
||||
bra _loop
|
||||
_done
|
||||
}}
|
||||
|
||||
if dont_stipple_flag {
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
|
||||
if draw
|
||||
cx16.VERA_DATA0 |= masked_ends[separate_pixels]
|
||||
else
|
||||
cx16.VERA_DATA0 &= ~masked_ends[separate_pixels]
|
||||
} else {
|
||||
repeat separate_pixels {
|
||||
plot(xx, yy, draw)
|
||||
xx++
|
||||
}
|
||||
}
|
||||
}
|
||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
|
||||
}
|
||||
|
||||
sub vertical_line(uword xx, uword yy, uword lheight, bool draw) {
|
||||
cx16.r15L = monogfx.plot.maskbits[xx as ubyte & 7] ; bitmask
|
||||
if draw {
|
||||
if dont_stipple_flag {
|
||||
; draw continuous line.
|
||||
position2(xx,yy,true)
|
||||
if width==320
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
} else {
|
||||
; draw stippled line.
|
||||
if (xx ^ yy)&1==0 {
|
||||
yy++
|
||||
lheight--
|
||||
}
|
||||
lheight++ ; because it is divided by 2 later, don't round off the last pixel
|
||||
position2(xx,yy,true)
|
||||
if width==320
|
||||
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(13) ; 160 increment = 2 line in 640 px monochrome
|
||||
repeat lheight/2 {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
position2(xx,yy,true)
|
||||
cx16.r15 = ~cx16.r15 ; erase pixels
|
||||
if width==320
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat lheight {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
and cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
sub set_both_strides(ubyte stride) {
|
||||
stride <<= 4
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, bool draw) {
|
||||
; Bresenham algorithm.
|
||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||
cx16.r0 = x1
|
||||
x1 = x2
|
||||
x2 = cx16.r0
|
||||
cx16.r0 = y1
|
||||
y1 = y2
|
||||
y2 = cx16.r0
|
||||
}
|
||||
word @zp dx = (x2 as word)-x1
|
||||
word @zp dy = (y2 as word)-y1
|
||||
|
||||
if dx==0 {
|
||||
vertical_line(x1, y1, abs(dy) as uword +1, draw)
|
||||
return
|
||||
}
|
||||
if dy==0 {
|
||||
if x1>x2
|
||||
x1=x2
|
||||
horizontal_line(x1, y1, abs(dx) as uword +1, draw)
|
||||
return
|
||||
}
|
||||
|
||||
word @zp d = 0
|
||||
cx16.r1L = true ; 'positive_ix'
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
cx16.r1L = false
|
||||
}
|
||||
word @zp dx2 = dx*2
|
||||
word @zp dy2 = dy*2
|
||||
cx16.r14 = x1 ; internal plot X
|
||||
|
||||
if dx >= dy {
|
||||
if cx16.r1L {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if cx16.r14==x2
|
||||
return
|
||||
cx16.r14++
|
||||
d += dy2
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if cx16.r14==x2
|
||||
return
|
||||
cx16.r14--
|
||||
d += dy2
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if cx16.r1L {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
cx16.r14++
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot(cx16.r14, y1, draw)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
cx16.r14--
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub circle(uword @zp xcenter, uword @zp ycenter, ubyte radius, bool draw) {
|
||||
; Midpoint algorithm.
|
||||
if radius==0
|
||||
return
|
||||
|
||||
ubyte @zp xx = radius
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-xx
|
||||
; R14 = internal plot X
|
||||
; R15 = internal plot Y
|
||||
|
||||
while xx>=yy {
|
||||
cx16.r14 = xcenter + xx
|
||||
cx16.r15 = ycenter + yy
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
cx16.r14 = xcenter - xx
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
cx16.r14 = xcenter + xx
|
||||
cx16.r15 = ycenter - yy
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
cx16.r14 = xcenter - xx
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
cx16.r14 = xcenter + yy
|
||||
cx16.r15 = ycenter + xx
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
cx16.r14 = xcenter - yy
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
cx16.r14 = xcenter + yy
|
||||
cx16.r15 = ycenter - xx
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
cx16.r14 = xcenter - yy
|
||||
plot(cx16.r14, cx16.r15, draw)
|
||||
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
else {
|
||||
xx--
|
||||
decisionOver2 += (yy as word -xx)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||
; Midpoint algorithm, filled
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += (yy as word)*2+1
|
||||
else {
|
||||
radius--
|
||||
decisionOver2 += (yy as word -radius)*2+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub plot(uword @zp xx, uword @zp yy, bool @zp draw) {
|
||||
ubyte[8] @shared maskbits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
if draw {
|
||||
; solid color or perhaps stipple
|
||||
%asm {{
|
||||
lda xx
|
||||
eor yy
|
||||
ora dont_stipple_flag
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
prepare()
|
||||
%asm {{
|
||||
tsb cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
} else {
|
||||
; only erase
|
||||
prepare()
|
||||
%asm {{
|
||||
trb cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
|
||||
sub prepare() {
|
||||
%asm {{
|
||||
lda xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
xx /= 8
|
||||
if width==320
|
||||
xx += yy*(320/8)
|
||||
else
|
||||
xx += yy*(640/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda maskbits,y
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
sub pget(uword @zp xx, uword yy) -> ubyte {
|
||||
%asm {{
|
||||
lda xx
|
||||
and #7
|
||||
pha ; xbits
|
||||
}}
|
||||
xx /= 8
|
||||
if width==320
|
||||
xx += yy*(320/8)
|
||||
else
|
||||
xx += yy*(640/8)
|
||||
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda xx+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda xx
|
||||
sta cx16.VERA_ADDR_L
|
||||
ply ; xbits
|
||||
lda plot.maskbits,y
|
||||
and cx16.VERA_DATA0
|
||||
beq +
|
||||
lda #1
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub fill(word @zp xx, word @zp yy, bool draw) {
|
||||
; 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 = draw as ubyte
|
||||
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 yy
|
||||
lda stack_y_msb,y
|
||||
sta yy+1
|
||||
ldy cx16.r12L
|
||||
lda stack_dy,y
|
||||
sta dy
|
||||
}}
|
||||
yy+=dy
|
||||
}
|
||||
cx16.r11L = pget(xx as uword, yy as uword) ; old_color
|
||||
if cx16.r11L == cx16.r10L
|
||||
return
|
||||
if xx<0 or xx>width-1 or yy<0 or yy>height-1
|
||||
return
|
||||
push_stack(xx, xx, yy, 1)
|
||||
push_stack(xx, xx, yy + 1, -1)
|
||||
word left = 0
|
||||
while cx16.r12L {
|
||||
pop_stack()
|
||||
xx = x1
|
||||
while xx >= 0 and pget(xx as uword, yy as uword) == cx16.r11L
|
||||
xx--
|
||||
if x1!=xx
|
||||
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L)
|
||||
else
|
||||
goto skip
|
||||
|
||||
left = xx + 1
|
||||
if left < x1
|
||||
push_stack(left, x1 - 1, yy, -dy)
|
||||
xx = x1 + 1
|
||||
|
||||
do {
|
||||
cx16.r9 = xx
|
||||
while xx <= width-1 and pget(xx as uword, yy as uword) == cx16.r11L
|
||||
xx++
|
||||
if cx16.r9!=xx
|
||||
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L)
|
||||
|
||||
push_stack(left, xx - 1, yy, dy)
|
||||
if xx > x2 + 1
|
||||
push_stack(x2 + 1, xx - 1, yy, -dy)
|
||||
skip:
|
||||
xx++
|
||||
while xx <= x2 and pget(xx as uword, yy as uword) != cx16.r11L
|
||||
xx++
|
||||
left = xx
|
||||
} until xx>x2
|
||||
}
|
||||
}
|
||||
|
||||
sub position(uword @zp xx, uword yy) {
|
||||
if width==320
|
||||
cx16.r0 = yy*(320/8)
|
||||
else
|
||||
cx16.r0 = yy*(640/8)
|
||||
cx16.vaddr(0, cx16.r0+(xx/8), 0, 1)
|
||||
}
|
||||
|
||||
sub position2(uword @zp xx, uword yy, bool also_port_1) {
|
||||
position(xx, yy)
|
||||
if also_port_1
|
||||
cx16.vaddr_clone(0)
|
||||
}
|
||||
|
||||
const ubyte charset_bank = $1
|
||||
const uword charset_addr = $f000 ; in bank 1, so $1f000
|
||||
|
||||
sub text_charset(ubyte charset) {
|
||||
; -- select the text charset to use with the text() routine
|
||||
; the charset number is the same as for the cx16.screen_set_charset() ROM function.
|
||||
; 1 = ISO charset, 2 = PETSCII uppercase+graphs, 3= PETSCII uppercase+lowercase.
|
||||
cx16.screen_set_charset(charset, 0)
|
||||
}
|
||||
|
||||
sub text(uword @zp xx, uword yy, bool draw, uword sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
uword chardataptr
|
||||
ubyte[8] @shared char_bitmap_bytes_left
|
||||
ubyte[8] @shared char_bitmap_bytes_right
|
||||
|
||||
cx16.r3 = sctextptr
|
||||
while @(cx16.r3) {
|
||||
chardataptr = charset_addr + @(cx16.r3) * $0008
|
||||
; copy the character bitmap into RAM
|
||||
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
|
||||
%asm {{
|
||||
; pre-shift the bits
|
||||
lda text.xx
|
||||
and #7
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
- lda cx16.VERA_DATA0
|
||||
stz P8ZP_SCRATCH_REG
|
||||
ldx P8ZP_SCRATCH_B1
|
||||
cpx #0
|
||||
beq +
|
||||
- lsr a
|
||||
ror P8ZP_SCRATCH_REG
|
||||
dex
|
||||
bne -
|
||||
+ sta char_bitmap_bytes_left,y
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta char_bitmap_bytes_right,y
|
||||
iny
|
||||
cpy #8
|
||||
bne --
|
||||
}}
|
||||
; left part of shifted char
|
||||
position2(xx, yy, true)
|
||||
set_autoincrs()
|
||||
if draw {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_left,y
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
} else {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_left,y
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
}
|
||||
; right part of shifted char
|
||||
if lsb(xx) & 7 {
|
||||
position2(xx+8, yy, true)
|
||||
set_autoincrs()
|
||||
if draw {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_right,y
|
||||
ora cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
} else {
|
||||
%asm {{
|
||||
ldy #0
|
||||
- lda char_bitmap_bytes_right,y
|
||||
eor #255
|
||||
and cx16.VERA_DATA1
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
cpy #8
|
||||
bne -
|
||||
}}
|
||||
}
|
||||
}
|
||||
cx16.r3++
|
||||
xx += 8
|
||||
}
|
||||
|
||||
sub set_autoincrs() {
|
||||
; set autoincrements to go to next pixel row (40 or 80 increment)
|
||||
if width==320 {
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
|
||||
} else {
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asmsub cs_innerloop640(ubyte color @A) clobbers(Y) {
|
||||
%asm {{
|
||||
cmp #0
|
||||
beq +
|
||||
lda #255
|
||||
+ ldy #80
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,11 @@ psg {
|
||||
const ubyte LEFT = %01000000
|
||||
const ubyte RIGHT = %10000000
|
||||
|
||||
sub voice(ubyte voice_num, ubyte channel, ubyte volume, ubyte waveform, ubyte pulsewidth) {
|
||||
sub voice(ubyte voice_num, ubyte channel, ubyte vol, ubyte waveform, ubyte pulsewidth) {
|
||||
; -- Enables a 'voice' on the PSG.
|
||||
; voice_num = 0-15, the voice number.
|
||||
; channel = either LEFT or RIGHT or (LEFT|RIGHT). Specifies the stereo channel(s) to use.
|
||||
; volume = 0-63, the starting volume for the voice
|
||||
; vol = 0-63, the starting volume for the voice
|
||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||
envelope_states[voice_num] = 255
|
||||
@@ -30,11 +30,11 @@ psg {
|
||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||
cx16.VERA_ADDR_H = 1
|
||||
cx16.VERA_DATA0 = channel | volume
|
||||
cx16.VERA_DATA0 = channel | vol
|
||||
cx16.VERA_ADDR_L++
|
||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||
envelope_volumes[voice_num] = mkword(volume, 0)
|
||||
envelope_maxvolumes[voice_num] = volume
|
||||
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||
envelope_maxvolumes[voice_num] = vol
|
||||
sys.irqsafe_clear_irqd()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
; Simple routines to control sprites.
|
||||
; They're not written for high performance, but for simplicity.
|
||||
; That's why they control 1 sprite at a time. The exception is pos_batch().
|
||||
; which is quite efficient to update sprite positions of multiple sprites in one go.
|
||||
|
||||
; note: sprites z-order will be in front of all layers.
|
||||
; note: collision mask is not supported here yet.
|
||||
|
||||
sprites {
|
||||
const ubyte SIZE_8 = 0
|
||||
const ubyte SIZE_16 = 1
|
||||
const ubyte SIZE_32 = 2
|
||||
const ubyte SIZE_64 = 3
|
||||
const ubyte COLORS_16 = 0
|
||||
const ubyte COLORS_256 = 128
|
||||
const uword VERA_SPRITEREGS = $fc00 ; $1fc00
|
||||
uword @zp sprite_reg
|
||||
|
||||
sub init(ubyte spritenum,
|
||||
ubyte databank, uword dataaddr,
|
||||
ubyte width_flag, ubyte height_flag,
|
||||
ubyte colors_flag, ubyte palette_offset_idx) {
|
||||
hide(spritenum)
|
||||
cx16.VERA_DC_VIDEO |= %01000000 ; enable sprites globally
|
||||
dataaddr >>= 5
|
||||
dataaddr |= (databank as uword)<<11
|
||||
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(dataaddr)) ; address 12:5
|
||||
cx16.vpoke(1, sprite_reg+1, colors_flag | msb(dataaddr)) ; 4 bpp + address 16:13
|
||||
cx16.vpoke(1, sprite_reg+6, %00001100) ; z depth %11 = in front of both layers, no flips
|
||||
cx16.vpoke(1, sprite_reg+7, height_flag<<6 | width_flag<<4 | palette_offset_idx>>4) ; 64x64 pixels, palette offset
|
||||
}
|
||||
|
||||
sub data(ubyte spritenum, ubyte bank, uword addr) {
|
||||
addr >>= 5
|
||||
addr |= (bank as uword)<<11
|
||||
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(addr)) ; address 12:5
|
||||
cx16.vpoke_mask(1, sprite_reg+1, %11110000, msb(addr)) ; address 16:13
|
||||
}
|
||||
|
||||
sub pos(ubyte spritenum, word xpos, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+2, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+3, msb(ypos))
|
||||
}
|
||||
|
||||
sub pos_batch(ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr) {
|
||||
; -- note: the x and y positions word arrays must be regular arrays, they cannot be @split arrays!
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
}
|
||||
sprite_reg += 2
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
}
|
||||
}
|
||||
|
||||
sub setx(ubyte spritenum, word xpos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
}
|
||||
|
||||
sub sety(ubyte spritenum, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(ypos))
|
||||
}
|
||||
|
||||
sub getx(ubyte spritenum) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
|
||||
sub gety(ubyte spritenum) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
|
||||
sub hide(ubyte spritenum) {
|
||||
pos(spritenum, -64, -64)
|
||||
}
|
||||
|
||||
sub flipx(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111110, flipped)
|
||||
}
|
||||
|
||||
sub flipy(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111101, flipped<<1)
|
||||
}
|
||||
|
||||
sub set_palette_offset(ubyte spritenum, ubyte offset) {
|
||||
sprite_reg = VERA_SPRITEREGS + 7 + spritenum*$0008
|
||||
cx16.vpoke_mask(1, sprite_reg, %11110000, offset>>4)
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798
|
||||
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 $FFD2 = CHROUT(ubyte character @ 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) 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
|
||||
@@ -53,25 +53,21 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
||||
|
||||
; ---- utility
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
asmsub STOP2() clobbers(X) -> 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 cbm.STOP
|
||||
beq +
|
||||
plx
|
||||
lda #0
|
||||
rts
|
||||
+ plx
|
||||
lda #1
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() -> uword @AY {
|
||||
asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs.
|
||||
%asm {{
|
||||
phx
|
||||
php
|
||||
sei
|
||||
jsr cbm.RDTIM
|
||||
@@ -81,7 +77,6 @@ asmsub RDTIM16() -> uword @AY {
|
||||
txa
|
||||
tay
|
||||
pla
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -272,6 +267,17 @@ cx16 {
|
||||
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
|
||||
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
|
||||
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
|
||||
|
||||
; experimental Vera FX registers: (depends on particular value set in VERA_CTRL!!!)
|
||||
&ubyte VERA_FX_CTRL = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_MULT = VERA_BASE + $000C
|
||||
&ubyte VERA_FX_CACHE_L = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_CACHE_M = VERA_BASE + $000A
|
||||
&ubyte VERA_FX_CACHE_H = VERA_BASE + $000B
|
||||
&ubyte VERA_FX_CACHE_U = VERA_BASE + $000C
|
||||
&ubyte VERA_FX_ACCUM_RESET = VERA_BASE + $0009 ; (DCSEL=6)
|
||||
|
||||
|
||||
; VERA_PSG_BASE = $1F9C0
|
||||
; VERA_PALETTE_BASE = $1FA00
|
||||
; VERA_SPRITES_BASE = $1FC00
|
||||
@@ -325,17 +331,16 @@ cx16 {
|
||||
; spelling of the names is taken from the Commander X-16 rom sources
|
||||
|
||||
; supported C128 additions
|
||||
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 $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, bool getCurrent @Pc) clobbers(X, Y) -> ubyte @A, bool @Pc ; note: X,Y size result is not supported, use SCREEN or get_screen_mode routine for that
|
||||
romsub $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
|
||||
romsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
|
||||
romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
|
||||
romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||
romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||
romsub $ff7d = primm()
|
||||
romsub $ff7d = PRIMM()
|
||||
|
||||
; It's not documented what registers are clobbered, so we assume the worst for all following kernal routines...:
|
||||
|
||||
@@ -351,8 +356,8 @@ romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword
|
||||
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, 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
|
||||
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte character @A) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_next_char(ubyte character @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
|
||||
|
||||
; framebuffer
|
||||
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
||||
@@ -382,14 +387,15 @@ romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @
|
||||
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, bool wrapping @Pc) clobbers(A,X,Y)
|
||||
romsub $fede = console_put_char(ubyte character @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)
|
||||
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 $ff44 = MACPTR(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
|
||||
romsub $feb1 = MCIOUT(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
|
||||
romsub $ff47 = enter_basic(bool cold_or_warm @Pc) clobbers(A,X,Y)
|
||||
romsub $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,6 +406,7 @@ romsub $febd = kbdbuf_peek() -> ubyte @A, ubyte @X ; key in A, queue length
|
||||
romsub $febd = kbdbuf_peek2() -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
||||
romsub $fec0 = kbdbuf_get_modifiers() -> ubyte @A
|
||||
romsub $fec3 = kbdbuf_put(ubyte key @A) clobbers(X)
|
||||
romsub $fed2 = keymap(uword identifier @XY, bool read @Pc) -> bool @Pc
|
||||
romsub $ff68 = mouse_config(ubyte shape @A, ubyte resX @X, ubyte resY @Y) clobbers (A, X, Y)
|
||||
romsub $ff6b = mouse_get(ubyte zpdataptr @X) -> ubyte @A
|
||||
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||
@@ -408,10 +415,10 @@ romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
||||
|
||||
; Audio (rom bank 10)
|
||||
romsub $C04B = psg_init() clobbers(A,X,Y)
|
||||
romsub $C04B = psg_init() clobbers(A,X,Y) ; (re)init Vera PSG
|
||||
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
|
||||
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize both vera PSG and YM audio chips
|
||||
; TODO: add more of the audio routines?
|
||||
|
||||
|
||||
@@ -454,15 +461,12 @@ asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub mouse_pos() -> ubyte @A {
|
||||
asmsub mouse_pos() clobbers(X) -> ubyte @A {
|
||||
; -- short wrapper around mouse_get() kernal routine:
|
||||
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status.
|
||||
%asm {{
|
||||
phx
|
||||
ldx #cx16.r0
|
||||
jsr cx16.mouse_get
|
||||
plx
|
||||
rts
|
||||
jmp cx16.mouse_get
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -500,7 +504,7 @@ inline asmsub getrambank() -> ubyte @A {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub numbanks() -> uword @AY {
|
||||
asmsub numbanks() clobbers(X) -> uword @AY {
|
||||
; -- Returns the number of available RAM banks according to the kernal (each bank is 8 Kb).
|
||||
; Note that the number of such banks can be 256 so a word is returned.
|
||||
; But just looking at the A register (the LSB of the result word) could suffice if you know that A=0 means 256 banks:
|
||||
@@ -508,15 +512,13 @@ asmsub numbanks() -> uword @AY {
|
||||
; Kernal's MEMTOP routine reports 0 in this case but that doesn't mean 'zero banks', instead it means 256 banks,
|
||||
; as there is no X16 without at least 1 page of banked RAM. So this routine returns 256 instead of 0.
|
||||
%asm {{
|
||||
phx
|
||||
sec
|
||||
jsr cbm.MEMTOP
|
||||
ldy #0
|
||||
cmp #0
|
||||
bne +
|
||||
iny
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -525,7 +527,8 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
||||
; note: inefficient when reading multiple sequential bytes!
|
||||
%asm {{
|
||||
pha
|
||||
lda #1
|
||||
lda cx16.VERA_CTRL
|
||||
ora #1
|
||||
sta cx16.VERA_CTRL
|
||||
pla
|
||||
and #1
|
||||
@@ -956,10 +959,6 @@ _irq_handler_init
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||
ldx #32
|
||||
cld
|
||||
rts
|
||||
|
||||
@@ -1079,19 +1078,17 @@ asmsub set_rasterline(uword line @AY) {
|
||||
void cx16.i2c_write_byte($42, $05, activity)
|
||||
}
|
||||
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
asmsub wait(uword jiffies @AY) clobbers(X) {
|
||||
; --- 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
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
|
||||
_loop lda P8ZP_SCRATCH_W1
|
||||
ora P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
plx
|
||||
rts
|
||||
|
||||
+ sei
|
||||
|
||||
@@ -42,11 +42,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A, X) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
%asm {{
|
||||
sty _ly+1
|
||||
phx
|
||||
pha
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
@@ -75,8 +74,7 @@ _ly ldy #1 ; modified
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
|
||||
set_vera_textmatrix_addresses:
|
||||
stz cx16.VERA_CTRL
|
||||
@@ -90,11 +88,10 @@ set_vera_textmatrix_addresses:
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(X, Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
@@ -116,16 +113,14 @@ _lx ldx #0 ; modified
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
asmsub clear_screencolors (ubyte color @ A) clobbers(X, Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
phx
|
||||
sta _la+1
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
@@ -150,8 +145,7 @@ _la lda #0 ; modified
|
||||
sta cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -192,11 +186,10 @@ sub iso_off() {
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_left() clobbers(A, Y) {
|
||||
asmsub scroll_left() clobbers(A, X, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
@@ -233,16 +226,14 @@ _lx ldx #0 ; modified
|
||||
|
||||
lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right() clobbers(A,Y) {
|
||||
asmsub scroll_right() clobbers(A,X,Y) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
@@ -287,16 +278,14 @@ _lx ldx #0 ; modified
|
||||
|
||||
lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up() clobbers(A, Y) {
|
||||
asmsub scroll_up() clobbers(A, X, Y) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
@@ -337,16 +326,14 @@ _nextline
|
||||
|
||||
+ lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down() clobbers(A, Y) {
|
||||
asmsub scroll_down() clobbers(A, X, Y) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
phx
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
dey
|
||||
@@ -393,12 +380,11 @@ _nextline
|
||||
|
||||
+ lda #0
|
||||
sta cx16.VERA_CTRL
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||
romsub $FFD2 = chrout(ubyte character @ 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
|
||||
@@ -418,10 +404,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
@@ -429,16 +414,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
@@ -454,16 +436,13 @@ _print_byte_digits
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
@@ -475,10 +454,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'$'
|
||||
@@ -487,16 +465,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,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 #'%'
|
||||
@@ -509,12 +484,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
@@ -526,7 +500,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -539,10 +513,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
@@ -550,17 +523,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ plx
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
plx
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
@@ -581,7 +551,7 @@ _allzero
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
@@ -704,13 +674,12 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: color handling is the same as on the C64: it only sets the foreground color and leaves the background color as is.
|
||||
; Use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors (is faster as well).
|
||||
%asm {{
|
||||
phx
|
||||
lda column
|
||||
lda col
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
@@ -722,7 +691,7 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
;clc
|
||||
adc #>VERA_TEXTMATRIX_ADDR
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda char
|
||||
lda character
|
||||
sta cx16.VERA_DATA0
|
||||
inc cx16.VERA_ADDR_L
|
||||
lda charcolor
|
||||
@@ -732,18 +701,16 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
and #$f0
|
||||
ora P8ZP_SCRATCH_B1
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
sub setcc2 (ubyte col, ubyte row, ubyte character, ubyte colors) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||
; use the high nybble in A to set the Bg color! Is a bit faster than setcc() too.
|
||||
%asm {{
|
||||
phx
|
||||
lda column
|
||||
lda col
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
@@ -755,25 +722,19 @@ sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
; clc
|
||||
adc #>VERA_TEXTMATRIX_ADDR
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda char
|
||||
lda character
|
||||
sta cx16.VERA_DATA0
|
||||
inc cx16.VERA_ADDR_L
|
||||
lda colors
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
phx
|
||||
tax
|
||||
clc
|
||||
jsr cbm.PLOT
|
||||
plx
|
||||
rts
|
||||
jmp cbm.PLOT
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
; Experimental Vera FX support.
|
||||
; Docs:
|
||||
; https://github.com/X16Community/x16-docs/blob/master/VERA%20FX%20Reference.md
|
||||
; https://docs.google.com/document/d/1q34uWOiM3Be2pnaHRVgSdHySI-qsiQWPTo_gfE54PTg/edit
|
||||
|
||||
verafx {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
sub available() -> bool {
|
||||
; returns true if Vera FX is available (Vera V0.3.1 or later), false if not.
|
||||
cx16.r1L = 0
|
||||
cx16.r0L = cx16.VERA_CTRL
|
||||
cx16.VERA_CTRL = $7e
|
||||
if cx16.VERA_DC_VER0 == $56 {
|
||||
; Vera version number is valid.
|
||||
; Vera fx is available on Vera version 0.3.1 and later,
|
||||
; so no need to even check VERA_DC_VER1, which contains 0 (or higher)
|
||||
cx16.r1L = mkword(cx16.VERA_DC_VER2, cx16.VERA_DC_VER3) >= $0301
|
||||
}
|
||||
cx16.VERA_CTRL = cx16.r0L
|
||||
return cx16.r1L
|
||||
}
|
||||
|
||||
sub clear(ubyte vbank, uword vaddr, ubyte data, uword amountof32bits) {
|
||||
; use cached 4-byte write to quickly clear a portion of the video memory to a given byte value
|
||||
; this routine is around 3 times faster as gfx2.clear_screen()
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = vbank | %00110000 ; 4-byte increment
|
||||
cx16.VERA_ADDR_M = msb(vaddr)
|
||||
cx16.VERA_ADDR_L = lsb(vaddr)
|
||||
cx16.VERA_CTRL = 6<<1 ; dcsel = 6, fill the 32 bits cache
|
||||
cx16.VERA_FX_CACHE_L = data
|
||||
cx16.VERA_FX_CACHE_M = data
|
||||
cx16.VERA_FX_CACHE_H = data
|
||||
cx16.VERA_FX_CACHE_U = data
|
||||
cx16.VERA_CTRL = 2<<1 ; dcsel = 2
|
||||
cx16.VERA_FX_MULT = 0
|
||||
cx16.VERA_FX_CTRL = %01000000 ; cache write enable
|
||||
|
||||
if (amountof32bits & %1111110000000011) == 0 {
|
||||
repeat lsb(amountof32bits >> 2)
|
||||
unroll 4 cx16.VERA_DATA0=0 ; write 4 bytes at a time, unrolled
|
||||
}
|
||||
else if (amountof32bits & %1111111000000001) == 0 {
|
||||
repeat lsb(amountof32bits >> 1)
|
||||
unroll 2 cx16.VERA_DATA0=0 ; write 4 bytes at a time, unrolled
|
||||
}
|
||||
else if (lsb(amountof32bits) & 3) == 0 {
|
||||
repeat amountof32bits >> 2
|
||||
unroll 4 cx16.VERA_DATA0=0 ; write 4 bytes at a time, unrolled
|
||||
}
|
||||
else if (lsb(amountof32bits) & 1) == 0 {
|
||||
repeat amountof32bits >> 1
|
||||
unroll 2 cx16.VERA_DATA0=0 ; write 4 bytes at a time, unrolled
|
||||
}
|
||||
else {
|
||||
repeat amountof32bits
|
||||
cx16.VERA_DATA0=0 ; write 4 bytes at a time
|
||||
}
|
||||
|
||||
cx16.VERA_FX_CTRL = 0 ; cache write disable
|
||||
cx16.VERA_CTRL = 0
|
||||
}
|
||||
|
||||
; unsigned multiplication just passes the values as signed to muls
|
||||
; if you do this yourself in your call to muls, it will save a few instructions.
|
||||
sub mult(uword value1, uword value2) -> uword {
|
||||
return muls(value1 as word, value2 as word) as uword
|
||||
}
|
||||
|
||||
asmsub muls(word value1 @R0, word value2 @R1) -> word @AY {
|
||||
%asm {{
|
||||
lda #(2 << 1)
|
||||
sta cx16.VERA_CTRL ; $9F25
|
||||
stz cx16.VERA_FX_CTRL ; $9F29 (mainly to reset Addr1 Mode to 0)
|
||||
lda #%00010000
|
||||
sta cx16.VERA_FX_MULT ; $9F2C
|
||||
lda #(6 << 1)
|
||||
sta cx16.VERA_CTRL ; $9F25
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
sta cx16.VERA_FX_CACHE_L ; $9F29
|
||||
sty cx16.VERA_FX_CACHE_M ; $9F2A
|
||||
lda cx16.r1
|
||||
ldy cx16.r1+1
|
||||
sta cx16.VERA_FX_CACHE_H ; $9F2B
|
||||
sty cx16.VERA_FX_CACHE_U ; $9F2C
|
||||
lda cx16.VERA_FX_ACCUM_RESET ; $9F29 (DCSEL=6)
|
||||
|
||||
; Set the ADDR0 pointer to $1f9bc and write our multiplication result there
|
||||
; (these are the 4 bytes just before the PSG registers start)
|
||||
lda #(2 << 1)
|
||||
sta cx16.VERA_CTRL
|
||||
lda #%01000000 ; Cache Write Enable
|
||||
sta cx16.VERA_FX_CTRL
|
||||
lda #$bc
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda #$f9
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #$01
|
||||
sta cx16.VERA_ADDR_H ; no increment
|
||||
stz cx16.VERA_DATA0 ; multiply and write out result
|
||||
lda #%00010001 ; $01 with Increment 1
|
||||
sta cx16.VERA_ADDR_H ; so we can read out the result
|
||||
lda cx16.VERA_DATA0
|
||||
ldy cx16.VERA_DATA0
|
||||
stz cx16.VERA_FX_CTRL ; Cache write disable
|
||||
stz cx16.VERA_CTRL ; reset DCSEL
|
||||
rts
|
||||
; we skip the upper 16 bits of the result:
|
||||
; lda cx16.VERA_DATA0
|
||||
; sta $0402
|
||||
; lda cx16.VERA_DATA0
|
||||
; sta $0403
|
||||
}}
|
||||
}
|
||||
|
||||
sub transparency(bool enable) {
|
||||
cx16.VERA_CTRL = 2<<1 ; dcsel = 2
|
||||
if enable
|
||||
cx16.VERA_FX_CTRL |= %10000000
|
||||
else
|
||||
cx16.VERA_FX_CTRL &= %01111111
|
||||
cx16.VERA_CTRL = 0
|
||||
}
|
||||
}
|
||||
@@ -7,22 +7,33 @@
|
||||
diskio {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
|
||||
ubyte drivenumber = 8
|
||||
|
||||
sub set_drive(ubyte number) {
|
||||
drivenumber = number
|
||||
}
|
||||
|
||||
sub reset_read_channel() {
|
||||
void cbm.CHKIN(READ_IO_CHANNEL)
|
||||
}
|
||||
|
||||
sub reset_write_channel() {
|
||||
cbm.CHKOUT(WRITE_IO_CHANNEL)
|
||||
}
|
||||
|
||||
sub directory() -> bool {
|
||||
; -- Prints the directory contents to the screen. Returns success.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
cbm.SETLFS(READ_IO_CHANNEL, 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
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -42,12 +53,12 @@ diskio {
|
||||
ubyte high = cbm.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.spc()
|
||||
ubyte @zp char
|
||||
ubyte @zp character
|
||||
repeat {
|
||||
char = cbm.CHRIN()
|
||||
if char==0
|
||||
character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
txt.chrout(char)
|
||||
txt.chrout(character)
|
||||
}
|
||||
txt.nl()
|
||||
void cbm.CHRIN() ; skip 2 bytes
|
||||
@@ -60,7 +71,7 @@ diskio {
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
|
||||
if status and status & $40 == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
@@ -76,12 +87,12 @@ io_error:
|
||||
; -- Returns pointer to disk name string or 0 if failure.
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
cbm.SETLFS(READ_IO_CHANNEL, 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
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -108,7 +119,7 @@ io_error:
|
||||
|
||||
io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if okay
|
||||
return &list_filename
|
||||
return 0
|
||||
@@ -126,7 +137,7 @@ io_error:
|
||||
|
||||
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.
|
||||
; Files in the buffer are separated 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.
|
||||
@@ -152,7 +163,7 @@ io_error:
|
||||
return files_found
|
||||
}
|
||||
|
||||
; ----- iterative file lister functions (uses io channel 12) -----
|
||||
; ----- iterative file lister functions (uses the read io channel) -----
|
||||
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
@@ -163,11 +174,11 @@ io_error:
|
||||
iteration_in_progress = true
|
||||
|
||||
cbm.SETNAM(1, "$")
|
||||
cbm.SETLFS(12, drivenumber, 0)
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, 0)
|
||||
void cbm.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void cbm.CHKIN(12) ; use #12 as input channel
|
||||
reset_read_channel()
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -192,7 +203,7 @@ io_error:
|
||||
return false
|
||||
|
||||
repeat {
|
||||
void cbm.CHKIN(12) ; use #12 as input channel again
|
||||
reset_read_channel() ; use the input io channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = cbm.CHRIN()
|
||||
@@ -211,12 +222,12 @@ io_error:
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = cbm.CHRIN()
|
||||
if char==0
|
||||
ubyte character = cbm.CHRIN()
|
||||
if character==0
|
||||
break
|
||||
if char=='\"'
|
||||
if character=='\"'
|
||||
break
|
||||
@(nameptr) = char
|
||||
@(nameptr) = character
|
||||
nameptr++
|
||||
}
|
||||
|
||||
@@ -253,13 +264,13 @@ close_end:
|
||||
; -- end an iterative file listing session (close channels).
|
||||
if iteration_in_progress {
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file loader functions (uses io channel 12) -----
|
||||
; ----- iterative file loader functions (uses the input io channel) -----
|
||||
|
||||
sub f_open(uword filenameptr) -> bool {
|
||||
; -- open a file for iterative reading with f_read
|
||||
@@ -267,18 +278,18 @@ close_end:
|
||||
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
|
||||
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL) ; note: has to be Channel,x,Channel 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
|
||||
reset_read_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
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
void cbm.CHKIN(12)
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -294,6 +305,7 @@ close_end:
|
||||
if not iteration_in_progress or not num_bytes
|
||||
return 0
|
||||
|
||||
reset_read_channel()
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
|
||||
%asm {{
|
||||
@@ -328,6 +340,7 @@ m_in_buffer sta $ffff
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
reset_read_channel()
|
||||
uword total_read = 0
|
||||
while not cbm.READST() {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
@@ -346,8 +359,7 @@ m_in_buffer sta $ffff
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx #12
|
||||
jsr cbm.CHKIN ; use channel 12 again for input
|
||||
jsr reset_read_channel
|
||||
ldy #0
|
||||
_loop jsr cbm.CHRIN
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
@@ -368,25 +380,23 @@ _end rts
|
||||
; -- end an iterative file loading session (close channels).
|
||||
if iteration_in_progress {
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(12)
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file writing functions (uses io channel 13) -----
|
||||
; ----- iterative file writing functions (uses write io channel) -----
|
||||
|
||||
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)
|
||||
cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, 1)
|
||||
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||
if_cc {
|
||||
cbm.CHKOUT(13) ; use #13 as output channel
|
||||
if_cc
|
||||
return not cbm.READST()
|
||||
}
|
||||
f_close_w()
|
||||
return false
|
||||
}
|
||||
@@ -394,7 +404,7 @@ _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 {
|
||||
cbm.CHKOUT(13) ; use #13 as output channel again
|
||||
reset_write_channel()
|
||||
repeat num_bytes {
|
||||
cbm.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
@@ -407,7 +417,7 @@ _end rts
|
||||
sub f_close_w() {
|
||||
; -- end an iterative file writing session (close channels).
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(13)
|
||||
cbm.CLOSE(WRITE_IO_CHANNEL)
|
||||
}
|
||||
|
||||
|
||||
@@ -444,24 +454,22 @@ io_error:
|
||||
goto done
|
||||
}
|
||||
|
||||
sub save(uword filenameptr, uword address, uword size) -> bool {
|
||||
sub save(uword filenameptr, uword start_address, uword savesize) -> bool {
|
||||
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||
cbm.SETLFS(1, drivenumber, 0)
|
||||
uword @shared end_address = address + size
|
||||
uword @shared end_address = start_address + savesize
|
||||
cx16.r0L = 0
|
||||
|
||||
%asm {{
|
||||
lda address
|
||||
lda start_address
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda address+1
|
||||
lda start_address+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<P8ZP_SCRATCH_W1
|
||||
ldx end_address
|
||||
ldy end_address+1
|
||||
jsr cbm.SAVE
|
||||
php
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
plp
|
||||
}}
|
||||
|
||||
@@ -488,7 +496,6 @@ io_error:
|
||||
secondary = 0
|
||||
cbm.SETLFS(1, drivenumber, secondary)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #0
|
||||
ldx address_override
|
||||
ldy address_override+1
|
||||
@@ -496,7 +503,7 @@ io_error:
|
||||
bcs +
|
||||
stx cx16.r1
|
||||
sty cx16.r1+1
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
+
|
||||
}}
|
||||
|
||||
cbm.CLRCHN()
|
||||
@@ -507,16 +514,16 @@ io_error:
|
||||
; 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 {
|
||||
sub load_raw(uword filenameptr, uword start_address) -> uword {
|
||||
; read the 2 header bytes separately to skip them
|
||||
if not f_open(filenameptr)
|
||||
return 0
|
||||
cx16.r1 = f_read(address, 2)
|
||||
cx16.r1 = f_read(start_address, 2)
|
||||
f_close()
|
||||
if cx16.r1!=2
|
||||
return 0
|
||||
address += 2
|
||||
return load(filenameptr, address)
|
||||
start_address += 2
|
||||
return load(filenameptr, start_address)
|
||||
}
|
||||
|
||||
sub delete(uword filenameptr) {
|
||||
|
||||
@@ -5,7 +5,6 @@ floats {
|
||||
sub print_f(float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%asm {{
|
||||
stx floats_store_reg
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM ; load float into fac1
|
||||
@@ -18,8 +17,7 @@ sub print_f(float value) {
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx floats_store_reg
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -44,10 +42,7 @@ sub sin(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr SIN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp SIN
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -56,9 +51,7 @@ sub cos(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr COS
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp COS
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -68,10 +61,7 @@ sub tan(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr TAN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp TAN
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -80,10 +70,7 @@ sub atan(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr ATN
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp ATN
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -92,10 +79,7 @@ sub ln(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp LOG
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -104,15 +88,12 @@ sub log2(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
jsr MOVEF
|
||||
lda #<FL_LOG2_const
|
||||
ldy #>FL_LOG2_const
|
||||
jsr MOVFM
|
||||
jsr FDIVT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp FDIVT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -122,12 +103,9 @@ sub rad(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_pi_div_180
|
||||
ldy #>_pi_div_180
|
||||
jsr FMULT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp FMULT
|
||||
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
||||
}}
|
||||
}
|
||||
@@ -138,11 +116,9 @@ sub deg(float angle) -> float {
|
||||
lda #<angle
|
||||
ldy #>angle
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_one_over_pi_div_180
|
||||
ldy #>_one_over_pi_div_180
|
||||
jsr FMULT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp FMULT
|
||||
rts
|
||||
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
||||
}}
|
||||
@@ -153,11 +129,8 @@ sub round(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FADDH
|
||||
jsr INT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp INT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -166,10 +139,7 @@ sub floor(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr INT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
jmp INT
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -179,7 +149,6 @@ sub ceil(float value) -> float {
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
@@ -192,8 +161,7 @@ sub ceil(float value) -> float {
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -202,14 +170,11 @@ sub rndseedf(float seed) {
|
||||
seed = -seed ; make sure fp seed is always negative
|
||||
|
||||
%asm {{
|
||||
stx floats_store_reg
|
||||
lda #<seed
|
||||
ldy #>seed
|
||||
jsr MOVFM ; load float into fac1
|
||||
lda #-1
|
||||
jsr floats.RND
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
jmp floats.RND
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
+233
-805
File diff suppressed because it is too large.
Load diff
@@ -98,6 +98,62 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub log2(ubyte value @A) -> ubyte @Y {
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda #$80
|
||||
ldy #7
|
||||
- bit P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
rts
|
||||
+ dey
|
||||
bne +
|
||||
rts
|
||||
+ lsr a
|
||||
bne -
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub log2w(uword value @AY) -> ubyte @Y {
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$8000
|
||||
sta cx16.r0
|
||||
lda #>$8000
|
||||
sta cx16.r0+1
|
||||
ldy #15
|
||||
- lda P8ZP_SCRATCH_W1
|
||||
and cx16.r0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
and cx16.r0+1
|
||||
ora P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
rts
|
||||
+ dey
|
||||
bne +
|
||||
rts
|
||||
+ lsr cx16.r0+1
|
||||
ror cx16.r0
|
||||
jmp -
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub mul16_last_upper() -> uword @AY {
|
||||
; This routine peeks into the internal 32 bits multiplication result buffer of the
|
||||
; 16*16 bits multiplication routine, to fetch the upper 16 bits of the last calculation.
|
||||
; Notes:
|
||||
; - to avoid interference it's best to fetch and store this value immediately after the multiplication expression.
|
||||
; for instance, simply printing a number may already result in new multiplication calls being performed
|
||||
; - not all multiplications in the source code result in an actual multiplication call:
|
||||
; some simpler multiplications will be optimized away into faster routines. These will not set the upper 16 bits at all!
|
||||
%asm {{
|
||||
lda multiply_words.result+2
|
||||
ldy multiply_words.result+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
|
||||
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
; Prog8 definitions for the Commodore PET
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
; see: https://www.pagetable.com/?p=926 , http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
|
||||
|
||||
cbm {
|
||||
; Commodore (CBM) common variables, vectors and kernal routines
|
||||
%option no_symbol_prefixing
|
||||
|
||||
&ubyte TIME_HI = $8d ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $8e ; .. mid byte
|
||||
&ubyte TIME_LO = $8f ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
&ubyte STATUS = $96 ; kernal status variable for I/O
|
||||
|
||||
&uword CINV = $0090 ; IRQ vector (in ram)
|
||||
&uword CBINV = $0092 ; BRK vector (in ram)
|
||||
&uword NMINV = $0094 ; NMI vector (in ram)
|
||||
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
; the default addresses for the character screen chars and colors
|
||||
const uword Screen = $8000 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||
|
||||
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte character @ A) ; output a character
|
||||
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; check the STOP key (and some others in A)
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
|
||||
asmsub STOP2() clobbers(X) -> 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 {{
|
||||
jsr cbm.STOP
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) {
|
||||
; PET stub to set the software clock
|
||||
%asm {{
|
||||
sty TIME_HI
|
||||
stx TIME_MID
|
||||
sta TIME_LO
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y {
|
||||
; PET stub to read the software clock (A=lo,X=mid,Y=high)
|
||||
%asm {{
|
||||
ldy TIME_HI
|
||||
ldx TIME_MID
|
||||
lda TIME_LO
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
lda TIME_LO
|
||||
ldy TIME_MID
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte target = 32 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 32=PET
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; Uppercase charset is activated.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
lda #142
|
||||
jsr cbm.CHROUT ; uppercase
|
||||
lda #147
|
||||
jsr cbm.CHROUT ; clear screen
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
rts ; no phase 2 steps on the PET
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
jmp (cbm.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitvsync() clobbers(A) {
|
||||
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||
; Note: on PET this simply waits until the next jiffy clock update, I don't know if a true vsync is possible there
|
||||
%asm {{
|
||||
lda #1
|
||||
ldy #0
|
||||
jmp wait
|
||||
}}
|
||||
}
|
||||
|
||||
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
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_B1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
|
||||
_loop lda P8ZP_SCRATCH_W1
|
||||
ora P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
ldx P8ZP_SCRATCH_B1
|
||||
rts
|
||||
|
||||
+ lda cbm.TIME_LO
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- lda cbm.TIME_LO
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W1+1
|
||||
+ dec P8ZP_SCRATCH_W1
|
||||
jmp _loop
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jmp prog8_lib.strcpy
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||
; note: only works for NON-OVERLAPPING memory regions!
|
||||
; note: can't be inlined because is called from asm as well
|
||||
%asm {{
|
||||
ldx cx16.r0
|
||||
stx P8ZP_SCRATCH_W1 ; source in ZP
|
||||
ldx cx16.r0+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
stx P8ZP_SCRATCH_W2 ; target in ZP
|
||||
ldx cx16.r1+1
|
||||
stx P8ZP_SCRATCH_W2+1
|
||||
cpy #0
|
||||
bne _longcopy
|
||||
|
||||
; copy <= 255 bytes
|
||||
tay
|
||||
bne _copyshort
|
||||
rts ; nothing to copy
|
||||
|
||||
_copyshort
|
||||
; decrease source and target pointers so we can simply index by Y
|
||||
lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W1+1
|
||||
+ dec P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
bne +
|
||||
dec P8ZP_SCRATCH_W2+1
|
||||
+ dec P8ZP_SCRATCH_W2
|
||||
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
|
||||
_longcopy
|
||||
sta P8ZP_SCRATCH_B1 ; lsb(count) = remainder in last page
|
||||
tya
|
||||
tax ; x = num pages (1+)
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
bne -
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
inc P8ZP_SCRATCH_W2+1
|
||||
dex
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
bne _copyshort
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memset(uword mem @R0, uword numbytes @R1, ubyte value @A) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
ldy cx16.r0
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy cx16.r0+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
ldy cx16.r1+1
|
||||
jmp prog8_lib.memset
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memsetw(uword mem @R0, uword numwords @R1, uword value @AY) clobbers(A,X,Y) {
|
||||
%asm {{
|
||||
ldx cx16.r0
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx cx16.r0+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
ldx cx16.r1
|
||||
stx P8ZP_SCRATCH_W2
|
||||
ldx cx16.r1+1
|
||||
stx P8ZP_SCRATCH_W2+1
|
||||
jmp prog8_lib.memsetw
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub read_flags() -> ubyte @A {
|
||||
%asm {{
|
||||
php
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_carry() {
|
||||
%asm {{
|
||||
clc
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_carry() {
|
||||
%asm {{
|
||||
sec
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clear_irqd() {
|
||||
%asm {{
|
||||
cli
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub set_irqd() {
|
||||
%asm {{
|
||||
sei
|
||||
}}
|
||||
}
|
||||
|
||||
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 {{
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
txs
|
||||
rts ; return to original caller
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub progend() -> uword @AY {
|
||||
%asm {{
|
||||
lda #<prog8_program_end
|
||||
ldy #>prog8_program_end
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the PET as well but their location in memory is different
|
||||
; (because there's no room for them in the zeropage)
|
||||
; we select the top page of RAM (assume 32Kb)
|
||||
&uword r0 = $7fe0
|
||||
&uword r1 = $7fe2
|
||||
&uword r2 = $7fe4
|
||||
&uword r3 = $7fe6
|
||||
&uword r4 = $7fe8
|
||||
&uword r5 = $7fea
|
||||
&uword r6 = $7fec
|
||||
&uword r7 = $7fee
|
||||
&uword r8 = $7ff0
|
||||
&uword r9 = $7ff2
|
||||
&uword r10 = $7ff4
|
||||
&uword r11 = $7ff6
|
||||
&uword r12 = $7ff8
|
||||
&uword r13 = $7ffa
|
||||
&uword r14 = $7ffc
|
||||
&uword r15 = $7ffe
|
||||
|
||||
&word r0s = $7fe0
|
||||
&word r1s = $7fe2
|
||||
&word r2s = $7fe4
|
||||
&word r3s = $7fe6
|
||||
&word r4s = $7fe8
|
||||
&word r5s = $7fea
|
||||
&word r6s = $7fec
|
||||
&word r7s = $7fee
|
||||
&word r8s = $7ff0
|
||||
&word r9s = $7ff2
|
||||
&word r10s = $7ff4
|
||||
&word r11s = $7ff6
|
||||
&word r12s = $7ff8
|
||||
&word r13s = $7ffa
|
||||
&word r14s = $7ffc
|
||||
&word r15s = $7ffe
|
||||
|
||||
&ubyte r0L = $7fe0
|
||||
&ubyte r1L = $7fe2
|
||||
&ubyte r2L = $7fe4
|
||||
&ubyte r3L = $7fe6
|
||||
&ubyte r4L = $7fe8
|
||||
&ubyte r5L = $7fea
|
||||
&ubyte r6L = $7fec
|
||||
&ubyte r7L = $7fee
|
||||
&ubyte r8L = $7ff0
|
||||
&ubyte r9L = $7ff2
|
||||
&ubyte r10L = $7ff4
|
||||
&ubyte r11L = $7ff6
|
||||
&ubyte r12L = $7ff8
|
||||
&ubyte r13L = $7ffa
|
||||
&ubyte r14L = $7ffc
|
||||
&ubyte r15L = $7ffe
|
||||
|
||||
&ubyte r0H = $7fe1
|
||||
&ubyte r1H = $7fe3
|
||||
&ubyte r2H = $7fe5
|
||||
&ubyte r3H = $7fe7
|
||||
&ubyte r4H = $7fe9
|
||||
&ubyte r5H = $7feb
|
||||
&ubyte r6H = $7fed
|
||||
&ubyte r7H = $7fef
|
||||
&ubyte r8H = $7ff1
|
||||
&ubyte r9H = $7ff3
|
||||
&ubyte r10H = $7ff5
|
||||
&ubyte r11H = $7ff7
|
||||
&ubyte r12H = $7ff9
|
||||
&ubyte r13H = $7ffb
|
||||
&ubyte r14H = $7ffd
|
||||
&ubyte r15H = $7fff
|
||||
|
||||
&byte r0sL = $7fe0
|
||||
&byte r1sL = $7fe2
|
||||
&byte r2sL = $7fe4
|
||||
&byte r3sL = $7fe6
|
||||
&byte r4sL = $7fe8
|
||||
&byte r5sL = $7fea
|
||||
&byte r6sL = $7fec
|
||||
&byte r7sL = $7fee
|
||||
&byte r8sL = $7ff0
|
||||
&byte r9sL = $7ff2
|
||||
&byte r10sL = $7ff4
|
||||
&byte r11sL = $7ff6
|
||||
&byte r12sL = $7ff8
|
||||
&byte r13sL = $7ffa
|
||||
&byte r14sL = $7ffc
|
||||
&byte r15sL = $7ffe
|
||||
|
||||
&byte r0sH = $7fe1
|
||||
&byte r1sH = $7fe3
|
||||
&byte r2sH = $7fe5
|
||||
&byte r3sH = $7fe7
|
||||
&byte r4sH = $7fe9
|
||||
&byte r5sH = $7feb
|
||||
&byte r6sH = $7fed
|
||||
&byte r7sH = $7fef
|
||||
&byte r8sH = $7ff1
|
||||
&byte r9sH = $7ff3
|
||||
&byte r10sH = $7ff5
|
||||
&byte r11sH = $7ff7
|
||||
&byte r12sH = $7ff9
|
||||
&byte r13sH = $7ffb
|
||||
&byte r14sH = $7ffd
|
||||
&byte r15sH = $7fff
|
||||
|
||||
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
- lda cx16.r0,y
|
||||
sta _cx16_vreg_storage,y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_virtual_registers() clobbers(A,Y) {
|
||||
%asm {{
|
||||
ldy #31
|
||||
- lda save_virtual_registers._cx16_vreg_storage,y
|
||||
sta cx16.r0,y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore PET
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
|
||||
txt {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
sub home() {
|
||||
txt.chrout(19)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
txt.chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
txt.chrout(' ')
|
||||
}
|
||||
|
||||
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character. color is ignored on PET
|
||||
%asm {{
|
||||
jmp clear_screenchars
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte character @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #250
|
||||
- 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
|
||||
}}
|
||||
}
|
||||
|
||||
sub clear_screencolors (ubyte color) {
|
||||
; --- dummy function on PET
|
||||
}
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
; --- dummy function on PET
|
||||
}
|
||||
|
||||
|
||||
sub lowercase() {
|
||||
txt.chrout(14)
|
||||
}
|
||||
|
||||
sub uppercase() {
|
||||
txt.chrout(142)
|
||||
}
|
||||
|
||||
asmsub scroll_left () clobbers(A, X, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
|
||||
%asm {{
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda cbm.Screen + 40*row + 1,x
|
||||
sta cbm.Screen + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right () clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda cbm.Screen + 40*row + 0,x
|
||||
sta cbm.Screen + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up () clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down () clobbers(A,X) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda cbm.Screen + 40*row,x
|
||||
sta cbm.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
romsub $FFD2 = chrout(ubyte character @ 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 cbm.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
txa
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
jsr cbm.CHROUT
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr cbm.CHROUT
|
||||
_ones txa
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr cbm.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr cbm.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr cbm.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr cbm.CHROUT
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubbin
|
||||
pla
|
||||
clc
|
||||
jmp print_ubbin
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubhex
|
||||
pla
|
||||
clc
|
||||
jmp print_ubhex
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
iny
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr cbm.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp cbm.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr cbm.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
pla
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp print_uw
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr cbm.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
iny
|
||||
bne -
|
||||
+ lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||
rts
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
|
||||
; ---- sets the character in the screen matrix at the given position
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the character in the screen matrix at the given location
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setclr (ubyte col, ubyte row, ubyte color) {
|
||||
; --- dummy function on PET
|
||||
}
|
||||
|
||||
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char at the given position on the screen. charcolor is ignored on PET
|
||||
%asm {{
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc col
|
||||
sta _charmod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
||||
%asm {{
|
||||
jsr home
|
||||
cpy #0
|
||||
beq +
|
||||
- lda #17
|
||||
jsr chrout
|
||||
dey
|
||||
bne -
|
||||
+ cpx #0
|
||||
beq +
|
||||
- lda #29
|
||||
jsr chrout
|
||||
dex
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||
; -- returns the text screen width (number of columns)
|
||||
%asm {{
|
||||
lda $d5
|
||||
clc
|
||||
adc #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||
; -- returns the text screen height (number of rows)
|
||||
%asm {{
|
||||
lda #25
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,6 @@
|
||||
; ---- builtin functions
|
||||
|
||||
|
||||
func_any_b_stack .proc
|
||||
jsr func_any_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_all_b_stack .proc
|
||||
jsr func_all_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_any_b_into_A .proc
|
||||
; -- any(array), array in P8ZP_SCRATCH_W1, num bytes in A
|
||||
sta _cmp_mod+1 ; self-modifying code
|
||||
@@ -49,14 +35,6 @@ func_any_w_into_A .proc
|
||||
jmp func_any_b_into_A
|
||||
.pend
|
||||
|
||||
func_any_w_stack .proc
|
||||
asl a
|
||||
jsr func_any_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_all_w_into_A .proc
|
||||
; -- all(warray), array in P8ZP_SCRATCH_W1, num bytes in A
|
||||
asl a ; times 2 because of word
|
||||
@@ -77,22 +55,6 @@ _cmp_mod cpy #255 ; modified
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_all_w_stack .proc
|
||||
jsr func_all_w_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_b_stack .proc
|
||||
; -- push abs(A) on stack (as unsigned word)
|
||||
jsr abs_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
stz P8ESTACK_HI,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_b_into_A .proc
|
||||
; -- A = abs(A)
|
||||
cmp #0
|
||||
@@ -104,16 +66,6 @@ abs_b_into_A .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_w_stack .proc
|
||||
; -- push abs(AY) on stack (as word)
|
||||
jsr abs_w_into_AY
|
||||
sta P8ESTACK_LO,x
|
||||
tya
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
abs_w_into_AY .proc
|
||||
; -- AY = abs(AY)
|
||||
cpy #0
|
||||
@@ -142,13 +94,6 @@ _neg lda #-1
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_b_stack .proc
|
||||
jsr func_sign_b_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_ub_into_A .proc
|
||||
cmp #0
|
||||
bne _pos
|
||||
@@ -157,13 +102,6 @@ _pos lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_ub_stack .proc
|
||||
jsr func_sign_ub_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_uw_into_A .proc
|
||||
cpy #0
|
||||
beq _possibly_zero
|
||||
@@ -174,13 +112,6 @@ _possibly_zero cmp #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_uw_stack .proc
|
||||
jsr func_sign_uw_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sign_w_into_A .proc
|
||||
cpy #0
|
||||
beq _possibly_zero
|
||||
@@ -195,52 +126,43 @@ _possibly_zero cmp #0
|
||||
.pend
|
||||
|
||||
|
||||
func_sign_w_stack .proc
|
||||
jsr func_sign_w_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sqrt16_stack .proc
|
||||
jsr func_sqrt16_into_A
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_sqrt16_into_A .proc
|
||||
; integer square root from http://6502org.wikidot.com/software-math-sqrt
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
pha
|
||||
lda #0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
ldx #8
|
||||
- sec
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sbc #$40
|
||||
tay
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sbc P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
+ rol P8ZP_SCRATCH_B1
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl P8ZP_SCRATCH_W1
|
||||
rol P8ZP_SCRATCH_W1+1
|
||||
rol P8ZP_SCRATCH_REG
|
||||
dex
|
||||
bne -
|
||||
pla
|
||||
tax
|
||||
lda P8ZP_SCRATCH_B1
|
||||
rts
|
||||
; integer square root
|
||||
; http://6502org.wikidot.com/software-math-sqrt
|
||||
; https://github.com/TobyLobster/sqrt_test/blob/main/sqrt/sqrt7.a
|
||||
; Tweaked by TobyLobster and 0xC0DE to be smaller and faster
|
||||
_numl = P8ZP_SCRATCH_W1
|
||||
_numh = P8ZP_SCRATCH_W1+1
|
||||
_loop_counter = P8ZP_SCRATCH_REG
|
||||
_root = P8ZP_SCRATCH_B1
|
||||
sta _numl
|
||||
sty _numh
|
||||
ldx #$ff
|
||||
stx _loop_counter
|
||||
inx
|
||||
stx _root
|
||||
sec
|
||||
_loop lda _numh
|
||||
sbc #$40
|
||||
tay
|
||||
txa
|
||||
sbc _root
|
||||
bcc +
|
||||
sty _numh
|
||||
bcs ++
|
||||
+ txa
|
||||
+ rol _root
|
||||
asl _numl
|
||||
rol _numh
|
||||
rol a
|
||||
asl _numl
|
||||
rol _numh
|
||||
rol a
|
||||
tax
|
||||
lsr _loop_counter
|
||||
bne _loop
|
||||
lda _root
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
|
||||
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
||||
|
||||
read_byte_from_address_on_stack .proc
|
||||
; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged)
|
||||
lda P8ESTACK_LO+1,x
|
||||
ldy P8ESTACK_HI+1,x
|
||||
|
||||
read_byte_from_address_in_AY_into_A .proc
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
@@ -16,318 +14,16 @@ read_byte_from_address_on_stack .proc
|
||||
.pend
|
||||
|
||||
|
||||
write_byte_to_address_on_stack .proc
|
||||
; -- write the byte in A to the memory address on the top of the stack (stack remains unchanged)
|
||||
ldy P8ESTACK_LO+1,x
|
||||
sty P8ZP_SCRATCH_W2
|
||||
ldy P8ESTACK_HI+1,x
|
||||
write_byte_X_to_address_in_AY .proc
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
txa
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
neg_b .proc
|
||||
lda #0
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
neg_w .proc
|
||||
sec
|
||||
lda #0
|
||||
sbc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda #0
|
||||
sbc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
inv_word .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
eor #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
eor #255
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitand_b .proc
|
||||
; -- bitwise and (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
and P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitor_b .proc
|
||||
; -- bitwise or (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
ora P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitxor_b .proc
|
||||
; -- bitwise xor (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
eor P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitand_w .proc
|
||||
; -- bitwise and (of 2 words)
|
||||
lda P8ESTACK_LO+2,x
|
||||
and P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
and P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitor_w .proc
|
||||
; -- bitwise or (of 2 words)
|
||||
lda P8ESTACK_LO+2,x
|
||||
ora P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitxor_w .proc
|
||||
; -- bitwise xor (of 2 bytes)
|
||||
lda P8ESTACK_LO+2,x
|
||||
eor P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
eor P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
add_w .proc
|
||||
; -- push word+word / uword+uword
|
||||
inx
|
||||
clc
|
||||
lda P8ESTACK_LO,x
|
||||
adc P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI,x
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
sub_w .proc
|
||||
; -- push word-word
|
||||
inx
|
||||
sec
|
||||
lda P8ESTACK_LO+1,x
|
||||
sbc P8ESTACK_LO,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
sbc P8ESTACK_HI,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte .proc
|
||||
; -- b*b->b (signed and unsigned)
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_LO+1,x
|
||||
jsr math.multiply_bytes
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word .proc
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO+1,x
|
||||
ldy P8ESTACK_HI+1,x
|
||||
jsr math.multiply_words
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
idiv_b .proc
|
||||
; signed division: use unsigned division and fix sign of result afterwards
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
eor P8ESTACK_LO+1,x
|
||||
php ; save sign of result
|
||||
lda P8ESTACK_LO,x
|
||||
bpl +
|
||||
eor #$ff
|
||||
sec
|
||||
adc #0 ; make num1 positive
|
||||
+ tay
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
bpl +
|
||||
eor #$ff
|
||||
sec
|
||||
adc #0 ; make num2 positive
|
||||
+ jsr math.divmod_ub_asm
|
||||
sta _remainder
|
||||
tya
|
||||
plp ; get sign of result
|
||||
bpl +
|
||||
eor #$ff
|
||||
sec
|
||||
adc #0 ; negate result
|
||||
+ sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_remainder .byte 0
|
||||
.pend
|
||||
|
||||
idiv_ub .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
lda P8ESTACK_LO+1,x
|
||||
jsr math.divmod_ub_asm
|
||||
tya
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
idiv_w .proc
|
||||
; signed division: use unsigned division and fix sign of result afterwards
|
||||
lda P8ESTACK_HI+2,x
|
||||
eor P8ESTACK_HI+1,x
|
||||
php ; save sign of result
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl +
|
||||
jsr neg_w ; make value positive
|
||||
+ inx
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl +
|
||||
jsr neg_w ; make value positive
|
||||
+ lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jsr math.divmod_uw_asm
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
sta P8ESTACK_HI+1,x
|
||||
plp
|
||||
bpl +
|
||||
jmp neg_w ; negate result
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
idiv_uw .proc
|
||||
inx
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jsr math.divmod_uw_asm
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
remainder_ub .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x ; right operand
|
||||
lda P8ESTACK_LO+1,x ; left operand
|
||||
jsr math.divmod_ub_asm
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
remainder_uw .proc
|
||||
inx
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jsr math.divmod_uw_asm
|
||||
lda P8ZP_SCRATCH_W2
|
||||
sta P8ESTACK_LO+1,x
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
equal_w .proc
|
||||
; -- are the two words on the stack identical?
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bne equal_b._equal_b_false
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bne equal_b._equal_b_false
|
||||
beq equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
notequal_b .proc
|
||||
; -- are the two bytes on the stack different?
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
beq equal_b._equal_b_false
|
||||
bne equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
notequal_w .proc
|
||||
; -- are the two words on the stack different?
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
beq notequal_b
|
||||
bne equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
less_ub .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcc equal_b._equal_b_true
|
||||
bcs equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
less_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_less_uw .proc
|
||||
; AY < P8ZP_SCRATCH_W2?
|
||||
cpy P8ZP_SCRATCH_W2+1
|
||||
@@ -341,17 +37,6 @@ _true lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_uw .proc
|
||||
lda P8ESTACK_HI+2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bcc equal_b._equal_b_true
|
||||
bne equal_b._equal_b_false
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcc equal_b._equal_b_true
|
||||
bcs equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_less_w .proc
|
||||
; -- AY < P8ZP_SCRATCH_W2?
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
@@ -366,48 +51,6 @@ _true lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_w .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
sbc P8ESTACK_HI+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
equal_b .proc
|
||||
; -- are the two bytes on the stack identical?
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bne _equal_b_false
|
||||
_equal_b_true lda #1
|
||||
_equal_b_store inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_equal_b_false lda #0
|
||||
beq _equal_b_store
|
||||
.pend
|
||||
|
||||
lesseq_ub .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
lesseq_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_lesseq_uw .proc
|
||||
; AY <= P8ZP_SCRATCH_W2?
|
||||
cpy P8ZP_SCRATCH_W2+1
|
||||
@@ -424,17 +67,6 @@ _true lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
lesseq_uw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bcc equal_b._equal_b_false
|
||||
bne equal_b._equal_b_true
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
reg_lesseq_w .proc
|
||||
; -- P8ZP_SCRATCH_W2 <= AY ? (note: order different from other routines)
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
@@ -449,224 +81,6 @@ reg_lesseq_w .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
lesseq_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
sbc P8ESTACK_HI+2,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl equal_b._equal_b_true
|
||||
bmi equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_ub .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
beq equal_b._equal_b_false
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl equal_b._equal_b_true
|
||||
bmi equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_uw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bcc equal_b._equal_b_true
|
||||
bne equal_b._equal_b_false
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bcc equal_b._equal_b_true
|
||||
bcs equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greater_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
sbc P8ESTACK_HI+2,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_true
|
||||
bpl equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_ub .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_b .proc
|
||||
; see http://www.6502.org/tutorials/compare_beyond.html
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc P8ESTACK_LO+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl equal_b._equal_b_true
|
||||
bmi equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_uw .proc
|
||||
lda P8ESTACK_HI+2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bcc equal_b._equal_b_false
|
||||
bne equal_b._equal_b_true
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
greatereq_w .proc
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
sbc P8ESTACK_HI+1,x
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi equal_b._equal_b_false
|
||||
bpl equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
|
||||
shiftleft_b .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne +
|
||||
rts
|
||||
+ lda P8ESTACK_LO+1,x
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
shiftright_b .proc
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne +
|
||||
rts
|
||||
+ lda P8ESTACK_LO+1,x
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
equalzero_b .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
beq _true
|
||||
bne _false
|
||||
_true lda #1
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_false lda #0
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
equalzero_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
beq equalzero_b._true
|
||||
bne equalzero_b._false
|
||||
.pend
|
||||
|
||||
notequalzero_b .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
beq equalzero_b._false
|
||||
bne equalzero_b._true
|
||||
.pend
|
||||
|
||||
notequalzero_w .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
beq equalzero_b._false
|
||||
bne equalzero_b._true
|
||||
.pend
|
||||
|
||||
lesszero_b .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bmi equalzero_b._true
|
||||
jmp equalzero_b._false
|
||||
.pend
|
||||
|
||||
lesszero_w .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi equalzero_b._true
|
||||
jmp equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_ub .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bne equalzero_b._true
|
||||
beq equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_sb .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
beq equalzero_b._false
|
||||
bpl equalzero_b._true
|
||||
bmi equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_uw .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora P8ESTACK_HI+1,x
|
||||
bne equalzero_b._true
|
||||
beq equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterzero_sw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi equalzero_b._false
|
||||
ora P8ESTACK_LO+1,x
|
||||
beq equalzero_b._false
|
||||
bne equalzero_b._true
|
||||
.pend
|
||||
|
||||
lessequalzero_sb .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bmi equalzero_b._true
|
||||
beq equalzero_b._true
|
||||
bne equalzero_b._false
|
||||
.pend
|
||||
|
||||
lessequalzero_sw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi equalzero_b._true
|
||||
ora P8ESTACK_LO+1,x
|
||||
beq equalzero_b._true
|
||||
bne equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterequalzero_sb .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
bpl equalzero_b._true
|
||||
bmi equalzero_b._false
|
||||
.pend
|
||||
|
||||
greaterequalzero_sw .proc
|
||||
lda P8ESTACK_HI+1,x
|
||||
bpl equalzero_b._true
|
||||
bmi equalzero_b._false
|
||||
.pend
|
||||
|
||||
memcopy16_up .proc
|
||||
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
|
||||
@@ -996,21 +410,6 @@ _arg_s1 .word 0
|
||||
_arg_s2 .word 0
|
||||
.pend
|
||||
|
||||
strcmp_stack .proc
|
||||
; -- compare strings, both on stack.
|
||||
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
jmp strcmp_mem
|
||||
.pend
|
||||
|
||||
|
||||
strcmp_mem .proc
|
||||
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
|
||||
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
|
||||
@@ -1041,16 +440,6 @@ _return_minusone
|
||||
.pend
|
||||
|
||||
|
||||
sign_extend_stack_byte .proc
|
||||
; -- sign extend the (signed) byte on the stack to full 16 bits
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
strlen .proc
|
||||
; -- returns the number of bytes in the string in AY, in Y.
|
||||
sta P8ZP_SCRATCH_W1
|
||||
|
||||
@@ -225,7 +225,7 @@ _done rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub lowerchar(ubyte char @A) -> ubyte @A {
|
||||
asmsub lowerchar(ubyte character @A) -> ubyte @A {
|
||||
%asm {{
|
||||
and #$7f
|
||||
cmp #97
|
||||
@@ -237,7 +237,7 @@ _done rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub upperchar(ubyte char @A) -> ubyte @A {
|
||||
asmsub upperchar(ubyte character @A) -> ubyte @A {
|
||||
%asm {{
|
||||
cmp #65
|
||||
bcc +
|
||||
@@ -280,11 +280,10 @@ _done rts
|
||||
;
|
||||
; see http://6502.org/source/strings/patmatch.htm
|
||||
|
||||
str = P8ZP_SCRATCH_W1
|
||||
strptr = P8ZP_SCRATCH_W1
|
||||
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta str
|
||||
sty str+1
|
||||
sta strptr
|
||||
sty strptr+1
|
||||
lda cx16.r0
|
||||
sta modify_pattern1+1
|
||||
sta modify_pattern2+1
|
||||
@@ -294,7 +293,6 @@ str = P8ZP_SCRATCH_W1
|
||||
jsr _match
|
||||
lda #0
|
||||
rol a
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
|
||||
@@ -308,9 +306,9 @@ next lda $ffff,x ; look at next pattern character MODIFIED
|
||||
iny ; no, let's look at the string
|
||||
cmp #'?' ; is the pattern caracter a ques?
|
||||
bne reg ; no, it's a regular character
|
||||
lda (str),y ; yes, so it will match anything
|
||||
lda (strptr),y ; yes, so it will match anything
|
||||
beq fail ; except the end of string
|
||||
reg cmp (str),y ; are both characters the same?
|
||||
reg cmp (strptr),y ; are both characters the same?
|
||||
bne fail ; no, so no match
|
||||
inx ; yes, keep checking
|
||||
cmp #0 ; are we at end of string?
|
||||
@@ -332,7 +330,7 @@ stloop txa ; we first try to match with * = ""
|
||||
tax
|
||||
bcs found ; we found a match, return with c=1
|
||||
iny ; no match yet, try to grow * string
|
||||
lda (str),y ; are we at the end of string?
|
||||
lda (strptr),y ; are we at the end of string?
|
||||
bne stloop ; not yet, add a character
|
||||
fail clc ; yes, no match found, return with c=0
|
||||
rts
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; utility debug code to print the X (evalstack) and SP (cpu stack) registers.
|
||||
; utility debug code to print the SP (cpu stack pointer) register.
|
||||
|
||||
%import textio
|
||||
|
||||
@@ -7,7 +7,6 @@ test_stack {
|
||||
|
||||
asmsub test() {
|
||||
%asm {{
|
||||
stx _saveX
|
||||
lda #13
|
||||
jsr txt.chrout
|
||||
lda #'-'
|
||||
@@ -17,14 +16,6 @@ test_stack {
|
||||
bne -
|
||||
lda #13
|
||||
jsr txt.chrout
|
||||
lda #'x'
|
||||
jsr txt.chrout
|
||||
lda #'='
|
||||
jsr txt.chrout
|
||||
lda _saveX
|
||||
jsr txt.print_ub
|
||||
lda #' '
|
||||
jsr txt.chrout
|
||||
lda #'s'
|
||||
jsr txt.chrout
|
||||
lda #'p'
|
||||
@@ -43,9 +34,7 @@ test_stack {
|
||||
bne -
|
||||
lda #13
|
||||
jsr txt.chrout
|
||||
ldx _saveX
|
||||
rts
|
||||
_saveX .byte 0
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,15 @@ sub print_f(float value) {
|
||||
}}
|
||||
}
|
||||
|
||||
sub parse_f(str value) -> float {
|
||||
; -- parse a string value of a number to float
|
||||
%ir {{
|
||||
loadm.w r65535,floats.parse_f.value
|
||||
syscall 45 (r65535.w): fr0.f
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub pow(float value, float power) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.pow.value
|
||||
|
||||
@@ -183,60 +183,98 @@ math {
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
|
||||
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
|
||||
; This adjusts the atan() result so that the direction N is centered on the angle=N instead of having it as a boundary
|
||||
ubyte angle = atan2(x1, y1, x2, y2) - 256/48
|
||||
return 23-lsb(mkword(angle,0) / 2730)
|
||||
}
|
||||
|
||||
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
|
||||
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
|
||||
; shift the points into the positive quadrant
|
||||
ubyte px1
|
||||
ubyte py1
|
||||
ubyte px2
|
||||
ubyte py2
|
||||
if x1<0 or x2<0 {
|
||||
px1 = x1 as ubyte + 128
|
||||
px2 = x2 as ubyte + 128
|
||||
} else {
|
||||
px1 = x1 as ubyte
|
||||
px2 = x2 as ubyte
|
||||
}
|
||||
if y1<0 or y2<0 {
|
||||
py1 = y1 as ubyte + 128
|
||||
py2 = y2 as ubyte + 128
|
||||
} else {
|
||||
py1 = y1 as ubyte
|
||||
py2 = y2 as ubyte
|
||||
sub log2(ubyte value) -> ubyte {
|
||||
ubyte result = 7
|
||||
ubyte compare = $80
|
||||
repeat {
|
||||
if value&compare
|
||||
return result
|
||||
result--
|
||||
if_z
|
||||
return 0
|
||||
compare >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
return direction(px1, py1, px2, py2)
|
||||
}
|
||||
sub log2w(uword value) -> ubyte {
|
||||
ubyte result = 15
|
||||
uword compare = $8000
|
||||
repeat {
|
||||
if value&compare
|
||||
return result
|
||||
result--
|
||||
if_z
|
||||
return 0
|
||||
compare >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
sub direction_qd(ubyte quadrant, ubyte xdelta, ubyte ydelta) -> ubyte {
|
||||
; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23.
|
||||
when quadrant {
|
||||
3 -> return direction(0, 0, xdelta, ydelta)
|
||||
2 -> return direction(xdelta, 0, 0, ydelta)
|
||||
1 -> return direction(0, ydelta, xdelta, 0)
|
||||
else -> return direction(xdelta, ydelta, 0, 0)
|
||||
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
|
||||
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
|
||||
; This adjusts the atan() result so that the direction N is centered on the angle=N instead of having it as a boundary
|
||||
ubyte angle = atan2(x1, y1, x2, y2) - 256/48
|
||||
return 23-lsb(mkword(angle,0) / 2730)
|
||||
}
|
||||
|
||||
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
|
||||
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
|
||||
; shift the points into the positive quadrant
|
||||
ubyte px1
|
||||
ubyte py1
|
||||
ubyte px2
|
||||
ubyte py2
|
||||
if x1<0 or x2<0 {
|
||||
px1 = x1 as ubyte + 128
|
||||
px2 = x2 as ubyte + 128
|
||||
} else {
|
||||
px1 = x1 as ubyte
|
||||
px2 = x2 as ubyte
|
||||
}
|
||||
if y1<0 or y2<0 {
|
||||
py1 = y1 as ubyte + 128
|
||||
py2 = y2 as ubyte + 128
|
||||
} else {
|
||||
py1 = y1 as ubyte
|
||||
py2 = y2 as ubyte
|
||||
}
|
||||
|
||||
return direction(px1, py1, px2, py2)
|
||||
}
|
||||
|
||||
sub direction_qd(ubyte quadrant, ubyte xdelta, ubyte ydelta) -> ubyte {
|
||||
; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23.
|
||||
when quadrant {
|
||||
3 -> return direction(0, 0, xdelta, ydelta)
|
||||
2 -> return direction(xdelta, 0, 0, ydelta)
|
||||
1 -> return direction(0, ydelta, xdelta, 0)
|
||||
else -> return direction(xdelta, ydelta, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
sub atan2(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
|
||||
;; Calculate the angle, in a 256-degree circle, between two points into A.
|
||||
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
|
||||
%ir {{
|
||||
loadm.b r65532,math.atan2.x1
|
||||
loadm.b r65533,math.atan2.y1
|
||||
loadm.b r65534,math.atan2.x2
|
||||
loadm.b r65535,math.atan2.y2
|
||||
syscall 44 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub mul16_last_upper() -> uword {
|
||||
; This routine peeks into the internal 32 bits multiplication result buffer of the
|
||||
; 16*16 bits multiplication routine, to fetch the upper 16 bits of the last calculation.
|
||||
; Notes:
|
||||
; - to avoid interference it's best to fetch and store this value immediately after the multiplication expression.
|
||||
; for instance, simply printing a number may already result in new multiplication calls being performed
|
||||
; - not all multiplications in the source code result in an actual multiplication call:
|
||||
; some simpler multiplications will be optimized away into faster routines. These will not set the upper 16 bits at all!
|
||||
%ir {{
|
||||
syscall 46 (): r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
sub atan2(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
|
||||
;; Calculate the angle, in a 256-degree circle, between two points into A.
|
||||
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
|
||||
%ir {{
|
||||
loadm.b r65532,math.atan2.x1
|
||||
loadm.b r65533,math.atan2.y1
|
||||
loadm.b r65534,math.atan2.x2
|
||||
loadm.b r65535,math.atan2.y2
|
||||
syscall 44 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package prog8
|
||||
import kotlinx.cli.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.code.core.CbmPrgLauncherType
|
||||
import prog8.code.core.toHex
|
||||
import prog8.code.target.*
|
||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||
import prog8.compiler.CompilationResult
|
||||
@@ -39,22 +38,22 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
|
||||
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
|
||||
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
|
||||
val evalStackAddrString by cli.option(ArgType.String, fullName = "esa", description = "override the eval-stack base address (must be page aligned)")
|
||||
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
||||
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
|
||||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
||||
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
|
||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations")
|
||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
|
||||
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 warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}') (required)")
|
||||
val includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
|
||||
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 breakpointCpuInstruction by cli.option(ArgType.Boolean, fullName = "breakinstr", description = "also use a CPU instruction for %breakpoint")
|
||||
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}', '${PETTarget.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 varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank (0=keep active), on other systems it is ignored.")
|
||||
val 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 varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
try {
|
||||
@@ -86,7 +85,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) {
|
||||
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, PETTarget.NAME, VMTarget.NAME)) {
|
||||
System.err.println("Invalid compilation target: $compilationTarget")
|
||||
return false
|
||||
}
|
||||
@@ -101,25 +100,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return runVm(moduleFiles.first())
|
||||
}
|
||||
|
||||
var evalStackAddr: UInt? = null
|
||||
if(evalStackAddrString!=null) {
|
||||
try {
|
||||
evalStackAddr = if (evalStackAddrString!!.startsWith("0x"))
|
||||
evalStackAddrString!!.substring(2).toUInt(16)
|
||||
else if (evalStackAddrString!!.startsWith("$"))
|
||||
evalStackAddrString!!.substring(1).toUInt(16)
|
||||
else
|
||||
evalStackAddrString!!.toUInt()
|
||||
} catch(nx: NumberFormatException) {
|
||||
System.err.println("invalid address for evalstack: $evalStackAddrString")
|
||||
return false
|
||||
}
|
||||
if(evalStackAddr !in 256u..65536u-512u || (evalStackAddr and 255u != 0u)) {
|
||||
System.err.println("invalid address for evalstack: ${evalStackAddr.toHex()}")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val processedSymbols = processSymbolDefs(symbolDefs) ?: return false
|
||||
|
||||
if(watchMode==true) {
|
||||
@@ -134,16 +114,16 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val compilerArgs = CompilerArguments(
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
optimizeFloatExpressions == true,
|
||||
dontWriteAssembly != true,
|
||||
slowCodegenWarnings == true,
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
compilationTarget!!,
|
||||
evalStackAddr,
|
||||
splitWordArrays == true,
|
||||
breakpointCpuInstruction = false,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
@@ -203,16 +183,16 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val compilerArgs = CompilerArguments(
|
||||
filepath,
|
||||
dontOptimize != true,
|
||||
optimizeFloatExpressions == true,
|
||||
dontWriteAssembly != true,
|
||||
slowCodegenWarnings == true,
|
||||
warnSymbolShadowing == true,
|
||||
quietAssembler == true,
|
||||
asmListfile == true,
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
varsHighBank,
|
||||
compilationTarget!!,
|
||||
evalStackAddr,
|
||||
splitWordArrays == true,
|
||||
breakpointCpuInstruction == true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
|
||||
@@ -5,11 +5,11 @@ package prog8.buildversion
|
||||
*/
|
||||
const val MAVEN_GROUP = "prog8"
|
||||
const val MAVEN_NAME = "compiler"
|
||||
const val VERSION = "9.2-SNAPSHOT"
|
||||
const val GIT_REVISION = 3964
|
||||
const val GIT_SHA = "aaa30e4a583fa7c8da61998cf7cbb2a60b52afb0"
|
||||
const val GIT_DATE = "2023-07-16T21:16:18Z"
|
||||
const val VERSION = "9.5.1"
|
||||
const val GIT_REVISION = 4157
|
||||
const val GIT_SHA = "3dc5a0e7f8e5a94fdb4c13ff9199a4bf912268f2"
|
||||
const val GIT_DATE = "2023-10-18T21:59:37Z"
|
||||
const val GIT_BRANCH = "master"
|
||||
const val BUILD_DATE = "2023-07-16T21:16:23Z"
|
||||
const val BUILD_UNIX_TIME = 1689542183583L
|
||||
const val BUILD_DATE = "2023-10-19T19:55:55Z"
|
||||
const val BUILD_UNIX_TIME = 1697745355291L
|
||||
const val DIRTY = 1
|
||||
Loaded 100 of 225 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user