mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 16:23:42 +00:00
Compare commits
373 Commits
Author | SHA1 | Date | |
---|---|---|---|
15ee90e99c | |||
795f80b4ec | |||
6b6427492d | |||
6055b8c3dc | |||
a98cb50d55 | |||
e98bbc1c52 | |||
7245aece4f | |||
60cbb02822 | |||
4e863ecdac | |||
5037033fcf | |||
e6b158bc97 | |||
4cc0dfa10b | |||
4ced8889d3 | |||
d26967a87d | |||
fc8955941b | |||
071a80360f | |||
d2154f5f2e | |||
334d382bfa | |||
90c4b00f74 | |||
71261525e8 | |||
3126959576 | |||
02e51d8282 | |||
ffb2027a19 | |||
70c9ab9074 | |||
6d1fdf1ba6 | |||
1f7180d9a8 | |||
b4e94ae4dd | |||
07c606bfc9 | |||
e705a8bd89 | |||
b3bdfb7f1f | |||
5af1aeb092 | |||
be64fa674a | |||
204f5591a9 | |||
ee3e3a3a40 | |||
f9200a2b75 | |||
f570b70827 | |||
0db141eeac | |||
acb2ee53bb | |||
c544b7f5ba | |||
c0024e97e5 | |||
bdf8aa9168 | |||
de5ce0f515 | |||
bb95484c8a | |||
cad18b8a3a | |||
0f6a98751a | |||
aac5a4c27f | |||
d3f6415387 | |||
04da44eb98 | |||
7649be97b1 | |||
c9ef777e0f | |||
c0cb2438d5 | |||
30c531b39e | |||
bf703a8a66 | |||
e7b631b087 | |||
a9f5dc036c | |||
0a83b51e00 | |||
eab63ecc6c | |||
b0794cf35e | |||
5b9e71a27d | |||
eae41de27d | |||
e9163aa3a7 | |||
8c617515ba | |||
04e4e71f2e | |||
a587482edf | |||
0aac9350d5 | |||
f56c12ee4e | |||
4bb9ae61f2 | |||
ff7f3484e4 | |||
5da3abe6b4 | |||
c0b398e0ce | |||
3de10adac2 | |||
1b573d6552 | |||
2a96f93919 | |||
c6b2639ca4 | |||
b9abf37a7e | |||
373cbb4144 | |||
a521982576 | |||
a77fde577c | |||
ea6926e57d | |||
ba25b7fee6 | |||
7ee162d98b | |||
380f557c45 | |||
1bdae53f4e | |||
9314c346da | |||
bfaad1388c | |||
0b580ad05d | |||
bb35a80177 | |||
24fc95ac81 | |||
8f864417c4 | |||
bb9d29b061 | |||
b9d8ec1463 | |||
1842a7660d | |||
5caa2f5536 | |||
d6078be8b7 | |||
cf60723f14 | |||
f7ff0a2b1d | |||
cc49664b2f | |||
99fe74f026 | |||
b021869eeb | |||
b8806d163b | |||
1116aae1de | |||
5e5f60253b | |||
bbc02752c9 | |||
9896bc110e | |||
ca60f8ecdd | |||
544acd1e35 | |||
6e07602d77 | |||
82898f7bba | |||
d61283a8bc | |||
1ee3f826cc | |||
4a00a5ba9e | |||
39eda67867 | |||
a99d38fdaa | |||
0eb2d437e2 | |||
3ac9036c79 | |||
c94e292176 | |||
91d87c2d9b | |||
ff472f69c0 | |||
e18119e24c | |||
4a592dc64c | |||
d9e13201dd | |||
5c75b19c5d | |||
52a77db60f | |||
0513c250fb | |||
48864ad6cf | |||
cdbccad21e | |||
e15bc68c9b | |||
8bffd7672d | |||
61df5b3060 | |||
b5255444cd | |||
0c94e377fc | |||
8e5c67b4b2 | |||
b24f2f1756 | |||
c69c17de42 | |||
061617122a | |||
125ce3240f | |||
7215efe167 | |||
06d1570142 | |||
093c370faa | |||
aec9574737 | |||
7ceb76cff5 | |||
300e2fe9f8 | |||
91e1643627 | |||
91421b0c62 | |||
40f611664f | |||
dcba4f4098 | |||
c098ad2b3b | |||
b43223cb7a | |||
e243531dab | |||
1af38e62bc | |||
f37f062cdc | |||
7e734214dc | |||
05d152746f | |||
dea7f37553 | |||
415c599310 | |||
70cd4fedbe | |||
1e6d7673bc | |||
b4963b725b | |||
0371ffa4ce | |||
6a664a7e15 | |||
88ce9300bc | |||
85cf0e311c | |||
0e3d75cfeb | |||
630c8a5faa | |||
905921a684 | |||
1e469b3b0f | |||
bff3c4f95c | |||
bd2bcb6994 | |||
4c8898a639 | |||
97df33ab1a | |||
ef46fb2685 | |||
d5d6dd3614 | |||
6c233c6a0a | |||
6db715d879 | |||
ab02e8a546 | |||
8cbfe64f19 | |||
fd1e9971e4 | |||
68336a76c5 | |||
393e914a86 | |||
ffb54110e9 | |||
533d825f1a | |||
c65279b672 | |||
f9926beeef | |||
add8a777d8 | |||
21bc505d85 | |||
3fc49c001e | |||
3d69a95c49 | |||
d81fdf6d6b | |||
87d3109ffb | |||
180dbbb521 | |||
24aac7cee5 | |||
53e18a5387 | |||
92062d056d | |||
06368ab0a1 | |||
38efe25c68 | |||
319079de7a | |||
025bf900a5 | |||
2885f4f7b1 | |||
c07eda15b1 | |||
4274296cf3 | |||
76a203d4df | |||
24f37e2062 | |||
f465b2e2a0 | |||
ce00e49a89 | |||
d494f9d66b | |||
c35a183a64 | |||
9cdd5fe7f2 | |||
c21428215e | |||
64d5af46f5 | |||
25846ea18a | |||
798383596d | |||
9ca71bc937 | |||
5407429ec0 | |||
ee5c94f6db | |||
91045afbee | |||
3f64782023 | |||
f8d35f9502 | |||
ea78d3ec9a | |||
e056a28316 | |||
0bea721c2e | |||
e1b89494d0 | |||
cd8e7f3912 | |||
50604c25c2 | |||
aa6b2357d8 | |||
5b2d29bef6 | |||
a296d26328 | |||
d01a26ec61 | |||
efd7d6f0c0 | |||
b55be093be | |||
7c1d5cadd7 | |||
dd1592b03b | |||
9b37ac483f | |||
090820958e | |||
ac21e1be5c | |||
5196443b26 | |||
c8531cbeb1 | |||
c560abedba | |||
9b952fbc44 | |||
ccdf05e922 | |||
c3d74f2ae9 | |||
f47498888c | |||
5665a7f0cb | |||
b8178c6c8d | |||
4a0f15eb88 | |||
c4f53fe525 | |||
8c93ec52de | |||
befe0fff2a | |||
b6a837cbea | |||
4861973899 | |||
c593e4b500 | |||
5bf78c20d4 | |||
5c672130e6 | |||
d8214d4f12 | |||
64d1f09ce0 | |||
47d0f0ea40 | |||
2d85fd093e | |||
d936568b76 | |||
4598a83e8e | |||
f4bf00ad31 | |||
07fde7f6cc | |||
729209574e | |||
f28206d989 | |||
0c81b32cac | |||
11216017cb | |||
a7b9f53967 | |||
1fa2e2e37d | |||
f67d5faeb7 | |||
5cbf859458 | |||
629ed74d09 | |||
ca2af2ca63 | |||
52ab089615 | |||
01461a196d | |||
04832f052a | |||
c8b2c8ae50 | |||
1b81c7fb22 | |||
9ccda0247e | |||
a7df4dcf25 | |||
d91f47c791 | |||
a9ac4e7f44 | |||
fc3ec57437 | |||
266f6ab919 | |||
6218c1c00b | |||
cc81d6fe82 | |||
69f9102f2d | |||
beb9275982 | |||
abe48713f2 | |||
82cfaf2fbb | |||
3d3bc4738f | |||
2d0746f5a4 | |||
9c71e2f1c8 | |||
134fd62da8 | |||
2afd283582 | |||
c66734bab0 | |||
8e56a61f95 | |||
d265271148 | |||
b40e397b28 | |||
35ff1d996a | |||
deea0b05cb | |||
c50c9ca545 | |||
a819b4a5a5 | |||
df2d7d4734 | |||
79ce4098cf | |||
374464a1f8 | |||
c8d0bf27af | |||
6e4ae034b2 | |||
52b560e72d | |||
9b971ad222 | |||
3613162d09 | |||
3a272e998d | |||
d4c750beb4 | |||
84b31e65e1 | |||
7b802bfd3d | |||
f9c4632b8d | |||
e4764cd8a6 | |||
dd78a3a686 | |||
94c06e13f4 | |||
e8bebe5a75 | |||
5b0e1b4f9e | |||
8c0a93779b | |||
9241479da4 | |||
8ffca93cd5 | |||
7fea0c124a | |||
20dbdb20d2 | |||
e6b8e2e8be | |||
7c5b7f77cc | |||
de84547a21 | |||
44676756ae | |||
b399b0f182 | |||
1152191f48 | |||
af1b07ad44 | |||
b8113fff1e | |||
ff6948cf2d | |||
fd25e85d59 | |||
c07cd72e85 | |||
e2c101206c | |||
92276b5769 | |||
a2133f61a8 | |||
199adbbcf0 | |||
dc316fd7b4 | |||
025183602f | |||
db4619a9d9 | |||
451e527b7c | |||
54dd3a00df | |||
03c5dab79d | |||
1fdee861e8 | |||
c12bf991b3 | |||
78a097585d | |||
39132327cc | |||
dc32318cec | |||
592f74124c | |||
e5e63cc5ac | |||
f40e0f786d | |||
ebd9f1471b | |||
d76547ead4 | |||
4600772e05 | |||
ed597423cd | |||
f20ca06f85 | |||
a636d3f394 | |||
043df18daa | |||
96996bf18e | |||
f350137a14 | |||
b7a6f3ec75 | |||
6c34672549 | |||
e779a07bce | |||
9a36e8ba3b | |||
c968bacb01 | |||
25199dfb43 | |||
48fed4e6fb | |||
fc253237c9 | |||
589948c7f4 | |||
7e69690605 | |||
95f498ba9b | |||
fd07ae5225 |
13
.github/workflows/all-ci.yml
vendored
13
.github/workflows/all-ci.yml
vendored
@ -10,13 +10,18 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install 64tass
|
- name: build and install recent 64tass
|
||||||
run: sudo apt-get update -y && sudo apt-get install -y 64tass
|
run: |
|
||||||
|
sudo apt-get install -y make build-essential
|
||||||
|
git clone --depth=1 https://github.com/irmen/64tass
|
||||||
|
cd 64tass
|
||||||
|
make -j4
|
||||||
|
sudo make install
|
||||||
|
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: adopt
|
distribution: adopt
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,6 +15,7 @@ out/
|
|||||||
parser/**/*.interp
|
parser/**/*.interp
|
||||||
parser/**/*.tokens
|
parser/**/*.tokens
|
||||||
parser/**/*.java
|
parser/**/*.java
|
||||||
|
compiler/src/prog8/buildversion/*
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*.egg
|
*.egg
|
||||||
*.egg-info
|
*.egg-info
|
||||||
@ -32,3 +33,4 @@ compiler/lib/
|
|||||||
/prog8compiler.jar
|
/prog8compiler.jar
|
||||||
sd*.img
|
sd*.img
|
||||||
*.d64
|
*.d64
|
||||||
|
|
||||||
|
9
.idea/kotlinc.xml
generated
Normal file
9
.idea/kotlinc.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Kotlin2JvmCompilerArguments">
|
||||||
|
<option name="jvmTarget" value="11" />
|
||||||
|
</component>
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="1.9.0-release-358" />
|
||||||
|
</component>
|
||||||
|
</project>
|
7
.idea/libraries/antlr_antlr4.xml
generated
7
.idea/libraries/antlr_antlr4.xml
generated
@ -1,17 +1,16 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="antlr.antlr4" type="repository">
|
<library name="antlr.antlr4" type="repository">
|
||||||
<properties maven-id="org.antlr:antlr4:4.11.1">
|
<properties maven-id="org.antlr:antlr4:4.13.0">
|
||||||
<exclude>
|
<exclude>
|
||||||
<dependency maven-id="com.ibm.icu:icu4j" />
|
<dependency maven-id="com.ibm.icu:icu4j" />
|
||||||
</exclude>
|
</exclude>
|
||||||
</properties>
|
</properties>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.11.1/antlr4-4.11.1.jar!/" />
|
<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.11.1/antlr4-runtime-4.11.1.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/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
|
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
30
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
30
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
@ -1,23 +1,23 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="github.hypfvieh.dbus.java" type="repository">
|
<library name="github.hypfvieh.dbus.java" type="repository">
|
||||||
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.1" />
|
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.1/dbus-java-3.3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.6/jnr-unixsocket-0.38.6.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.2/jnr-ffi-2.2.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1-native.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.1/asm-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.1/asm-commons-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.1/asm-analysis-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.1/asm-tree-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.1/asm-util-9.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.1/jnr-constants-0.10.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.4/jnr-enxio-0.32.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.5/jnr-posix-3.1.5.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
28
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
28
.idea/libraries/io_kotest_assertions_core_jvm.xml
generated
@ -1,21 +1,21 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.3.2" />
|
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.6.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.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.3.2/kotest-assertions-shared-jvm-5.3.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$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.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-common-jvm/5.3.2/kotest-common-jvm-5.3.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-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.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>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
30
.idea/libraries/io_kotest_property_jvm.xml
generated
30
.idea/libraries/io_kotest_property_jvm.xml
generated
@ -1,22 +1,22 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="io.kotest.property.jvm" type="repository">
|
<library name="io.kotest.property.jvm" type="repository">
|
||||||
<properties maven-id="io.kotest:kotest-property-jvm:5.3.2" />
|
<properties maven-id="io.kotest:kotest-property-jvm:5.6.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.3.2/kotest-property-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.6.2/kotest-property-jvm-5.6.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.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-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.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-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.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/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
71
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
71
.idea/libraries/io_kotest_runner_junit5_jvm.xml
generated
@ -1,51 +1,42 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.3.2" />
|
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.6.2" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.3.2/kotest-runner-junit5-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.6.2/kotest-runner-junit5-jvm-5.6.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.3.2/kotest-framework-api-jvm-5.3.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.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.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.6.1/kotlinx-coroutines-test-jvm-1.6.1.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.3.2/kotest-common-jvm-5.3.2.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.3.2/kotest-framework-engine-jvm-5.3.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.146/classgraph-4.8.146.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.157/classgraph-4.8.157.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.1/kotlinx-coroutines-debug-1.6.1.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/5.9.0/jna-5.9.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.3.2/kotest-framework-discovery-jvm-5.3.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.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$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.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-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.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-extensions-jvm/5.3.2/kotest-extensions-jvm-5.3.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$/commons-io/commons-io/2.11.0/commons-io-2.11.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/mockk/mockk/1.12.3/mockk-1.12.3.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/mockk/mockk-common/1.12.3/mockk-common-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.6.2/kotest-framework-concurrency-jvm-5.6.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.3/mockk-dsl-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.3/mockk-dsl-jvm-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.3/mockk-agent-jvm-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.3/mockk-agent-api-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.3/mockk-agent-common-1.12.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.6/byte-buddy-1.12.6.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.6/byte-buddy-agent-1.12.6.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.3.2/kotest-framework-concurrency-jvm-5.3.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/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.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-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.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/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.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$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
|
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
4
.idea/libraries/jetbrains_kotlinx_cli_jvm.xml
generated
@ -1,8 +1,8 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
|
||||||
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.4" />
|
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.5" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.4/kotlinx-cli-jvm-0.3.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.5/kotlinx-cli-jvm-0.3.5.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
12
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
12
.idea/libraries/michael_bull_kotlin_result_jvm.xml
generated
@ -1,13 +1,13 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
|
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.18/kotlin-result-jvm-1.1.18.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
|
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
6
.idea/libraries/slf4j_simple.xml
generated
6
.idea/libraries/slf4j_simple.xml
generated
@ -1,9 +1,9 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="slf4j.simple" type="repository">
|
<library name="slf4j.simple" type="repository">
|
||||||
<properties maven-id="org.slf4j:slf4j-simple:1.7.36" />
|
<properties maven-id="org.slf4j:slf4j-simple:2.0.7" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar!/" />
|
<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/1.7.36/slf4j-api-1.7.36.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
19
.idea/libraries/takes.xml
generated
19
.idea/libraries/takes.xml
generated
@ -1,15 +1,16 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="takes" type="repository">
|
<library name="takes" type="repository">
|
||||||
<properties maven-id="org.takes:takes:1.20" />
|
<properties maven-id="org.takes:takes:1.24.4" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.20/takes-1.20.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.50/cactoos-0.50.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" />
|
||||||
|
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
@ -7,13 +7,9 @@ version: 2
|
|||||||
|
|
||||||
# Set the version of Python and other tools you might need
|
# Set the version of Python and other tools you might need
|
||||||
build:
|
build:
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
tools:
|
tools:
|
||||||
python: "3.9"
|
python: "3.11"
|
||||||
# You can also specify other tool versions:
|
|
||||||
# nodejs: "16"
|
|
||||||
# rust: "1.55"
|
|
||||||
# golang: "1.17"
|
|
||||||
|
|
||||||
# Build documentation in the docs/ directory with Sphinx
|
# Build documentation in the docs/ directory with Sphinx
|
||||||
sphinx:
|
sphinx:
|
||||||
|
19
README.md
19
README.md
@ -14,6 +14,21 @@ Documentation
|
|||||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||||
https://prog8.readthedocs.io/
|
https://prog8.readthedocs.io/
|
||||||
|
|
||||||
|
How to get it/build it
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- Download the latest [official release](https://github.com/irmen/prog8/releases) from github.
|
||||||
|
- Or, if you want/need a bleeding edge development version, you can:
|
||||||
|
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
|
||||||
|
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
|
||||||
|
|
||||||
|
Community
|
||||||
|
---------
|
||||||
|
Most of the development on Prog8 and the use of it is currently centered around
|
||||||
|
the [Commander X16](https://www.commanderx16.com/) retro computer. Their [discord server](https://discord.gg/nS2PqEC) contains a small channel
|
||||||
|
dedicated to Prog8. Other than that, use the issue tracker on github.
|
||||||
|
|
||||||
|
|
||||||
Software license
|
Software license
|
||||||
----------------
|
----------------
|
||||||
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
||||||
@ -76,7 +91,9 @@ IntelliJ IDEA with the Kotlin plugin).
|
|||||||
|
|
||||||
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
||||||
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
||||||
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
and a recent emulator version (R42 or newer) for the CommanderX16, such as [x16emu](https://cx16forum.com/forum/viewforum.php?f=30)
|
||||||
|
(preferred, this is the official emulator. If required, source code is [here](https://github.com/X16Community/x16-emulator/)).
|
||||||
|
There is also [Box16](https://github.com/indigodarkwolf/box16) which has powerful debugging features.
|
||||||
|
|
||||||
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
||||||
Look in the [syntax-files](https://github.com/irmen/prog8/tree/master/syntax-files) directory in the github repository to find them.
|
Look in the [syntax-files](https://github.com/irmen/prog8/tree/master/syntax-files) directory in the github repository to find them.
|
||||||
|
@ -26,7 +26,7 @@ compileTestKotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
// should have no dependencies to other modules
|
// should have no dependencies to other modules
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
@ -15,14 +15,26 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val flat: Map<String, StNode> by lazy {
|
private var cachedFlat: Map<String, StNode>? = null
|
||||||
|
|
||||||
|
val flat: Map<String, StNode> get() {
|
||||||
|
if(cachedFlat!=null)
|
||||||
|
return cachedFlat!!
|
||||||
|
|
||||||
val result = mutableMapOf<String, StNode>()
|
val result = mutableMapOf<String, StNode>()
|
||||||
fun flatten(node: StNode) {
|
fun collect(node: StNode) {
|
||||||
result[node.scopedName] = node
|
for(child in node.children) {
|
||||||
node.children.values.forEach { flatten(it) }
|
result[child.value.scopedName] = child.value
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
children.values.forEach { flatten(it) }
|
collect(this)
|
||||||
result
|
cachedFlat = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetCachedFlat() {
|
||||||
|
cachedFlat = null
|
||||||
}
|
}
|
||||||
|
|
||||||
val allVariables: Collection<StStaticVariable> by lazy {
|
val allVariables: Collection<StStaticVariable> by lazy {
|
||||||
@ -68,6 +80,16 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: String) = flat[scopedName]
|
override fun lookup(scopedName: String) = flat[scopedName]
|
||||||
|
|
||||||
|
fun getLength(name: String): Int? {
|
||||||
|
val node = flat[name]
|
||||||
|
return when(node) {
|
||||||
|
is StMemVar -> node.length
|
||||||
|
is StMemorySlab -> node.size.toInt()
|
||||||
|
is StStaticVariable -> node.length
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -133,7 +155,7 @@ open class StNode(val name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val scopedNameList: List<String> by lazy {
|
private val scopedNameList: List<String> by lazy {
|
||||||
if(type== StNodeType.GLOBAL)
|
if(type==StNodeType.GLOBAL)
|
||||||
emptyList()
|
emptyList()
|
||||||
else
|
else
|
||||||
parent.scopedNameList + name
|
parent.scopedNameList + name
|
||||||
@ -142,7 +164,7 @@ open class StNode(val name: String,
|
|||||||
private fun lookup(scopedName: List<String>): StNode? {
|
private fun lookup(scopedName: List<String>): StNode? {
|
||||||
// a scoped name refers to a name in another namespace, and always stars from the root.
|
// a scoped name refers to a name in another namespace, and always stars from the root.
|
||||||
var node = this
|
var node = this
|
||||||
while(node.type!= StNodeType.GLOBAL)
|
while(node.type!=StNodeType.GLOBAL)
|
||||||
node = node.parent
|
node = node.parent
|
||||||
|
|
||||||
for(name in scopedName) {
|
for(name in scopedName) {
|
||||||
@ -201,6 +223,11 @@ class StMemVar(name: String,
|
|||||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
astNode: PtNode) :
|
astNode: PtNode) :
|
||||||
StNode(name, StNodeType.MEMVAR, astNode) {
|
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||||
|
|
||||||
|
init{
|
||||||
|
if(dt in ArrayDatatypes || dt == DataType.STR)
|
||||||
|
require(length!=null) { "memory mapped array or string must have known length" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StMemorySlab(
|
class StMemorySlab(
|
||||||
@ -218,7 +245,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
|
|||||||
|
|
||||||
|
|
||||||
class StRomSub(name: String,
|
class StRomSub(name: String,
|
||||||
val address: UInt,
|
val address: UInt?, // null in case of asmsub, specified in case of romsub
|
||||||
val parameters: List<StRomSubParameter>,
|
val parameters: List<StRomSubParameter>,
|
||||||
val returns: List<StRomSubParameter>,
|
val returns: List<StRomSubParameter>,
|
||||||
astNode: PtNode) :
|
astNode: PtNode) :
|
||||||
|
@ -26,11 +26,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||||
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||||
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||||
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, 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, 256u, Position.DUMMY)
|
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY)
|
||||||
).forEach {
|
).forEach {
|
||||||
it.parent = program
|
it.parent = program
|
||||||
st.add(StMemVar(it.name, it.type, it.address, null, it))
|
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,14 +40,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
|
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
|
||||||
val stNode = when(node) {
|
val stNode = when(node) {
|
||||||
is PtAsmSub -> {
|
is PtAsmSub -> {
|
||||||
if(node.address==null) {
|
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||||
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
|
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
||||||
StSub(node.name, params, node.returns.singleOrNull()?.second, node)
|
StRomSub(node.name, node.address, parameters, returns, node)
|
||||||
} else {
|
|
||||||
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
|
||||||
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
|
||||||
StRomSub(node.name, node.address, parameters, returns, node)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is PtBlock -> {
|
is PtBlock -> {
|
||||||
StNode(node.name, StNodeType.BLOCK, node)
|
StNode(node.name, StNodeType.BLOCK, node)
|
||||||
@ -99,6 +94,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
|||||||
initialString = null
|
initialString = null
|
||||||
numElements = node.arraySize?.toInt()
|
numElements = node.arraySize?.toInt()
|
||||||
}
|
}
|
||||||
|
// if(node.type in SplitWordArrayTypes) {
|
||||||
|
// ... split array also add _lsb and _msb to symboltable?
|
||||||
|
// }
|
||||||
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
|
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
|
||||||
}
|
}
|
||||||
is PtBuiltinFunctionCall -> {
|
is PtBuiltinFunctionCall -> {
|
||||||
|
@ -37,16 +37,17 @@ class PtNodeGroup : PtNode(Position.DUMMY)
|
|||||||
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||||
// Note that as an exception, the 'name' is not read-only
|
// Note that as an exception, the 'name' is not read-only
|
||||||
// but a var. This is to allow for cheap node renames.
|
// but a var. This is to allow for cheap node renames.
|
||||||
val scopedName: String by lazy {
|
val scopedName: String
|
||||||
var namedParent: PtNode = this.parent
|
get() {
|
||||||
if(namedParent is PtProgram)
|
var namedParent: PtNode = this.parent
|
||||||
name
|
return if(namedParent is PtProgram)
|
||||||
else {
|
name
|
||||||
while (namedParent !is PtNamedNode)
|
else {
|
||||||
namedParent = namedParent.parent
|
while (namedParent !is PtNamedNode)
|
||||||
namedParent.scopedName + "." + name
|
namedParent = namedParent.parent
|
||||||
|
namedParent.scopedName + "." + name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -63,7 +64,9 @@ class PtProgram(
|
|||||||
children.asSequence().filterIsInstance<PtBlock>()
|
children.asSequence().filterIsInstance<PtBlock>()
|
||||||
|
|
||||||
fun entrypoint(): PtSub? =
|
fun entrypoint(): PtSub? =
|
||||||
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
|
allBlocks().firstOrNull { it.name == "main" || it.name=="p8_main" }
|
||||||
|
?.children
|
||||||
|
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8_start" || it.name=="p8_main.p8_start") } as PtSub?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -71,6 +74,7 @@ class PtBlock(name: String,
|
|||||||
val address: UInt?,
|
val address: UInt?,
|
||||||
val library: Boolean,
|
val library: Boolean,
|
||||||
val forceOutput: Boolean,
|
val forceOutput: Boolean,
|
||||||
|
val noSymbolPrefixing: Boolean,
|
||||||
val alignment: BlockAlignment,
|
val alignment: BlockAlignment,
|
||||||
val source: SourceCode, // taken from the module the block is defined in.
|
val source: SourceCode, // taken from the module the block is defined in.
|
||||||
position: Position
|
position: Position
|
||||||
|
@ -25,7 +25,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
infix fun isSameAs(other: PtExpression): Boolean {
|
infix fun isSameAs(other: PtExpression): Boolean {
|
||||||
return when(this) {
|
return when(this) {
|
||||||
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
||||||
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
|
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 PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
|
||||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||||
@ -48,7 +48,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
this.name == target.identifier!!.name
|
this.name == target.identifier!!.name
|
||||||
}
|
}
|
||||||
target.array != null && this is PtArrayIndexer -> {
|
target.array != null && this is PtArrayIndexer -> {
|
||||||
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index
|
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@ -115,6 +115,9 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
|||||||
val index: PtExpression
|
val index: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
|
val splitWords: Boolean
|
||||||
|
get() = variable.type in SplitWordArrayTypes
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(elementType in NumericDatatypes)
|
require(elementType in NumericDatatypes)
|
||||||
}
|
}
|
||||||
@ -180,7 +183,13 @@ class PtFunctionCall(val name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position)
|
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "[PtIdentifier:$name $type $position]"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copy() = PtIdentifier(name, type, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
||||||
@ -215,6 +224,8 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||||
|
|
||||||
|
override fun toString() = "PtNumber:$type:$number"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import prog8.code.core.*
|
|||||||
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
|
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
|
||||||
* passing it as a String to the specified receiver function.
|
* passing it as a String to the specified receiver function.
|
||||||
*/
|
*/
|
||||||
fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
|
||||||
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
||||||
fun txt(node: PtNode): String {
|
fun txt(node: PtNode): String {
|
||||||
return when(node) {
|
return when(node) {
|
||||||
@ -17,7 +17,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||||
is PtAddressOf -> "&"
|
is PtAddressOf -> "&"
|
||||||
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
|
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
|
||||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}"
|
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
|
||||||
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||||
is PtBuiltinFunctionCall -> {
|
is PtBuiltinFunctionCall -> {
|
||||||
val str = if(node.void) "void " else ""
|
val str = if(node.void) "void " else ""
|
||||||
@ -96,13 +96,14 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
str
|
str
|
||||||
}
|
}
|
||||||
is PtVariable -> {
|
is PtVariable -> {
|
||||||
|
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
|
||||||
val str = if(node.arraySize!=null) {
|
val str = if(node.arraySize!=null) {
|
||||||
val eltType = ArrayToElementTypes.getValue(node.type)
|
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||||
"${eltType.name.lowercase()}[${node.arraySize}] ${node.name}"
|
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
|
||||||
}
|
}
|
||||||
else if(node.type in ArrayDatatypes) {
|
else if(node.type in ArrayDatatypes) {
|
||||||
val eltType = ArrayToElementTypes.getValue(node.type)
|
val eltType = ArrayToElementTypes.getValue(node.type)
|
||||||
"${eltType.name.lowercase()}[] ${node.name}"
|
"${eltType.name.lowercase()}[] $split ${node.name}"
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
"${node.type.name.lowercase()} ${node.name}"
|
"${node.type.name.lowercase()} ${node.name}"
|
||||||
@ -134,16 +135,22 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
root.children.forEach {
|
root.children.forEach {
|
||||||
walkAst(it) { node, depth ->
|
walkAst(it) { node, depth ->
|
||||||
val txt = txt(node)
|
val txt = txt(node)
|
||||||
if(txt.isNotEmpty())
|
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||||
output(" ".repeat(depth) + txt(node))
|
if(!library || !skipLibraries) {
|
||||||
|
if (txt.isNotEmpty())
|
||||||
|
output(" ".repeat(depth) + txt(node))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println()
|
println()
|
||||||
} else {
|
} else {
|
||||||
walkAst(root) { node, depth ->
|
walkAst(root) { node, depth ->
|
||||||
val txt = txt(node)
|
val txt = txt(node)
|
||||||
if(txt.isNotEmpty())
|
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
|
||||||
output(" ".repeat(depth) + txt(node))
|
if(!library || !skipLibraries) {
|
||||||
|
if (txt.isNotEmpty())
|
||||||
|
output(" ".repeat(depth) + txt(node))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,25 +38,17 @@ class PtSub(
|
|||||||
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
|
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
|
||||||
|
|
||||||
|
|
||||||
class PtAssignment(position: Position) : PtNode(position) {
|
sealed interface IPtAssignment {
|
||||||
|
val children: MutableList<PtNode>
|
||||||
val target: PtAssignTarget
|
val target: PtAssignTarget
|
||||||
get() = children[0] as PtAssignTarget
|
get() = children[0] as PtAssignTarget
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
|
||||||
|
|
||||||
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position) {
|
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
|
||||||
val target: PtAssignTarget
|
|
||||||
get() = children[0] as PtAssignTarget
|
|
||||||
val value: PtExpression
|
|
||||||
get() = children[1] as PtExpression
|
|
||||||
init {
|
|
||||||
require(operator.endsWith('=') || operator in PrefixOperators) {
|
|
||||||
"invalid augmented assign operator $operator"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtAssignTarget(position: Position) : PtNode(position) {
|
class PtAssignTarget(position: Position) : PtNode(position) {
|
||||||
@ -100,8 +92,8 @@ class PtForLoop(position: Position) : PtNode(position) {
|
|||||||
|
|
||||||
|
|
||||||
class PtIfElse(position: Position) : PtNode(position) {
|
class PtIfElse(position: Position) : PtNode(position) {
|
||||||
val condition: PtBinaryExpression
|
val condition: PtExpression
|
||||||
get() = children[0] as PtBinaryExpression
|
get() = children[0] as PtExpression
|
||||||
val ifScope: PtNodeGroup
|
val ifScope: PtNodeGroup
|
||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
val elseScope: PtNodeGroup
|
val elseScope: PtNodeGroup
|
||||||
|
@ -68,7 +68,6 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||||
// this set of function have no return value and operate in-place:
|
// this set of function have no return value and operate in-place:
|
||||||
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
@ -79,17 +78,42 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
|||||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
// cmp returns a status in the carry flag, but not a proper return value
|
// cmp returns a status in the carry flag, but not a proper return value
|
||||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||||
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
|
||||||
|
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||||
|
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||||
|
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
|
||||||
|
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
|
||||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||||
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
|
||||||
|
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||||
|
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
|
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
|
||||||
|
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
|
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
|
||||||
|
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
|
||||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||||
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||||
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||||
|
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
|
||||||
|
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||||
|
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||||
|
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
|
||||||
|
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
|
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
|
||||||
|
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||||
|
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||||
|
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
|
||||||
|
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
|
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
|
||||||
|
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
|
||||||
|
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
|
||||||
|
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
|
||||||
|
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||||
|
@ -19,7 +19,8 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var asmQuiet: Boolean = false,
|
var asmQuiet: Boolean = false,
|
||||||
var asmListfile: Boolean = false,
|
var asmListfile: Boolean = false,
|
||||||
var experimentalCodegen: Boolean = false,
|
var experimentalCodegen: Boolean = false,
|
||||||
var varsHigh: Boolean = false,
|
var varsHighBank: Int? = null,
|
||||||
|
var splitWordArrays: Boolean = false,
|
||||||
var evalStackBaseAddress: UInt? = null,
|
var evalStackBaseAddress: UInt? = null,
|
||||||
var outputDir: Path = Path(""),
|
var outputDir: Path = Path(""),
|
||||||
var symbolDefs: Map<String, String> = emptyMap()
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
|
@ -11,7 +11,9 @@ enum class DataType {
|
|||||||
ARRAY_UB, // pass by reference
|
ARRAY_UB, // pass by reference
|
||||||
ARRAY_B, // pass by reference
|
ARRAY_B, // pass by reference
|
||||||
ARRAY_UW, // pass by reference
|
ARRAY_UW, // pass by reference
|
||||||
|
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
|
||||||
ARRAY_W, // pass by reference
|
ARRAY_W, // pass by reference
|
||||||
|
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
|
||||||
ARRAY_F, // pass by reference
|
ARRAY_F, // pass by reference
|
||||||
ARRAY_BOOL, // pass by reference
|
ARRAY_BOOL, // pass by reference
|
||||||
UNDEFINED;
|
UNDEFINED;
|
||||||
@ -119,12 +121,14 @@ val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWO
|
|||||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
|
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
|
||||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||||
|
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
|
||||||
val IterableDatatypes = arrayOf(
|
val IterableDatatypes = arrayOf(
|
||||||
DataType.STR,
|
DataType.STR,
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||||
|
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
|
||||||
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
DataType.ARRAY_F, DataType.ARRAY_BOOL
|
||||||
)
|
)
|
||||||
val PassByValueDatatypes = NumericDatatypes
|
val PassByValueDatatypes = NumericDatatypes
|
||||||
@ -135,6 +139,8 @@ val ArrayToElementTypes = mapOf(
|
|||||||
DataType.ARRAY_UB to DataType.UBYTE,
|
DataType.ARRAY_UB to DataType.UBYTE,
|
||||||
DataType.ARRAY_W to DataType.WORD,
|
DataType.ARRAY_W to DataType.WORD,
|
||||||
DataType.ARRAY_UW to DataType.UWORD,
|
DataType.ARRAY_UW to DataType.UWORD,
|
||||||
|
DataType.ARRAY_W_SPLIT to DataType.WORD,
|
||||||
|
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
|
||||||
DataType.ARRAY_F to DataType.FLOAT,
|
DataType.ARRAY_F to DataType.FLOAT,
|
||||||
DataType.ARRAY_BOOL to DataType.BOOL
|
DataType.ARRAY_BOOL to DataType.BOOL
|
||||||
)
|
)
|
||||||
|
@ -13,7 +13,5 @@ interface ICodeGeneratorBackend {
|
|||||||
|
|
||||||
interface IAssemblyProgram {
|
interface IAssemblyProgram {
|
||||||
val name: String
|
val name: String
|
||||||
fun assemble(options: CompilationOptions): Boolean
|
fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
|
||||||
|
@ -3,6 +3,7 @@ package prog8.code.core
|
|||||||
interface IErrorReporter {
|
interface IErrorReporter {
|
||||||
fun err(msg: String, position: Position)
|
fun err(msg: String, position: Position)
|
||||||
fun warn(msg: String, position: Position)
|
fun warn(msg: String, position: Position)
|
||||||
|
fun undefined(symbol: List<String>, position: Position)
|
||||||
fun noErrors(): Boolean
|
fun noErrors(): Boolean
|
||||||
fun report()
|
fun report()
|
||||||
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
|
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
|
||||||
|
@ -131,6 +131,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: this class is not yet used
|
||||||
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
||||||
private var nextLocation: UInt = region.first
|
private var nextLocation: UInt = region.first
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
|
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||||
import java.nio.file.InvalidPathException
|
import java.nio.file.InvalidPathException
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
import kotlin.io.path.absolute
|
||||||
@ -7,6 +8,10 @@ import kotlin.io.path.absolute
|
|||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
fun toClickableStr(): String {
|
fun toClickableStr(): String {
|
||||||
|
if(this===DUMMY)
|
||||||
|
return ""
|
||||||
|
if(file.startsWith(libraryFilePrefix))
|
||||||
|
return "$file:$line:$startCol:"
|
||||||
return try {
|
return try {
|
||||||
val path = Path(file).absolute().normalize().toString()
|
val path = Path(file).absolute().normalize().toString()
|
||||||
"file://$path:$line:$startCol:"
|
"file://$path:$line:$startCol:"
|
||||||
|
@ -16,5 +16,7 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Cb
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val NAME = "c64"
|
const val NAME = "c64"
|
||||||
|
|
||||||
|
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class AtariMachineDefinition: IMachineDefinition {
|
|||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
override lateinit var golden: GoldenRam
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
|
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return if (compilerOptions.output == OutputType.XEX)
|
return if (compilerOptions.output == OutputType.XEX)
|
||||||
|
@ -13,6 +13,10 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (options.floats) {
|
||||||
|
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
|
||||||
|
}
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
@ -39,9 +43,9 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val distictFree = free.distinct()
|
val distinctFree = free.distinct()
|
||||||
free.clear()
|
free.clear()
|
||||||
free.addAll(distictFree)
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.code.target.c128
|
package prog8.code.target.c128
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ class C128MachineDefinition: IMachineDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println("\nStarting C-128 emulator x128...")
|
println("\nStarting C-128 emulator x128...")
|
||||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
@ -6,15 +6,22 @@ import prog8.code.core.Zeropage
|
|||||||
import prog8.code.core.ZeropageType
|
import prog8.code.core.ZeropageType
|
||||||
|
|
||||||
|
|
||||||
|
// reference: "Mapping the C128" zero page chapter.
|
||||||
|
|
||||||
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte
|
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x9cu // temp storage for a register, must be B1+1
|
override val SCRATCH_REG = 0x75u // temp storage for a register, must be B1+1
|
||||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (options.floats) {
|
||||||
|
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||||
|
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||||
|
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||||
|
}
|
||||||
|
|
||||||
if (options.floats && options.zeropage !in arrayOf(
|
if (options.floats && options.zeropage !in arrayOf(
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE,
|
ZeropageType.BASICSAFE,
|
||||||
@ -24,23 +31,42 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
|
|
||||||
when (options.zeropage) {
|
when (options.zeropage) {
|
||||||
ZeropageType.FULL -> {
|
ZeropageType.FULL -> {
|
||||||
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine
|
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
|
||||||
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09?
|
free.addAll(0x0au..0xffu)
|
||||||
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
|
||||||
|
}
|
||||||
|
ZeropageType.KERNALSAFE -> {
|
||||||
|
free.addAll(0x0au..0x8fu) // BASIC variables
|
||||||
|
free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||||
|
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
|
||||||
}
|
}
|
||||||
ZeropageType.KERNALSAFE,
|
|
||||||
ZeropageType.FLOATSAFE,
|
ZeropageType.FLOATSAFE,
|
||||||
ZeropageType.BASICSAFE -> {
|
ZeropageType.BASICSAFE -> {
|
||||||
free.clear() // TODO c128 usable zero page addresses
|
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
|
||||||
|
free.addAll(0x1bu..0x23u)
|
||||||
|
free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
|
||||||
|
0x55u, 0x56u, 0x57u, 0x58u,
|
||||||
|
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
|
||||||
|
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||||
|
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u
|
||||||
|
))
|
||||||
|
|
||||||
|
// if(options.zeropage==ZeropageType.BASICSAFE) {
|
||||||
|
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
|
||||||
|
free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
|
||||||
|
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
|
||||||
|
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
|
||||||
|
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
ZeropageType.DONTUSE -> {
|
ZeropageType.DONTUSE -> {
|
||||||
free.clear() // don't use zeropage at all
|
free.clear() // don't use zeropage at all
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val distictFree = free.distinct()
|
val distinctFree = free.distinct()
|
||||||
free.clear()
|
free.clear()
|
||||||
free.addAll(distictFree)
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.code.target.c64
|
package prog8.code.target.c64
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -42,7 +43,7 @@ class C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
@ -44,7 +44,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
// remove the zeropage locations used for floating point operations from the free list
|
// remove the zeropage locations used for floating point operations from the free list
|
||||||
free.removeAll(listOf(
|
free.removeAll(listOf(
|
||||||
0x03, 0x04, 0x10, 0x11, 0x12,
|
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
|
||||||
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
@ -65,9 +65,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val distictFree = free.distinct()
|
val distinctFree = free.distinct()
|
||||||
free.clear()
|
free.clear()
|
||||||
free.addAll(distictFree)
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.code.target.cx16
|
package prog8.code.target.cx16
|
||||||
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||||||
}
|
}
|
||||||
2 -> {
|
2 -> {
|
||||||
emulator = "box16"
|
emulator = "box16"
|
||||||
extraArgs = listOf("-sym", viceMonListName(programNameWithPath.toString()))
|
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
|
||||||
@ -63,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
zeropage = CX16Zeropage(compilerOptions)
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
golden = GoldenRam(compilerOptions, 0x0600u until 0x0800u)
|
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,9 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val distictFree = free.distinct()
|
val distinctFree = free.distinct()
|
||||||
free.clear()
|
free.clear()
|
||||||
free.addAll(distictFree)
|
free.addAll(distinctFree)
|
||||||
|
|
||||||
removeReservedFromFreePool()
|
removeReservedFromFreePool()
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ dependencies {
|
|||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||||
|
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -311,20 +311,9 @@ private fun optimizeSameAssignments(
|
|||||||
/*
|
/*
|
||||||
sta A ; or stz double store, remove this first one
|
sta A ; or stz double store, remove this first one
|
||||||
sta A ; or stz
|
sta A ; or stz
|
||||||
*/
|
However, this cannot be done relyably because 'A' could be a constant symbol referring to an I/O address.
|
||||||
if(!overlappingMods && first.isStoreRegOrZero() && second.isStoreRegOrZero()) {
|
We can't see that here and would otherwise delete valid double stores.
|
||||||
if(first[2]==second[2]) {
|
*/
|
||||||
val firstvalue = first.substring(4)
|
|
||||||
val secondvalue = second.substring(4)
|
|
||||||
if(firstvalue==secondvalue) {
|
|
||||||
val address = getAddressArg(first, symbolTable)
|
|
||||||
if(address==null || !machine.isIOAddress(address)) {
|
|
||||||
overlappingMods = true
|
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mods
|
return mods
|
||||||
|
@ -4,6 +4,7 @@ import com.github.michaelbull.result.Ok
|
|||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
import com.github.michaelbull.result.mapError
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
@ -19,10 +20,10 @@ internal class AssemblyProgram(
|
|||||||
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
|
||||||
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
|
||||||
private val binFile = outputDir.resolve("$name.bin")
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
private val viceMonListFile = outputDir.resolve(viceMonListName(name))
|
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
|
||||||
private val listFile = outputDir.resolve("$name.list")
|
private val listFile = outputDir.resolve("$name.list")
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
|
|
||||||
val assemblerCommand: List<String>
|
val assemblerCommand: List<String>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.StStaticVariable
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.*
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
@ -30,10 +31,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
|
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
|
||||||
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
||||||
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
||||||
"abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
|
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultToStack, resultRegister)
|
||||||
|
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultToStack, resultRegister)
|
||||||
|
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultToStack, resultRegister)
|
||||||
|
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
|
||||||
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
|
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
|
||||||
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
|
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
|
||||||
"sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
|
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope)
|
||||||
|
"divmod__ubyte" -> funcDivmod(fcall)
|
||||||
|
"divmod__uword" -> funcDivmodW(fcall)
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
"ror" -> funcRor(fcall)
|
"ror" -> funcRor(fcall)
|
||||||
@ -70,12 +76,52 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
"rrestorex" -> funcRrestoreX()
|
"rrestorex" -> funcRrestoreX()
|
||||||
"cmp" -> funcCmp(fcall)
|
"cmp" -> funcCmp(fcall)
|
||||||
"callfar" -> funcCallFar(fcall)
|
"callfar" -> funcCallFar(fcall)
|
||||||
|
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultToStack)
|
||||||
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
return BuiltinFunctions.getValue(fcall.name).returnType
|
return BuiltinFunctions.getValue(fcall.name).returnType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun funcDivmod(fcall: PtBuiltinFunctionCall) {
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.Y, false)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.A ,false)
|
||||||
|
// math.divmod_ub_asm: -- divide A by Y, result quotient in Y, remainder in A (unsigned)
|
||||||
|
asmgen.out(" jsr math.divmod_ub_asm")
|
||||||
|
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||||
|
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||||
|
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||||
|
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||||
|
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false)
|
||||||
|
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
|
||||||
|
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
|
||||||
|
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
|
||||||
|
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
|
||||||
|
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
|
||||||
|
asmgen.out(" jsr math.divmod_uw_asm")
|
||||||
|
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||||
|
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||||
|
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||||
|
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
|
sta $remainderVar
|
||||||
|
sty $remainderVar+1""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
|
||||||
|
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() {
|
private fun funcRsave() {
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -162,27 +208,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
|
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
if(arg1.isSimple()) {
|
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
|
||||||
} else {
|
|
||||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
|
||||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(arg1.isSimple()) {
|
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
|
||||||
} else {
|
|
||||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
|
||||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -208,25 +240,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(arg1.isSimple()) {
|
asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
|
asmgen.out("""
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
asmgen.out("""
|
bne +
|
||||||
cpy P8ZP_SCRATCH_W1+1
|
cmp P8ZP_SCRATCH_W1
|
||||||
bne +
|
+""")
|
||||||
cmp P8ZP_SCRATCH_W1
|
|
||||||
+""")
|
|
||||||
} else {
|
|
||||||
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
|
|
||||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
|
||||||
asmgen.out("""
|
|
||||||
cpy P8ZP_SCRATCH_W1+1
|
|
||||||
bne +
|
|
||||||
cmp P8ZP_SCRATCH_W1
|
|
||||||
+""")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -246,20 +265,41 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||||
val target =
|
val target =
|
||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
|
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null, fcall.position)
|
||||||
else
|
else
|
||||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
translateArguments(fcall, scope)
|
translateArguments(fcall, scope)
|
||||||
if(resultToStack)
|
when(fcall.args[0].type) {
|
||||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
DataType.UBYTE -> {
|
||||||
else {
|
if(resultToStack)
|
||||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_stack")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
else {
|
||||||
|
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
|
||||||
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(resultToStack)
|
||||||
|
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||||
|
else {
|
||||||
|
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||||
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
|
||||||
|
if(resultToStack)
|
||||||
|
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
|
||||||
|
else {
|
||||||
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +333,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr prog8_lib.func_reverse_w""")
|
jsr prog8_lib.func_reverse_w""")
|
||||||
}
|
}
|
||||||
|
DataType.STR -> {
|
||||||
|
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda #$stringLength
|
||||||
|
jsr prog8_lib.func_reverse_b""")
|
||||||
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
@ -302,6 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr floats.func_reverse_f""")
|
jsr floats.func_reverse_f""")
|
||||||
}
|
}
|
||||||
|
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,7 +388,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
lda #$numElements""")
|
lda #$numElements""")
|
||||||
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
||||||
}
|
}
|
||||||
|
DataType.STR -> {
|
||||||
|
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda #$stringLength
|
||||||
|
jsr prog8_lib.func_sort_ub""")
|
||||||
|
}
|
||||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||||
|
in SplitWordArrayTypes -> TODO("split word sort")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -372,6 +434,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
|
if(what.splitWords)
|
||||||
|
TODO("ror2 split words ${what.position}")
|
||||||
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
|
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||||
}
|
}
|
||||||
@ -431,6 +495,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
|
if(what.splitWords)
|
||||||
|
TODO("ror split words ${what.position}")
|
||||||
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
|
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||||
}
|
}
|
||||||
@ -473,6 +539,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
|
if(what.splitWords)
|
||||||
|
TODO("rol2 split words ${what.position}")
|
||||||
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
|
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||||
}
|
}
|
||||||
@ -532,6 +600,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is PtArrayIndexer -> {
|
is PtArrayIndexer -> {
|
||||||
|
if(what.splitWords)
|
||||||
|
TODO("rol split words ${what.position}")
|
||||||
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
|
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||||
}
|
}
|
||||||
@ -547,17 +617,20 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
|
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
|
||||||
|
if(indexer.splitWords)
|
||||||
|
TODO("rol/ror split words access ${indexer.position}")
|
||||||
if(arrayvar.type==DataType.UWORD) {
|
if(arrayvar.type==DataType.UWORD) {
|
||||||
if(dt!='b')
|
if(dt!='b')
|
||||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||||
} else {
|
} else {
|
||||||
|
val p = arrayvar.parent
|
||||||
val addressOf = PtAddressOf(arrayvar.position)
|
val addressOf = PtAddressOf(arrayvar.position)
|
||||||
addressOf.add(arrayvar)
|
addressOf.add(arrayvar)
|
||||||
addressOf.parent = arrayvar.parent.parent
|
addressOf.parent = p
|
||||||
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||||
}
|
}
|
||||||
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
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?) {
|
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
@ -581,7 +654,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,6 +666,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
|
||||||
|
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -600,9 +674,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||||
|
in SplitWordArrayTypes -> TODO("split word any/all")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,21 +686,35 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val dt = fcall.args.single().type
|
val dt = fcall.args.single().type
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> asmgen.out(" ldy #0")
|
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
||||||
DataType.UWORD -> {}
|
|
||||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> {
|
||||||
|
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||||
|
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> asmgen.out(" ldy #0")
|
DataType.BYTE -> {
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY")
|
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
||||||
DataType.UWORD -> {}
|
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")
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||||
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||||
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||||
|
}
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
|
||||||
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,33 +751,39 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
|
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
|
val pointer = result?.first as? PtIdentifier
|
||||||
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
|
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// can do ZP,Y indexing
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
val varname = asmgen.asmVariableName(pointer)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
val scope = fcall.definingISub()!!
|
||||||
val index = (addrExpr.right as PtNumber).number.toHex()
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
asmgen.out("""
|
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||||
ldy #$index
|
asmgen.saveRegisterLocal(CpuRegister.Y, scope)
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.Y)
|
||||||
|
asmgen.out("""
|
||||||
sta ($varname),y
|
sta ($varname),y
|
||||||
txa
|
txa
|
||||||
iny
|
iny
|
||||||
sta ($varname),y""")
|
sta ($varname),y""")
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("wrong pokew arg type")
|
else -> { /* fall through */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD, null)
|
// fall through method:
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
|
||||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
|
fun fallback() {
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||||
|
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||||
|
}
|
||||||
when(val addrExpr = fcall.args[0]) {
|
when(val addrExpr = fcall.args[0]) {
|
||||||
is PtNumber -> {
|
is PtNumber -> {
|
||||||
val addr = addrExpr.number.toHex()
|
val addr = addrExpr.number.toHex()
|
||||||
@ -714,38 +809,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
tay
|
tay
|
||||||
pla""")
|
pla""")
|
||||||
}
|
}
|
||||||
} else {
|
} else fallback()
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is PtBinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
|
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
|
val pointer = result?.first as? PtIdentifier
|
||||||
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
|
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// can do ZP,Y indexing
|
||||||
val index = (addrExpr.right as PtNumber).number.toHex()
|
val varname = asmgen.asmVariableName(pointer)
|
||||||
asmgen.out("""
|
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
|
||||||
ldy #$index
|
asmgen.out("""
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
pha
|
pha
|
||||||
iny
|
iny
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
tay
|
tay
|
||||||
pla""")
|
pla""")
|
||||||
} else {
|
} else fallback()
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
|
||||||
}
|
}
|
||||||
|
else -> fallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
if(resultToStack){
|
if(resultToStack){
|
||||||
@ -764,11 +846,167 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
|
val signed = fcall.type in SignedDatatypes
|
||||||
|
when(fcall.type) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
|
||||||
|
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
|
||||||
|
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
|
||||||
|
if(resultToStack) {
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
} else {
|
||||||
|
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||||
|
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
|
||||||
|
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum
|
||||||
|
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value
|
||||||
|
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
|
||||||
|
if(resultToStack) {
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||||
|
} else {
|
||||||
|
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||||
|
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMin(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
|
val signed = fcall.type in SignedDatatypes
|
||||||
|
if(fcall.type in ByteDatatypes) {
|
||||||
|
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
if(signed) asmgen.out(" bmi +") else asmgen.out(" bcc +")
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
+""")
|
||||||
|
if(resultToStack) {
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
} else {
|
||||||
|
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||||
|
}
|
||||||
|
} else if(fcall.type in WordDatatypes) {
|
||||||
|
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||||
|
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||||
|
if(signed) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
cmp P8ZP_SCRATCH_W2
|
||||||
|
tya
|
||||||
|
sbc P8ZP_SCRATCH_W2+1
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bpl +
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
jmp ++
|
||||||
|
+ lda P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
|
+""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_W1+1
|
||||||
|
cmp P8ZP_SCRATCH_W2+1
|
||||||
|
bcc ++
|
||||||
|
bne +
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
cmp P8ZP_SCRATCH_W2
|
||||||
|
bcc ++
|
||||||
|
+ lda P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
|
jmp ++
|
||||||
|
+ lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
if(resultToStack) {
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||||
|
} else {
|
||||||
|
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("min float not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMax(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
|
val signed = fcall.type in SignedDatatypes
|
||||||
|
if(fcall.type in ByteDatatypes) {
|
||||||
|
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
if(signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
+""")
|
||||||
|
if(resultToStack) {
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
} else {
|
||||||
|
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||||
|
}
|
||||||
|
} else if(fcall.type in WordDatatypes) {
|
||||||
|
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||||
|
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||||
|
if(signed) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
cmp P8ZP_SCRATCH_W2
|
||||||
|
tya
|
||||||
|
sbc P8ZP_SCRATCH_W2+1
|
||||||
|
bvc +
|
||||||
|
eor #$80
|
||||||
|
+ bmi +
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
jmp ++
|
||||||
|
+ lda P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
|
+""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ZP_SCRATCH_W1+1
|
||||||
|
cmp P8ZP_SCRATCH_W2+1
|
||||||
|
bcc ++
|
||||||
|
bne +
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
cmp P8ZP_SCRATCH_W2
|
||||||
|
bcc ++
|
||||||
|
+ lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
jmp ++
|
||||||
|
+ lda P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
if(resultToStack) {
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||||
|
} else {
|
||||||
|
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("max float not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||||
|
asmgen.out(" pha")
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" sta P8ESTACK_LO,x | pla | sta P8ESTACK_HI,x | dex")
|
||||||
} else {
|
} else {
|
||||||
val reg = resultRegister ?: RegisterOrPair.AY
|
val reg = resultRegister ?: RegisterOrPair.AY
|
||||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||||
@ -976,7 +1214,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
val addr = PtAddressOf(value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
addr.add(variable)
|
addr.add(variable)
|
||||||
addr.parent = call
|
addr.parent = call
|
||||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
|
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1002,9 +1240,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, value.position, variableAsmName = varname)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign, scope)
|
||||||
}
|
}
|
||||||
conv.reg != null -> {
|
conv.reg != null -> {
|
||||||
val src = when (conv.dt) {
|
val src = when (conv.dt) {
|
||||||
@ -1020,9 +1258,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
|||||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign, scope)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("callconv")
|
else -> throw AssemblyError("callconv")
|
||||||
}
|
}
|
||||||
|
@ -33,14 +33,19 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
is PtIdentifier -> translateExpression(expression)
|
is PtIdentifier -> translateExpression(expression)
|
||||||
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
|
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||||
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||||
is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
is PtContainmentCheck -> translateContainmentCheck(expression)
|
||||||
is PtArray, is PtString -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
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 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")
|
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")
|
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) {
|
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
|
||||||
// only for use in nested expression evaluation
|
// only for use in nested expression evaluation
|
||||||
|
|
||||||
@ -232,6 +237,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
|
||||||
}
|
}
|
||||||
|
in SplitWordArrayTypes -> {
|
||||||
|
throw AssemblyError("can't push address of split-word array ${expr.position}")
|
||||||
|
}
|
||||||
in IterableDatatypes -> {
|
in IterableDatatypes -> {
|
||||||
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
||||||
}
|
}
|
||||||
@ -241,16 +249,50 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
private fun translateExpression(expr: PtBinaryExpression) {
|
private fun translateExpression(expr: PtBinaryExpression) {
|
||||||
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||||
|
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
|
||||||
|
return
|
||||||
|
|
||||||
val leftDt = expr.left.type
|
val leftDt = expr.left.type
|
||||||
val rightDt = expr.right.type
|
val rightDt = expr.right.type
|
||||||
// see if we can apply some optimized routines still
|
|
||||||
when(expr.operator) {
|
// 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) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val leftVal = expr.left.asConstInteger()
|
val leftVal = left.asConstInteger()
|
||||||
val rightVal = expr.right.asConstInteger()
|
val rightVal = right.asConstInteger()
|
||||||
if (leftVal!=null && leftVal in -4..4) {
|
if (leftVal!=null && leftVal in -4..4) {
|
||||||
translateExpressionInternal(expr.right)
|
translateExpressionInternal(right)
|
||||||
if(rightDt in ByteDatatypes) {
|
if(rightDt in ByteDatatypes) {
|
||||||
val incdec = if(leftVal<0) "dec" else "inc"
|
val incdec = if(leftVal<0) "dec" else "inc"
|
||||||
repeat(leftVal.absoluteValue) {
|
repeat(leftVal.absoluteValue) {
|
||||||
@ -276,11 +318,11 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
else if (rightVal!=null && rightVal in -4..4)
|
else if (rightVal!=null && rightVal in -4..4)
|
||||||
{
|
{
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
val incdec = if(rightVal<0) "dec" else "inc"
|
val incdec = if(rightVal<0) "dec" else "inc"
|
||||||
repeat(rightVal.absoluteValue) {
|
repeat(rightVal.absoluteValue) {
|
||||||
@ -306,16 +348,16 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val rightVal = expr.right.asConstInteger()
|
val rightVal = right.asConstInteger()
|
||||||
if (rightVal!=null && rightVal in -4..4)
|
if (rightVal!=null && rightVal in -4..4)
|
||||||
{
|
{
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
val incdec = if(rightVal<0) "inc" else "dec"
|
val incdec = if(rightVal<0) "inc" else "dec"
|
||||||
repeat(rightVal.absoluteValue) {
|
repeat(rightVal.absoluteValue) {
|
||||||
@ -341,14 +383,14 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
val amount = expr.right.asConstInteger()
|
val amount = right.asConstInteger()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
@ -374,17 +416,17 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
var left = amount
|
var amountLeft = amount
|
||||||
while (left >= 7) {
|
while (amountLeft >= 7) {
|
||||||
asmgen.out(" jsr math.shift_right_uw_7")
|
asmgen.out(" jsr math.shift_right_uw_7")
|
||||||
left -= 7
|
amountLeft -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (amountLeft in 0..2)
|
||||||
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_uw_$left")
|
asmgen.out(" jsr math.shift_right_uw_$amountLeft")
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(amount>=16) {
|
if(amount>=16) {
|
||||||
@ -399,27 +441,27 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
sta P8ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
sta P8ESTACK_HI+1,x
|
sta P8ESTACK_HI+1,x
|
||||||
+""")
|
+""")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
var left = amount
|
var amountLeft = amount
|
||||||
while (left >= 7) {
|
while (amountLeft >= 7) {
|
||||||
asmgen.out(" jsr math.shift_right_w_7")
|
asmgen.out(" jsr math.shift_right_w_7")
|
||||||
left -= 7
|
amountLeft -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (amountLeft in 0..2)
|
||||||
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_w_$left")
|
asmgen.out(" jsr math.shift_right_w_$amountLeft")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
val amount = expr.right.asConstInteger()
|
val amount = right.asConstInteger()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if (leftDt in ByteDatatypes) {
|
if (leftDt in ByteDatatypes) {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||||
@ -429,78 +471,80 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var left = amount
|
var amountLeft = amount
|
||||||
while (left >= 7) {
|
while (amountLeft >= 7) {
|
||||||
asmgen.out(" jsr math.shift_left_w_7")
|
asmgen.out(" jsr math.shift_left_w_7")
|
||||||
left -= 7
|
amountLeft -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (amountLeft in 0..2)
|
||||||
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_left_w_$left")
|
asmgen.out(" jsr math.shift_left_w_$amountLeft")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val leftVar = expr.left as? PtIdentifier
|
val leftVar = left as? PtIdentifier
|
||||||
val rightVar = expr.right as? PtIdentifier
|
val rightVar = right as? PtIdentifier
|
||||||
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
|
if(leftVar!=null && rightVar!=null && leftVar==rightVar) {
|
||||||
return translateSquared(leftVar, leftDt)
|
translateSquared(leftVar, leftDt)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val value = expr.right as? PtNumber
|
val value = right as? PtNumber
|
||||||
if(value!=null) {
|
if(value!=null) {
|
||||||
if(rightDt in IntegerDatatypes) {
|
if(rightDt in IntegerDatatypes) {
|
||||||
val amount = value.number.toInt()
|
val amount = value.number.toInt()
|
||||||
if(amount==2) {
|
if(amount==2) {
|
||||||
// optimize x*2 common case
|
// optimize x*2 common case
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
asmgen.out(" asl P8ESTACK_LO+1,x")
|
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
when(rightDt) {
|
when(rightDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(amount in asmgen.optimizedByteMultiplications) {
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if(amount in asmgen.optimizedByteMultiplications) {
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(amount in asmgen.optimizedWordMultiplications) {
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(amount in asmgen.optimizedWordMultiplications) {
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
@ -510,9 +554,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val rightVal = expr.right.asConstInteger()
|
val rightVal = right.asConstInteger()
|
||||||
if(rightVal!=null && rightVal==2) {
|
if(rightVal!=null && rightVal==2) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(left)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
asmgen.out(" lsr P8ESTACK_LO+1,x")
|
asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||||
@ -545,38 +589,13 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 in ByteDatatypes && rightDt !in ByteDatatypes)
|
return false
|
||||||
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
|
||||||
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
|
||||||
|
|
||||||
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators) {
|
|
||||||
translateCompareStrings(expr.left, expr.operator, expr.right)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 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 translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
|
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
|
||||||
@ -657,7 +676,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
"<=" -> {
|
"<=" -> {
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzeros_b")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzero_sb")
|
||||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
|
||||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
|
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
|
||||||
@ -743,6 +762,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(arrayExpr.splitWords)
|
||||||
|
TODO("split words expression ${arrayExpr.position}")
|
||||||
|
|
||||||
val constIndexNum = arrayExpr.index.asConstInteger()
|
val constIndexNum = arrayExpr.index.asConstInteger()
|
||||||
if(constIndexNum!=null) {
|
if(constIndexNum!=null) {
|
||||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||||
@ -871,9 +893,13 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
|
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
|
||||||
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
|
||||||
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
|
||||||
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||||
|
compareStringsProcessResultInA(operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compareStringsProcessResultInA(operator: String) {
|
||||||
when(operator) {
|
when(operator) {
|
||||||
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
|
||||||
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
|
||||||
|
@ -50,8 +50,36 @@ internal class ForLoopsAsmGen(private val program: PtProgram,
|
|||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||||
|
// pre-check for end already reached
|
||||||
|
if(iterableDt==DataType.ARRAY_B) {
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bpl $endLabel""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bmi $endLabel""")
|
||||||
|
} else {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $varname
|
||||||
|
beq +
|
||||||
|
bcs $endLabel
|
||||||
|
+""")
|
||||||
|
else
|
||||||
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
}
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -68,8 +96,36 @@ $modifiedLabel cmp #0 ; modified
|
|||||||
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||||
|
// pre-check for end already reached
|
||||||
|
if(iterableDt==DataType.ARRAY_B) {
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bpl $endLabel""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sec
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bmi $endLabel""")
|
||||||
|
} else {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
cmp $varname
|
||||||
|
beq +
|
||||||
|
bcs $endLabel
|
||||||
|
+""")
|
||||||
|
else
|
||||||
|
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||||
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
|
}
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
if(stepsize>0) {
|
if(stepsize>0) {
|
||||||
@ -100,8 +156,9 @@ $modifiedLabel cmp #0 ; modified
|
|||||||
|
|
||||||
stepsize == 1 || stepsize == -1 -> {
|
stepsize == 1 || stepsize == -1 -> {
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvarWord(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
@ -134,8 +191,9 @@ $modifiedLabel2 cmp #0 ; modified
|
|||||||
|
|
||||||
// (u)words, step >= 2
|
// (u)words, step >= 2
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvarWord(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
@ -182,31 +240,15 @@ $endLabel""")
|
|||||||
|
|
||||||
// (u)words, step <= -2
|
// (u)words, step <= -2
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvarWord(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
|
|
||||||
if(iterableDt==DataType.ARRAY_UW) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $varname
|
|
||||||
sec
|
|
||||||
sbc #<${stepsize.absoluteValue}
|
|
||||||
sta $varname
|
|
||||||
lda $varname+1
|
|
||||||
sbc #>${stepsize.absoluteValue}
|
|
||||||
sta $varname+1
|
|
||||||
$modifiedLabel cmp #0 ; modified
|
|
||||||
bcc $endLabel
|
|
||||||
bne $loopLabel
|
|
||||||
lda $varname
|
|
||||||
$modifiedLabel2 cmp #0 ; modified
|
|
||||||
bcs $loopLabel
|
|
||||||
$endLabel""")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
sec
|
sec
|
||||||
@ -224,7 +266,6 @@ $modifiedLabel sbc #0 ; modified
|
|||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,6 +275,56 @@ $endLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||||
|
// pre-check for end already reached.
|
||||||
|
// 'to' is in AY, do NOT clobber this!
|
||||||
|
if(iterableDt==DataType.ARRAY_W) {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
sta P8ZP_SCRATCH_W2 ; to
|
||||||
|
sty P8ZP_SCRATCH_W2+1 ; to
|
||||||
|
lda $fromVar
|
||||||
|
cmp P8ZP_SCRATCH_W2
|
||||||
|
lda $fromVar+1
|
||||||
|
sbc P8ZP_SCRATCH_W2+1
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bmi $endLabel
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
cmp $fromVar
|
||||||
|
tya
|
||||||
|
sbc $fromVar+1
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bmi $endLabel
|
||||||
|
lda P8ZP_SCRATCH_REG""")
|
||||||
|
} else {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy $fromVar+1
|
||||||
|
beq +
|
||||||
|
bcc ++
|
||||||
|
bcs $endLabel
|
||||||
|
+ cmp $fromVar
|
||||||
|
bcc +
|
||||||
|
beq +
|
||||||
|
bne $endLabel
|
||||||
|
+""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
cpy $fromVar+1
|
||||||
|
bcc $endLabel
|
||||||
|
bne +
|
||||||
|
cmp $fromVar
|
||||||
|
bcc $endLabel
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
|
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
@ -332,7 +423,46 @@ $loopLabel sty $indexVar
|
|||||||
// allocate index var on ZP if possible
|
// allocate index var on ZP if possible
|
||||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
result.fold(
|
result.fold(
|
||||||
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
asmgen.out("$indexVar .byte 0")
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
|
||||||
|
numElements!!
|
||||||
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
$loopLabel sty $indexVar
|
||||||
|
lda ${iterableName}_lsb,y
|
||||||
|
sta $loopvarName
|
||||||
|
lda ${iterableName}_msb,y
|
||||||
|
sta $loopvarName+1""")
|
||||||
|
asmgen.translate(stmt.statements)
|
||||||
|
if(numElements<=255u) {
|
||||||
|
asmgen.out("""
|
||||||
|
ldy $indexVar
|
||||||
|
iny
|
||||||
|
cpy #$numElements
|
||||||
|
beq $endLabel
|
||||||
|
bne $loopLabel""")
|
||||||
|
} else {
|
||||||
|
// length is 256
|
||||||
|
asmgen.out("""
|
||||||
|
ldy $indexVar
|
||||||
|
iny
|
||||||
|
bne $loopLabel
|
||||||
|
beq $endLabel""")
|
||||||
|
}
|
||||||
|
if(numElements>=16u) {
|
||||||
|
// allocate index var on ZP if possible
|
||||||
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
|
result.fold(
|
||||||
|
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -590,10 +720,9 @@ $loopLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLoopvar(stmt: PtForLoop, range: PtRange) =
|
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =
|
||||||
asmgen.assignExpressionToVariable(
|
asmgen.assignExpressionToVariable(
|
||||||
range.from,
|
range.from,
|
||||||
asmgen.asmVariableName(stmt.variable),
|
asmgen.asmVariableName(stmt.variable),
|
||||||
stmt.variable.type,
|
stmt.variable.type)
|
||||||
stmt.definingISub())
|
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
||||||
asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
|
asmgen.assignExpressionToVariable(value, varName, parameter.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
|
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
|
||||||
@ -164,7 +164,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
if(requiredDt!=value.type)
|
if(requiredDt!=value.type)
|
||||||
throw AssemblyError("for statusflag, byte value is required")
|
throw AssemblyError("for statusflag, byte value is required")
|
||||||
if (statusflag == Statusflag.Pc) {
|
if (statusflag == Statusflag.Pc) {
|
||||||
// this param needs to be set last, right before the jsr
|
// this boolean param needs to be set last, right before the jsr
|
||||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||||
when(value) {
|
when(value) {
|
||||||
is PtNumber -> {
|
is PtNumber -> {
|
||||||
@ -183,12 +183,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out("""
|
asmgen.out(" ror a")
|
||||||
beq +
|
|
||||||
sec
|
|
||||||
bcs ++
|
|
||||||
+ clc
|
|
||||||
+""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else throw AssemblyError("can only use Carry as status flag parameter")
|
} else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
@ -198,16 +193,17 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
register!!
|
register!!
|
||||||
if(requiredDt largerThan value.type) {
|
if(requiredDt largerThan value.type) {
|
||||||
// we need to sign extend the source, do this via temporary word variable
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
|
||||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||||
} else {
|
} else {
|
||||||
|
val scope = value.definingISub()
|
||||||
val target: AsmAssignTarget =
|
val target: AsmAssignTarget =
|
||||||
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, sub, register = register)
|
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||||
else {
|
else {
|
||||||
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
||||||
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
|
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
|
||||||
}
|
}
|
||||||
val src = if(value.type in PassByReferenceDatatypes) {
|
val src = if(value.type in PassByReferenceDatatypes) {
|
||||||
if(value is PtIdentifier) {
|
if(value is PtIdentifier) {
|
||||||
@ -221,7 +217,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY))
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,31 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
|||||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
|
||||||
val elementDt = targetArrayIdx.type
|
val elementDt = targetArrayIdx.type
|
||||||
val constIndex = targetArrayIdx.index.asConstInteger()
|
val constIndex = targetArrayIdx.index.asConstInteger()
|
||||||
|
if(targetArrayIdx.splitWords) {
|
||||||
|
if(constIndex!=null) {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc ${asmArrayvarname}_lsb+$constIndex | bne + | inc ${asmArrayvarname}_msb+$constIndex |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${asmArrayvarname}_lsb+$constIndex
|
||||||
|
bne +
|
||||||
|
dec ${asmArrayvarname}_msb+$constIndex
|
||||||
|
+ dec ${asmArrayvarname}_lsb+$constIndex""")
|
||||||
|
} else {
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${asmArrayvarname}_lsb,x
|
||||||
|
bne +
|
||||||
|
dec ${asmArrayvarname}_msb,x
|
||||||
|
+ dec ${asmArrayvarname}_lsb,x""")
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
@ -74,11 +99,10 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
|||||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $asmArrayvarname+$indexValue
|
lda $asmArrayvarname+$indexValue
|
||||||
bne +
|
bne +
|
||||||
dec $asmArrayvarname+$indexValue+1
|
dec $asmArrayvarname+$indexValue+1
|
||||||
+ dec $asmArrayvarname+$indexValue
|
+ dec $asmArrayvarname+$indexValue""")
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
|
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
|
||||||
@ -89,9 +113,8 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
|
||||||
asmgen.out(" tax")
|
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||||
@ -101,20 +124,20 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
|||||||
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $asmArrayvarname,x
|
lda $asmArrayvarname,x
|
||||||
bne +
|
bne +
|
||||||
dec $asmArrayvarname+1,x
|
dec $asmArrayvarname+1,x
|
||||||
+ dec $asmArrayvarname,x
|
+ dec $asmArrayvarname,x
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #>$asmArrayvarname
|
ldy #>$asmArrayvarname
|
||||||
clc
|
clc
|
||||||
adc #<$asmArrayvarname
|
adc #<$asmArrayvarname
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jsr floats.inc_var_f""")
|
+ jsr floats.inc_var_f""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird array elt dt")
|
else -> throw AssemblyError("weird array elt dt")
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,9 @@ internal class ProgramAndVarsGen(
|
|||||||
internal fun generate() {
|
internal fun generate() {
|
||||||
header()
|
header()
|
||||||
val allBlocks = program.allBlocks()
|
val allBlocks = program.allBlocks()
|
||||||
if(allBlocks.first().name != "main")
|
|
||||||
throw AssemblyError("first block should be 'main'")
|
if(allBlocks.first().name != "p8_main" && allBlocks.first().name != "main")
|
||||||
|
throw AssemblyError("first block should be 'main' or 'p8_main'")
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
program.allBlocks().forEach { block2asm(it) }
|
program.allBlocks().forEach { block2asm(it) }
|
||||||
@ -104,15 +105,15 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("+\t.word 0")
|
asmgen.out("+\t.word 0")
|
||||||
asmgen.out("prog8_entrypoint\t; assembly code starts here")
|
asmgen.out("prog8_entrypoint\t; assembly code starts here")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
asmgen.out(" jsr sys.init_system")
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
asmgen.out(" jsr sys.init_system_phase2")
|
||||||
}
|
}
|
||||||
CbmPrgLauncherType.NONE -> {
|
CbmPrgLauncherType.NONE -> {
|
||||||
asmgen.out("; ---- program without basic sys call ----")
|
asmgen.out("; ---- program without basic sys call ----")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
asmgen.out(" jsr sys.init_system")
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
asmgen.out(" jsr sys.init_system_phase2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,8 +121,8 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("; ---- atari xex program ----")
|
asmgen.out("; ---- atari xex program ----")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
asmgen.out(" jsr sys.init_system")
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
asmgen.out(" jsr sys.init_system_phase2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,25 +140,24 @@ internal class ProgramAndVarsGen(
|
|||||||
"cx16" -> {
|
"cx16" -> {
|
||||||
if(options.floats)
|
if(options.floats)
|
||||||
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||||
asmgen.out(" jsr main.start")
|
asmgen.out(" jsr p8_main.p8_start")
|
||||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||||
}
|
}
|
||||||
"c64" -> {
|
"c64" -> {
|
||||||
asmgen.out(" jsr main.start | lda #31 | sta $01")
|
asmgen.out(" jsr p8_main.p8_start | lda #31 | sta $01")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||||
else
|
else
|
||||||
asmgen.out(" rts")
|
asmgen.out(" rts")
|
||||||
}
|
}
|
||||||
"c128" -> {
|
"c128" -> {
|
||||||
asmgen.out(" jsr main.start")
|
asmgen.out(" jsr p8_main.p8_start | lda #0 | sta ${"$"}ff00")
|
||||||
// TODO c128: how to bank basic+kernal back in?
|
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
asmgen.out(" jmp sys.cleanup_at_exit")
|
||||||
else
|
else
|
||||||
asmgen.out(" rts")
|
asmgen.out(" rts")
|
||||||
}
|
}
|
||||||
else -> asmgen.jmp("main.start")
|
else -> asmgen.jmp("p8_main.p8_start")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +176,8 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
private fun footer() {
|
private fun footer() {
|
||||||
asmgen.out("; bss sections")
|
asmgen.out("; bss sections")
|
||||||
if(options.varsHigh) {
|
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
|
||||||
|
if(options.varsHighBank!=null) {
|
||||||
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
|
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
|
||||||
throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
|
throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
|
||||||
}
|
}
|
||||||
@ -312,7 +313,9 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
asmgen.out("${sub.name}\t$asmStartScope")
|
asmgen.out("${sub.name}\t$asmStartScope")
|
||||||
|
|
||||||
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
val scope = symboltable.lookupOrElse(sub.scopedName) {
|
||||||
|
throw AssemblyError("lookup ${sub.scopedName}")
|
||||||
|
}
|
||||||
require(scope.type==StNodeType.SUBROUTINE)
|
require(scope.type==StNodeType.SUBROUTINE)
|
||||||
val varsInSubroutine = getVars(scope)
|
val varsInSubroutine = getVars(scope)
|
||||||
|
|
||||||
@ -332,14 +335,14 @@ internal class ProgramAndVarsGen(
|
|||||||
asmsubs2asm(sub.children)
|
asmsubs2asm(sub.children)
|
||||||
|
|
||||||
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||||
if(sub.name=="start" && sub.definingBlock()!!.name=="main")
|
if((sub.name=="start" || sub.name=="p8_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8_main"))
|
||||||
entrypointInitialization()
|
entrypointInitialization()
|
||||||
|
|
||||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
||||||
asmgen.out("; simple int arg(s) passed via register(s)")
|
asmgen.out("; simple int arg(s) passed via register(s)")
|
||||||
if(sub.parameters.size==1) {
|
if(sub.parameters.size==1) {
|
||||||
val dt = sub.parameters[0].type
|
val dt = sub.parameters[0].type
|
||||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||||
if(dt in ByteDatatypes)
|
if(dt in ByteDatatypes)
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||||
else
|
else
|
||||||
@ -347,8 +350,8 @@ internal class ProgramAndVarsGen(
|
|||||||
} else {
|
} else {
|
||||||
require(sub.parameters.size==2)
|
require(sub.parameters.size==2)
|
||||||
// 2 simple byte args, first in A, second in Y
|
// 2 simple byte args, first in A, second in Y
|
||||||
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
|
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
|
||||||
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
|
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name)
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target1)
|
asmgen.assignRegister(RegisterOrPair.A, target1)
|
||||||
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
||||||
}
|
}
|
||||||
@ -358,26 +361,29 @@ internal class ProgramAndVarsGen(
|
|||||||
sub.children.forEach { asmgen.translate(it) }
|
sub.children.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
asmgen.out("; variables")
|
asmgen.out("; variables")
|
||||||
|
asmgen.out(" .section BSS")
|
||||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||||
if(addr!=null)
|
if(addr!=null)
|
||||||
asmgen.out("$name = $addr")
|
asmgen.out("$name = $addr")
|
||||||
else when(dt) {
|
else when(dt) {
|
||||||
DataType.UBYTE -> asmgen.out("$name .byte 0")
|
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||||
DataType.UWORD -> asmgen.out("$name .word 0")
|
DataType.UWORD -> asmgen.out("$name .word ?")
|
||||||
else -> throw AssemblyError("weird dt")
|
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
|
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
||||||
asmgen.out("prog8_regsaveA .byte 0")
|
asmgen.out("prog8_regsaveA .byte ?")
|
||||||
if(asmGenInfo.usedRegsaveX)
|
if(asmGenInfo.usedRegsaveX)
|
||||||
asmgen.out("prog8_regsaveX .byte 0")
|
asmgen.out("prog8_regsaveX .byte ?")
|
||||||
if(asmGenInfo.usedRegsaveY)
|
if(asmGenInfo.usedRegsaveY)
|
||||||
asmgen.out("prog8_regsaveY .byte 0")
|
asmgen.out("prog8_regsaveY .byte ?")
|
||||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
|
asmgen.out(" .send BSS")
|
||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInSubroutine
|
val variables = varsInSubroutine
|
||||||
@ -478,8 +484,8 @@ internal class ProgramAndVarsGen(
|
|||||||
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val scopedName = variable.key
|
val scopedName = variable.key
|
||||||
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
||||||
if(svar.onetimeInitializationStringValue!=null)
|
if(svar?.onetimeInitializationStringValue!=null)
|
||||||
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
|
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -490,8 +496,8 @@ internal class ProgramAndVarsGen(
|
|||||||
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val scopedName = variable.key
|
val scopedName = variable.key
|
||||||
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
val svar = symboltable.lookup(scopedName) as? StStaticVariable
|
||||||
if(svar.onetimeInitializationArrayValue!=null)
|
if(svar?.onetimeInitializationArrayValue!=null)
|
||||||
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
|
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -541,6 +547,11 @@ internal class ProgramAndVarsGen(
|
|||||||
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
||||||
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
||||||
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
|
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
|
in SplitWordArrayTypes -> {
|
||||||
|
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
|
||||||
|
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
|
||||||
|
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
|
||||||
|
}
|
||||||
in ArrayDatatypes -> {
|
in ArrayDatatypes -> {
|
||||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||||
@ -625,6 +636,18 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .sint " + chunk.joinToString())
|
asmgen.out(" .sint " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DataType.ARRAY_UW_SPLIT -> {
|
||||||
|
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||||
|
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||||
|
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||||
|
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W_SPLIT -> {
|
||||||
|
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||||
|
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||||
|
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||||
|
asmgen.out("${varname}_msb\t.byte >_array_$varname")
|
||||||
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||||
val floatFills = array.map {
|
val floatFills = array.map {
|
||||||
@ -684,7 +707,7 @@ internal class ProgramAndVarsGen(
|
|||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
"$"+number.toString(16).padStart(2, '0')
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> array.map {
|
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
||||||
if(it.number!=null) {
|
if(it.number!=null) {
|
||||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||||
}
|
}
|
||||||
@ -716,11 +739,11 @@ internal class ProgramAndVarsGen(
|
|||||||
else
|
else
|
||||||
"-$$hexnum"
|
"-$$hexnum"
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> array.map {
|
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
|
||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
"$" + number.toString(16).padStart(4, '0')
|
"$" + number.toString(16).padStart(4, '0')
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> array.map {
|
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
|
||||||
val number = it.number!!.toInt()
|
val number = it.number!!.toInt()
|
||||||
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||||
if(number>=0)
|
if(number>=0)
|
||||||
|
@ -28,6 +28,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
private val asmgen: AsmGen6502Internal,
|
private val asmgen: AsmGen6502Internal,
|
||||||
val datatype: DataType,
|
val datatype: DataType,
|
||||||
val scope: IPtSubroutine?,
|
val scope: IPtSubroutine?,
|
||||||
|
val position: Position,
|
||||||
private val variableAsmName: String? = null,
|
private val variableAsmName: String? = null,
|
||||||
val array: PtArrayIndexer? = null,
|
val array: PtArrayIndexer? = null,
|
||||||
val memory: PtMemoryByte? = null,
|
val memory: PtMemoryByte? = null,
|
||||||
@ -43,8 +44,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
asmgen.asmVariableName(array.variable)
|
asmgen.asmVariableName(array.variable)
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var origAssign: AsmAssignmentBase
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(register!=null && datatype !in NumericDatatypes)
|
if(register!=null && datatype !in NumericDatatypes)
|
||||||
throw AssemblyError("register must be integer or float type")
|
throw AssemblyError("register must be integer or float type")
|
||||||
@ -63,28 +62,28 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
if(reg.statusflag!=null)
|
if(reg.statusflag!=null)
|
||||||
throw AssemblyError("can't assign value to processor statusflag directly")
|
throw AssemblyError("can't assign value to processor statusflag directly")
|
||||||
else
|
else
|
||||||
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, register=reg.registerOrPair, origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, target.position, register=reg.registerOrPair, origAstTarget = this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, target.position, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||||
}
|
}
|
||||||
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, array = array, origAstTarget = this)
|
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
|
||||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, memory = memory, origAstTarget = this)
|
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget =
|
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, pos: Position, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget =
|
||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
|
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers)
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, register = registers)
|
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||||
RegisterOrPair.R0,
|
RegisterOrPair.R0,
|
||||||
RegisterOrPair.R1,
|
RegisterOrPair.R1,
|
||||||
RegisterOrPair.R2,
|
RegisterOrPair.R2,
|
||||||
@ -100,9 +99,31 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R12,
|
RegisterOrPair.R12,
|
||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isSameAs(left: PtExpression): Boolean =
|
||||||
|
when(kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
val scopedName: String = if('.' in asmVarname)
|
||||||
|
asmVarname
|
||||||
|
else {
|
||||||
|
val scopeName = (scope as? PtNamedNode)?.scopedName
|
||||||
|
if (scopeName == null) asmVarname else "$scopeName.$asmVarname"
|
||||||
|
}
|
||||||
|
left is PtIdentifier && left.name==scopedName
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
left isSameAs memory!!
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER, TargetStorageKind.STACK -> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignSource(val kind: SourceStorageKind,
|
internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||||
@ -157,8 +178,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
|
||||||
}
|
}
|
||||||
is PtFunctionCall -> {
|
is PtFunctionCall -> {
|
||||||
val symbol = asmgen.symbolTable.lookup(value.name)
|
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||||
val sub = symbol!!.astNode as IPtSubroutine
|
val sub = symbol.astNode as IPtSubroutine
|
||||||
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -34,11 +34,19 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
val warnings = mutableListOf<String>()
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
override fun err(msg: String, position: Position) {
|
override fun err(msg: String, position: Position) {
|
||||||
errors.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in errors)
|
||||||
|
errors.add(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun warn(msg: String, position: Position) {
|
override fun warn(msg: String, position: Position) {
|
||||||
warnings.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in warnings)
|
||||||
|
warnings.add(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun undefined(symbol: List<String>, position: Position) {
|
||||||
|
err("undefined symbol: ${symbol.joinToString(".")}", position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8tests.codegencpu6502
|
package prog8tests.codegencpu6502
|
||||||
|
|
||||||
|
import io.kotest.assertions.throwables.shouldNotThrowAny
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
@ -39,9 +40,9 @@ class TestCodegen: FunSpec({
|
|||||||
// xx += cx16.r0
|
// xx += cx16.r0
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
val codegen = AsmGen6502()
|
val codegen = AsmGen6502(prefixSymbols = false)
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||||
@ -91,7 +92,7 @@ class TestCodegen: FunSpec({
|
|||||||
program.add(block)
|
program.add(block)
|
||||||
|
|
||||||
// define the "cx16.r0" virtual register
|
// define the "cx16.r0" virtual register
|
||||||
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||||
program.add(cx16block)
|
program.add(cx16block)
|
||||||
|
|
||||||
@ -102,5 +103,14 @@ class TestCodegen: FunSpec({
|
|||||||
result.name shouldBe "test"
|
result.name shouldBe "test"
|
||||||
Files.deleteIfExists(Path("${result.name}.asm"))
|
Files.deleteIfExists(Path("${result.name}.asm"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("64tass assembler available? - if this fails you need to install 64tass in the path") {
|
||||||
|
val command = mutableListOf("64tass", "--version")
|
||||||
|
shouldNotThrowAny {
|
||||||
|
val proc = ProcessBuilder(command).inheritIO().start()
|
||||||
|
val result = proc.waitFor()
|
||||||
|
result.shouldBe(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ dependencies {
|
|||||||
implementation project(':codeGenIntermediate')
|
implementation project(':codeGenIntermediate')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
options: CompilationOptions,
|
options: CompilationOptions,
|
||||||
errors: IErrorReporter
|
errors: IErrorReporter
|
||||||
): IAssemblyProgram? {
|
): IAssemblyProgram? {
|
||||||
|
|
||||||
// you could write a code generator directly on the PtProgram AST,
|
// you could write a code generator directly on the PtProgram AST,
|
||||||
// but you can also use the Intermediate Representation to build a codegen on:
|
// but you can also use the Intermediate Representation to build a codegen on:
|
||||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
@ -25,6 +26,15 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
|||||||
IRFileWriter(irProgram, null).write()
|
IRFileWriter(irProgram, null).write()
|
||||||
|
|
||||||
println("** experimental codegen stub: no assembly generated **")
|
println("** experimental codegen stub: no assembly generated **")
|
||||||
return null
|
return EmptyProgram
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object EmptyProgram : IAssemblyProgram {
|
||||||
|
override val name = "<Empty Program>"
|
||||||
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
|
println("** nothing assembled **")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -28,9 +28,9 @@ dependencies {
|
|||||||
implementation project(':intermediate')
|
implementation project(':intermediate')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||||
|
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
@ -16,32 +16,28 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
return translateRegularAssign(assignment)
|
return translateRegularAssign(assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks {
|
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
|
||||||
if(augmentedAssign.target.children.single() is PtMachineRegister)
|
if(augAssign.target.children.single() is PtMachineRegister)
|
||||||
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||||
|
|
||||||
return translateInplaceAssign(augmentedAssign)
|
val ident = augAssign.target.identifier
|
||||||
}
|
val memory = augAssign.target.memory
|
||||||
|
val array = augAssign.target.array
|
||||||
private fun translateInplaceAssign(assignment: PtAugmentedAssign): IRCodeChunks {
|
|
||||||
val ident = assignment.target.identifier
|
|
||||||
val memory = assignment.target.memory
|
|
||||||
val array = assignment.target.array
|
|
||||||
|
|
||||||
return if(ident!=null) {
|
return if(ident!=null) {
|
||||||
assignVarAugmented(ident.name, assignment)
|
assignVarAugmented(ident.name, augAssign)
|
||||||
} else if(memory != null) {
|
} else if(memory != null) {
|
||||||
if(memory.address is PtNumber)
|
if(memory.address is PtNumber)
|
||||||
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment)
|
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
|
||||||
else
|
else
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(augAssign)
|
||||||
} else if(array!=null) {
|
} else if(array!=null) {
|
||||||
// NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
|
// NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
|
||||||
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
|
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
|
||||||
// will be optimized later and have the double assignments removed.
|
// will be optimized later and have the double assignments removed.
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(augAssign)
|
||||||
} else {
|
} else {
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(augAssign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +46,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
assignment: PtAugmentedAssign
|
assignment: PtAugmentedAssign
|
||||||
): IRCodeChunks {
|
): IRCodeChunks {
|
||||||
val value = assignment.value
|
val value = assignment.value
|
||||||
val vmDt = codeGen.irType(value.type)
|
val vmDt = irType(value.type)
|
||||||
return when(assignment.operator) {
|
return when(assignment.operator) {
|
||||||
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
||||||
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
||||||
@ -61,25 +57,40 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
||||||
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
||||||
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, 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)
|
||||||
|
"<" -> expressionEval.operatorLessInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
|
">" -> expressionEval.operatorGreaterInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
|
"<=" -> expressionEval.operatorLessEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
|
">=" -> expressionEval.operatorGreaterEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
||||||
|
|
||||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
||||||
val value = assignment.value
|
val value = assignment.value
|
||||||
val valueVmDt = codeGen.irType(value.type)
|
val targetDt = irType(assignment.target.type)
|
||||||
return when (assignment.operator) {
|
return when (assignment.operator) {
|
||||||
"+=" -> expressionEval.operatorPlusInplace(null, symbol, valueVmDt, value)
|
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
|
||||||
"-=" -> expressionEval.operatorMinusInplace(null, symbol, valueVmDt, value)
|
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
|
||||||
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, valueVmDt, value)
|
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, targetDt, value)
|
||||||
"/=" -> expressionEval.operatorDivideInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
"/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
"|=" -> expressionEval.operatorOrInplace(null, symbol, valueVmDt, value)
|
"|=" -> expressionEval.operatorOrInplace(null, symbol, targetDt, value)
|
||||||
"&=" -> expressionEval.operatorAndInplace(null, symbol, valueVmDt, value)
|
"&=" -> expressionEval.operatorAndInplace(null, symbol, targetDt, value)
|
||||||
"^=" -> expressionEval.operatorXorInplace(null, symbol, valueVmDt, value)
|
"^=" -> expressionEval.operatorXorInplace(null, symbol, targetDt, value)
|
||||||
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, valueVmDt, value)
|
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value)
|
||||||
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol)
|
"%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value)
|
||||||
|
"==" -> expressionEval.operatorEqualsInplace(null, symbol, targetDt, value)
|
||||||
|
"!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, value)
|
||||||
|
"<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
"<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
|
||||||
|
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, null, symbol)
|
||||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,8 +98,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||||
if (codeGen.options.slowCodegenWarnings)
|
if (codeGen.options.slowCodegenWarnings)
|
||||||
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
|
||||||
val normalAssign = PtAssignment(origAssign.position)
|
|
||||||
normalAssign.add(origAssign.target)
|
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
if(origAssign.operator in PrefixOperators) {
|
if(origAssign.operator in PrefixOperators) {
|
||||||
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||||
@ -100,6 +109,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
value.add(left)
|
value.add(left)
|
||||||
value.add(origAssign.value)
|
value.add(origAssign.value)
|
||||||
}
|
}
|
||||||
|
val normalAssign = PtAssignment(origAssign.position)
|
||||||
|
normalAssign.add(origAssign.target)
|
||||||
normalAssign.add(value)
|
normalAssign.add(value)
|
||||||
return translateRegularAssign(normalAssign)
|
return translateRegularAssign(normalAssign)
|
||||||
}
|
}
|
||||||
@ -110,16 +121,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
"+" -> { }
|
"+" -> { }
|
||||||
"-" -> {
|
"-" -> {
|
||||||
code += if(address!=null)
|
code += if(address!=null)
|
||||||
IRInstruction(Opcode.NEGM, vmDt, value = address)
|
IRInstruction(Opcode.NEGM, vmDt, address = address)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
"~" -> {
|
"~" -> {
|
||||||
val regMask = codeGen.registers.nextFree()
|
val regMask = codeGen.registers.nextFree()
|
||||||
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = mask)
|
||||||
code += if(address!=null)
|
code += if(address!=null)
|
||||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = address)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
@ -130,119 +141,177 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
|
|
||||||
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
||||||
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
|
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
|
||||||
val ident = assignment.target.identifier
|
val targetIdent = assignment.target.identifier
|
||||||
val memory = assignment.target.memory
|
val targetMemory = assignment.target.memory
|
||||||
val array = assignment.target.array
|
val targetArray = assignment.target.array
|
||||||
val vmDt = codeGen.irType(assignment.value.type)
|
val valueDt = irType(assignment.value.type)
|
||||||
|
val targetDt = irType(assignment.target.type)
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
var resultRegister = -1
|
|
||||||
var resultFpRegister = -1
|
var valueRegister = -1
|
||||||
|
var valueFpRegister = -1
|
||||||
val zero = codeGen.isZero(assignment.value)
|
val zero = codeGen.isZero(assignment.value)
|
||||||
if(!zero) {
|
if(!zero) {
|
||||||
// calculate the assignment value
|
// calculate the assignment value
|
||||||
if (vmDt == IRDataType.FLOAT) {
|
if (valueDt == IRDataType.FLOAT) {
|
||||||
resultFpRegister = codeGen.registers.nextFreeFloat()
|
val tr = expressionEval.translateExpression(assignment.value)
|
||||||
result += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
|
valueFpRegister = tr.resultFpReg
|
||||||
|
addToResult(result, tr, -1, valueFpRegister)
|
||||||
} else {
|
} else {
|
||||||
resultRegister = if (assignment.value is PtMachineRegister) {
|
val extendByteToWord = if(targetDt != valueDt) {
|
||||||
(assignment.value as PtMachineRegister).register
|
// usually an error EXCEPT when a byte is assigned to a word.
|
||||||
|
if(targetDt==IRDataType.WORD && valueDt==IRDataType.BYTE)
|
||||||
|
true
|
||||||
|
else
|
||||||
|
throw AssemblyError("assignment value and target dt mismatch")
|
||||||
|
} else false
|
||||||
|
if (assignment.value is PtMachineRegister) {
|
||||||
|
valueRegister = (assignment.value as PtMachineRegister).register
|
||||||
|
if(extendByteToWord) {
|
||||||
|
valueRegister = codeGen.registers.nextFree()
|
||||||
|
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister, reg2=(assignment.value as PtMachineRegister).register), null)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val reg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(assignment.value)
|
||||||
result += expressionEval.translateExpression(assignment.value, reg, -1)
|
valueRegister = tr.resultReg
|
||||||
reg
|
addToResult(result, tr, valueRegister, -1)
|
||||||
|
if(extendByteToWord) {
|
||||||
|
valueRegister = codeGen.registers.nextFree()
|
||||||
|
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
|
||||||
|
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ident!=null) {
|
|
||||||
|
if(targetIdent!=null) {
|
||||||
val instruction = if(zero) {
|
val instruction = if(zero) {
|
||||||
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = ident.name)
|
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||||
} else {
|
} else {
|
||||||
if (vmDt == IRDataType.FLOAT)
|
if (targetDt == IRDataType.FLOAT)
|
||||||
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = ident.name)
|
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = ident.name)
|
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(null, null).also { it += instruction }
|
result += IRCodeChunk(null, null).also { it += instruction }
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else if(array!=null) {
|
else if(targetArray!=null) {
|
||||||
val variable = array.variable.name
|
val variable = targetArray.variable.name
|
||||||
val itemsize = codeGen.program.memsizer.memorySize(array.type)
|
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
|
||||||
|
|
||||||
if(array.variable.type==DataType.UWORD) {
|
if(targetArray.variable.type==DataType.UWORD) {
|
||||||
// indexing a pointer var instead of a real array or string
|
// indexing a pointer var instead of a real array or string
|
||||||
if(itemsize!=1)
|
if(itemsize!=1)
|
||||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
if(array.index.type!=DataType.UBYTE)
|
if(targetArray.index.type!=DataType.UBYTE)
|
||||||
throw AssemblyError("non-array var indexing requires bytes index")
|
throw AssemblyError("non-array var indexing requires bytes index")
|
||||||
val idxReg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(targetArray.index)
|
||||||
result += expressionEval.translateExpression(array.index, idxReg, -1)
|
val idxReg = tr.resultReg
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
if(zero) {
|
if(zero) {
|
||||||
// there's no STOREZIX instruction
|
// there's no STOREZIX instruction
|
||||||
resultRegister = codeGen.registers.nextFree()
|
valueRegister = codeGen.registers.nextFree()
|
||||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
|
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
|
||||||
}
|
}
|
||||||
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
|
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
||||||
result += code
|
result += code
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
val fixedIndex = constIntValue(array.index)
|
val fixedIndex = constIntValue(targetArray.index)
|
||||||
|
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
|
||||||
if(zero) {
|
if(zero) {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
val chunk = IRCodeChunk(null, null).also {
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
|
if(targetArray.splitWords) {
|
||||||
|
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||||
|
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
||||||
|
}
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val indexReg = codeGen.registers.nextFree()
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += loadIndexReg(array, itemsize, indexReg)
|
result += code
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
|
result += IRCodeChunk(null, null).also {
|
||||||
|
if(targetArray.splitWords) {
|
||||||
|
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
|
||||||
|
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(vmDt== IRDataType.FLOAT) {
|
if(targetDt== IRDataType.FLOAT) {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
val offset = fixedIndex*itemsize
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") }
|
val chunk = IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
|
||||||
|
}
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val indexReg = codeGen.registers.nextFree()
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += loadIndexReg(array, itemsize, indexReg)
|
result += code
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = resultFpRegister, labelSymbol = variable) }
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
val chunk = IRCodeChunk(null, null).also {
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") }
|
if(targetArray.splitWords) {
|
||||||
|
val msbReg = codeGen.registers.nextFree()
|
||||||
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
||||||
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
||||||
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
||||||
|
}
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val indexReg = codeGen.registers.nextFree()
|
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
|
||||||
result += loadIndexReg(array, itemsize, indexReg)
|
result += code
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
|
result += IRCodeChunk(null, null).also {
|
||||||
|
if(targetArray.splitWords) {
|
||||||
|
val msbReg = codeGen.registers.nextFree()
|
||||||
|
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||||
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
||||||
|
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
else if(memory!=null) {
|
else if(targetMemory!=null) {
|
||||||
require(vmDt== IRDataType.BYTE) { "must be byte type ${memory.position}"}
|
require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
||||||
if(zero) {
|
if(zero) {
|
||||||
if(memory.address is PtNumber) {
|
if(targetMemory.address is PtNumber) {
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||||
result += expressionEval.translateExpression(memory.address, addressReg, -1)
|
val addressReg = tr.resultReg
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(memory.address is PtNumber) {
|
if(targetMemory.address is PtNumber) {
|
||||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
|
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||||
result += expressionEval.translateExpression(memory.address, addressReg, -1)
|
val addressReg = tr.resultReg
|
||||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,14 +321,22 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
throw AssemblyError("weird assigntarget")
|
throw AssemblyError("weird assigntarget")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): IRCodeChunks {
|
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
|
||||||
return if(itemsize==1) {
|
// returns the code to load the Index into the register, which is also returned.
|
||||||
expressionEval.translateExpression(array.index, indexReg, -1)
|
|
||||||
} else {
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
if(itemsize==1 || array.splitWords) {
|
||||||
mult.children += array.index
|
val tr = expressionEval.translateExpression(array.index)
|
||||||
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
expressionEval.translateExpression(mult, indexReg, -1)
|
return Pair(result, tr.resultReg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val mult: PtExpression
|
||||||
|
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||||
|
mult.children += array.index
|
||||||
|
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
|
||||||
|
val tr = expressionEval.translateExpression(mult)
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
return Pair(result, tr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,25 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
import prog8.code.StStaticVariable
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.AssemblyError
|
import prog8.code.core.AssemblyError
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
|
import prog8.code.core.SignedDatatypes
|
||||||
|
import prog8.code.core.SplitWordArrayTypes
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
|
||||||
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
|
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
|
||||||
|
|
||||||
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
return when(call.name) {
|
return when(call.name) {
|
||||||
"any" -> funcAny(call, resultRegister)
|
"any" -> funcAny(call)
|
||||||
"all" -> funcAll(call, resultRegister)
|
"all" -> funcAll(call)
|
||||||
"abs" -> funcAbs(call, resultRegister)
|
"abs__byte", "abs__word", "abs__float" -> funcAbs(call)
|
||||||
"cmp" -> funcCmp(call)
|
"cmp" -> funcCmp(call)
|
||||||
"sgn" -> funcSgn(call, resultRegister)
|
"sgn" -> funcSgn(call)
|
||||||
"sqrt16" -> funcSqrt16(call, resultRegister)
|
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
|
||||||
|
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
|
||||||
|
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
|
||||||
"pop" -> funcPop(call)
|
"pop" -> funcPop(call)
|
||||||
"popw" -> funcPopw(call)
|
"popw" -> funcPopw(call)
|
||||||
"push" -> funcPush(call)
|
"push" -> funcPush(call)
|
||||||
@ -24,44 +27,100 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
"rsave",
|
"rsave",
|
||||||
"rsavex",
|
"rsavex",
|
||||||
"rrestore",
|
"rrestore",
|
||||||
"rrestorex" -> emptyList() // vm doesn't have registers to save/restore
|
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
|
||||||
"callfar" -> throw AssemblyError("callfar() is for cx16 target only")
|
"callfar" -> funcCallfar(call)
|
||||||
"msb" -> funcMsb(call, resultRegister)
|
"msb" -> funcMsb(call)
|
||||||
"lsb" -> funcLsb(call, resultRegister)
|
"lsb" -> funcLsb(call)
|
||||||
"memory" -> funcMemory(call, resultRegister)
|
"memory" -> funcMemory(call)
|
||||||
"peek" -> funcPeek(call, resultRegister)
|
"peek" -> funcPeek(call)
|
||||||
"peekw" -> funcPeekW(call, resultRegister)
|
"peekw" -> funcPeekW(call)
|
||||||
"poke" -> funcPoke(call)
|
"poke" -> funcPoke(call)
|
||||||
"pokew" -> funcPokeW(call)
|
"pokew" -> funcPokeW(call)
|
||||||
"pokemon" -> emptyList()
|
"pokemon" -> ExpressionCodeResult.EMPTY // easter egg function
|
||||||
"mkword" -> funcMkword(call, resultRegister)
|
"mkword" -> funcMkword(call)
|
||||||
|
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
|
||||||
|
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
|
||||||
|
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
|
||||||
"sort" -> funcSort(call)
|
"sort" -> funcSort(call)
|
||||||
"reverse" -> funcReverse(call)
|
"reverse" -> funcReverse(call)
|
||||||
"rol" -> funcRolRor(Opcode.ROXL, call, resultRegister)
|
"rol" -> funcRolRor(Opcode.ROXL, call)
|
||||||
"ror" -> funcRolRor(Opcode.ROXR, call, resultRegister)
|
"ror" -> funcRolRor(Opcode.ROXR, call)
|
||||||
"rol2" -> funcRolRor(Opcode.ROL, call, resultRegister)
|
"rol2" -> funcRolRor(Opcode.ROL, call)
|
||||||
"ror2" -> funcRolRor(Opcode.ROR, call, resultRegister)
|
"ror2" -> funcRolRor(Opcode.ROR, call)
|
||||||
|
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val leftRegister = codeGen.registers.nextFree()
|
|
||||||
val rightRegister = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], leftRegister, -1)
|
val bankTr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[1], rightRegister, -1)
|
val addressTr = exprGen.translateExpression(call.args[1])
|
||||||
result += IRCodeChunk(null, null).also {
|
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||||
it += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
|
addToResult(result, bankTr, bankTr.resultReg, -1)
|
||||||
}
|
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||||
return result
|
addToResult(result, argumentwordTr, argumentwordTr.resultReg, -1)
|
||||||
|
result += codeGen.makeSyscall(IMSyscall.CALLFAR, listOf(IRDataType.BYTE to bankTr.resultReg, IRDataType.WORD to addressTr.resultReg, IRDataType.WORD to argumentwordTr.resultReg), IRDataType.WORD to argumentwordTr.resultReg)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.WORD, argumentwordTr.resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val number = call.args[0]
|
||||||
|
val divident = call.args[1]
|
||||||
|
val divisionReg: Int
|
||||||
|
val remainderReg: Int
|
||||||
|
if(divident is PtNumber) {
|
||||||
|
val tr = exprGen.translateExpression(number)
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null)
|
||||||
|
divisionReg = tr.resultReg
|
||||||
|
remainderReg = codeGen.registers.nextFree()
|
||||||
|
} else {
|
||||||
|
val numTr = exprGen.translateExpression(number)
|
||||||
|
addToResult(result, numTr, numTr.resultReg, -1)
|
||||||
|
val dividentTr = exprGen.translateExpression(divident)
|
||||||
|
addToResult(result, dividentTr, dividentTr.resultReg, -1)
|
||||||
|
addInstr(result, IRInstruction(Opcode.DIVMODR, type, reg1 = numTr.resultReg, reg2=dividentTr.resultReg), null)
|
||||||
|
divisionReg = numTr.resultReg
|
||||||
|
remainderReg = dividentTr.resultReg
|
||||||
|
}
|
||||||
|
// DIVMOD result convention: on value stack, division and remainder on top.
|
||||||
|
addInstr(result, IRInstruction(Opcode.POP, type, reg1=remainderReg), null)
|
||||||
|
addInstr(result, IRInstruction(Opcode.POP, type, reg1=divisionReg), null)
|
||||||
|
result += assignRegisterTo(call.args[2], divisionReg)
|
||||||
|
result += assignRegisterTo(call.args[3], remainderReg)
|
||||||
|
return ExpressionCodeResult(result, type, -1, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val left = exprGen.translateExpression(call.args[0])
|
||||||
|
val right = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, left, left.resultReg, -1)
|
||||||
|
addToResult(result, right, right.resultReg, -1)
|
||||||
|
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val leftTr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
|
val rightTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
|
val dt = irType(call.args[0].type)
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.CMP, dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
|
||||||
|
}
|
||||||
|
return ExpressionCodeResult(result, dt, leftTr.resultReg, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||||
val syscall =
|
val syscall =
|
||||||
when (array.dt) {
|
when (arrayName.type) {
|
||||||
DataType.ARRAY_UB,
|
DataType.ARRAY_UB,
|
||||||
DataType.ARRAY_B -> IMSyscall.ANY_BYTE
|
DataType.ARRAY_B -> IMSyscall.ANY_BYTE
|
||||||
DataType.ARRAY_UW,
|
DataType.ARRAY_UW,
|
||||||
@ -70,21 +129,19 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
else -> throw IllegalArgumentException("weird type")
|
else -> throw IllegalArgumentException("weird type")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += IRCodeChunk(null, null).also {
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
val lengthReg = codeGen.registers.nextFree()
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||||
if(resultRegister!=0)
|
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||||
val syscall =
|
val syscall =
|
||||||
when(array.dt) {
|
when(arrayName.type) {
|
||||||
DataType.ARRAY_UB,
|
DataType.ARRAY_UB,
|
||||||
DataType.ARRAY_B -> IMSyscall.ALL_BYTE
|
DataType.ARRAY_B -> IMSyscall.ALL_BYTE
|
||||||
DataType.ARRAY_UW,
|
DataType.ARRAY_UW,
|
||||||
@ -93,301 +150,413 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
else -> throw IllegalArgumentException("weird type")
|
else -> throw IllegalArgumentException("weird type")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += IRCodeChunk(null, null).also {
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
val lengthReg = codeGen.registers.nextFree()
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
|
||||||
if(resultRegister!=0)
|
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val sourceDt = call.args.single().type
|
val sourceDt = call.args.single().type
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(sourceDt!=DataType.UWORD) {
|
if(sourceDt==DataType.UWORD)
|
||||||
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
return ExpressionCodeResult.EMPTY
|
||||||
when (sourceDt) {
|
|
||||||
DataType.UBYTE -> {
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += IRCodeChunk(null, null).also {
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = resultRegister)
|
when (sourceDt) {
|
||||||
}
|
DataType.BYTE -> {
|
||||||
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
|
val compareReg = codeGen.registers.nextFree()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
|
||||||
|
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||||
|
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
result += IRCodeChunk(notNegativeLabel, null)
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||||
val compareReg = codeGen.registers.nextFree()
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister)
|
|
||||||
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
|
|
||||||
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
|
||||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister)
|
|
||||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
|
|
||||||
}
|
|
||||||
result += IRCodeChunk(notNegativeLabel, null)
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
val notNegativeLabel = codeGen.createLabelName()
|
|
||||||
val compareReg = codeGen.registers.nextFree()
|
|
||||||
result += IRCodeChunk(null, null).also {
|
|
||||||
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister)
|
|
||||||
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
|
|
||||||
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
|
||||||
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister)
|
|
||||||
}
|
|
||||||
result += IRCodeChunk(notNegativeLabel, null)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
val notNegativeLabel = codeGen.createLabelName()
|
||||||
|
val compareReg = codeGen.registers.nextFree()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
|
||||||
|
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||||
|
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(notNegativeLabel, null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||||
|
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
|
||||||
|
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val reg = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
val vmDt = irType(call.type)
|
||||||
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1 = resultRegister, reg2 = reg)
|
it += IRInstruction(Opcode.SGN, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, vmDt, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val reg = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += IRCodeChunk(null, null).also {
|
val dt = call.args[0].type
|
||||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
|
when(dt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
|
||||||
|
}
|
||||||
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||||
|
}
|
||||||
|
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
addToResult(result, tr, -1, tr.resultFpReg)
|
||||||
|
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
|
||||||
|
}
|
||||||
|
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid dt for sqrt")
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
val reg = codeGen.registers.nextFree()
|
val reg = codeGen.registers.nextFree()
|
||||||
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
|
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
|
||||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||||
result += assignRegisterTo(call.args.single(), reg)
|
result += assignRegisterTo(call.args.single(), reg)
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, reg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPopw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
val reg = codeGen.registers.nextFree()
|
val reg = codeGen.registers.nextFree()
|
||||||
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
|
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
|
||||||
val result = mutableListOf<IRCodeChunkBase>(code)
|
val result = mutableListOf<IRCodeChunkBase>(code)
|
||||||
result += assignRegisterTo(call.args.single(), reg)
|
result += assignRegisterTo(call.args.single(), reg)
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.WORD, reg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPush(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val reg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
|
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=tr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPushw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val reg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), reg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = reg)
|
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||||
val syscall =
|
val syscall =
|
||||||
when(array.dt) {
|
when(arrayName.type) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
|
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
|
||||||
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
|
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
|
||||||
|
in SplitWordArrayTypes -> TODO("split word reverse")
|
||||||
else -> throw IllegalArgumentException("weird type to reverse")
|
else -> throw IllegalArgumentException("weird type to reverse")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += IRCodeChunk(null, null).also {
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
val lengthReg = codeGen.registers.nextFree()
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||||
}
|
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val arrayName = call.args[0] as PtIdentifier
|
val arrayName = call.args[0] as PtIdentifier
|
||||||
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
|
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
|
||||||
val syscall =
|
val syscall =
|
||||||
when(array.dt) {
|
when(arrayName.type) {
|
||||||
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
|
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
|
||||||
DataType.ARRAY_B -> IMSyscall.SORT_BYTE
|
DataType.ARRAY_B -> IMSyscall.SORT_BYTE
|
||||||
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
|
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
|
||||||
DataType.ARRAY_W -> IMSyscall.SORT_WORD
|
DataType.ARRAY_W -> IMSyscall.SORT_WORD
|
||||||
DataType.STR -> IMSyscall.SORT_UBYTE
|
DataType.STR -> IMSyscall.SORT_UBYTE
|
||||||
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
|
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
|
||||||
|
in SplitWordArrayTypes -> TODO("split word sort")
|
||||||
else -> throw IllegalArgumentException("weird type to sort")
|
else -> throw IllegalArgumentException("weird type to sort")
|
||||||
}
|
}
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += IRCodeChunk(null, null).also {
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
val lengthReg = codeGen.registers.nextFree()
|
||||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
|
||||||
}
|
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val msbReg = codeGen.registers.nextFree()
|
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], msbReg, -1)
|
val msbTr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[1], resultRegister, -1)
|
addToResult(result, msbTr, msbTr.resultReg, -1)
|
||||||
|
val lsbTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, lsbTr, lsbTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = resultRegister, reg2 = msbReg)
|
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = lsbTr.resultReg, reg2 = msbTr.resultReg)
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.WORD, lsbTr.resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val type = irType(call.type)
|
||||||
|
val valueTr = exprGen.translateExpression(call.args[0])
|
||||||
|
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||||
|
val maximumTr = exprGen.translateExpression(call.args[2])
|
||||||
|
result += valueTr.chunks
|
||||||
|
result += minimumTr.chunks
|
||||||
|
result += maximumTr.chunks
|
||||||
|
if(type==IRDataType.FLOAT) {
|
||||||
|
result += codeGen.makeSyscall(
|
||||||
|
IMSyscall.CLAMP_FLOAT, listOf(
|
||||||
|
valueTr.dt to valueTr.resultFpReg,
|
||||||
|
minimumTr.dt to minimumTr.resultFpReg,
|
||||||
|
maximumTr.dt to maximumTr.resultFpReg,
|
||||||
|
), type to valueTr.resultFpReg
|
||||||
|
)
|
||||||
|
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
|
||||||
|
} else {
|
||||||
|
val syscall = when(call.type) {
|
||||||
|
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE
|
||||||
|
DataType.BYTE -> IMSyscall.CLAMP_BYTE
|
||||||
|
DataType.UWORD -> IMSyscall.CLAMP_UWORD
|
||||||
|
DataType.WORD -> IMSyscall.CLAMP_WORD
|
||||||
|
else -> throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
result += codeGen.makeSyscall(syscall, listOf(
|
||||||
|
valueTr.dt to valueTr.resultReg,
|
||||||
|
minimumTr.dt to minimumTr.resultReg,
|
||||||
|
maximumTr.dt to maximumTr.resultReg,
|
||||||
|
), type to valueTr.resultReg
|
||||||
|
)
|
||||||
|
return ExpressionCodeResult(result, type, valueTr.resultReg, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMin(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
|
val type = irType(call.type)
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val leftTr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
|
val rightTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
|
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
|
||||||
|
val after = codeGen.createLabelName()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
|
||||||
|
// right <= left, take right
|
||||||
|
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(after, null)
|
||||||
|
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMax(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
|
val type = irType(call.type)
|
||||||
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
|
val leftTr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||||
|
val rightTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||||
|
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
|
||||||
|
val after = codeGen.createLabelName()
|
||||||
|
result += IRCodeChunk(null, null).also {
|
||||||
|
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
|
||||||
|
// right >= left, take right
|
||||||
|
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
|
||||||
|
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
|
||||||
|
}
|
||||||
|
result += IRCodeChunk(after, null)
|
||||||
|
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(codeGen.isZero(call.args[1])) {
|
if(codeGen.isZero(call.args[1])) {
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
|
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, address = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val tr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
|
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, address = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val addressTr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val valueTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunks {
|
private fun funcPoke(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(codeGen.isZero(call.args[1])) {
|
if(codeGen.isZero(call.args[1])) {
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
|
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val valueReg = codeGen.registers.nextFree()
|
|
||||||
if (call.args[0] is PtNumber) {
|
if (call.args[0] is PtNumber) {
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val tr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val addressTr = exprGen.translateExpression(call.args[0])
|
||||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
addToResult(result, addressTr, addressTr.resultReg, -1)
|
||||||
result += exprGen.translateExpression(call.args[1], valueReg, -1)
|
val valueTr = exprGen.translateExpression(call.args[1])
|
||||||
|
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
|
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcPeekW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(call.args[0] is PtNumber) {
|
return if(call.args[0] is PtNumber) {
|
||||||
|
val resultRegister = codeGen.registers.nextFree()
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
|
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, address = address)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
|
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcPeek(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
if(call.args[0] is PtNumber) {
|
return if(call.args[0] is PtNumber) {
|
||||||
|
val resultRegister = codeGen.registers.nextFree()
|
||||||
val address = (call.args[0] as PtNumber).number.toInt()
|
val address = (call.args[0] as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = address)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||||
} else {
|
} else {
|
||||||
val addressReg = codeGen.registers.nextFree()
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
|
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
|
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val name = (call.args[0] as PtString).value
|
val name = (call.args[0] as PtString).value
|
||||||
val code = IRCodeChunk(null, null)
|
val code = IRCodeChunk(null, null)
|
||||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
val resultReg = codeGen.registers.nextFree()
|
||||||
return listOf(code)
|
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||||
|
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
return exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
return exprGen.translateExpression(call.args.single())
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
val tr = exprGen.translateExpression(call.args.single())
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
|
val resultReg = codeGen.registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultRegister, reg2 = resultRegister)
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
|
||||||
}
|
}
|
||||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||||
return result
|
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
|
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||||
val vmDt = codeGen.irType(call.args[0].type)
|
val vmDt = irType(call.args[0].type)
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
val tr = exprGen.translateExpression(call.args[0])
|
||||||
|
addToResult(result, tr, tr.resultReg, -1)
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(opcode, vmDt, reg1 = resultRegister)
|
it += IRInstruction(opcode, vmDt, reg1 = tr.resultReg)
|
||||||
}
|
}
|
||||||
result += assignRegisterTo(call.args[0], resultRegister)
|
result += assignRegisterTo(call.args[0], tr.resultReg)
|
||||||
return result
|
return ExpressionCodeResult(result, vmDt, -1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
|
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,42 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
|
|
||||||
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||||
fun optimize() {
|
fun optimize(optimizationsEnabled: Boolean, errors: IErrorReporter) {
|
||||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
if(!optimizationsEnabled)
|
||||||
|
return optimizeOnlyJoinChunks()
|
||||||
|
|
||||||
|
peepholeOptimize()
|
||||||
|
val remover = IRUnusedCodeRemover(irprog, errors)
|
||||||
|
var totalRemovals = 0
|
||||||
|
do {
|
||||||
|
val numRemoved = remover.optimize()
|
||||||
|
totalRemovals += numRemoved
|
||||||
|
} while(numRemoved>0 && errors.noErrors())
|
||||||
|
errors.report()
|
||||||
|
|
||||||
|
if(totalRemovals>0) {
|
||||||
|
irprog.linkChunks() // re-link again.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeOnlyJoinChunks() {
|
||||||
|
irprog.foreachSub { sub ->
|
||||||
|
joinChunks(sub)
|
||||||
removeEmptyChunks(sub)
|
removeEmptyChunks(sub)
|
||||||
joinChunks(sub)
|
joinChunks(sub)
|
||||||
|
}
|
||||||
|
irprog.linkChunks() // re-link
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun peepholeOptimize() {
|
||||||
|
irprog.foreachSub { sub ->
|
||||||
|
joinChunks(sub)
|
||||||
|
removeEmptyChunks(sub)
|
||||||
|
joinChunks(sub)
|
||||||
|
|
||||||
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
||||||
// we don't optimize Inline Asm chunks here.
|
// we don't optimize Inline Asm chunks here.
|
||||||
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
|
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
|
||||||
@ -20,15 +50,16 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
||||||
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
||||||
|| cleanupPushPop(chunk1, indexedInstructions)
|
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||||
// TODO other optimizations:
|
// TODO other optimizations
|
||||||
// more complex optimizations such as unused registers
|
|
||||||
} while (changed)
|
} while (changed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeEmptyChunks(sub)
|
removeEmptyChunks(sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
irprog.linkChunks() // re-link
|
// TODO also do register optimization step here at the end?
|
||||||
|
|
||||||
|
irprog.linkChunks() // re-link
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeEmptyChunks(sub: IRSubroutine) {
|
private fun removeEmptyChunks(sub: IRSubroutine) {
|
||||||
@ -38,7 +69,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
/*
|
/*
|
||||||
Empty Code chunk with label ->
|
Empty Code chunk with label ->
|
||||||
If next chunk has no label -> move label to next chunk, remove original
|
If next chunk has no label -> move label to next chunk, remove original
|
||||||
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: consolidate labels into 1)
|
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: merge both labels into 1)
|
||||||
If is last chunk -> keep chunk in place because of the label.
|
If is last chunk -> keep chunk in place because of the label.
|
||||||
Empty Code chunk without label ->
|
Empty Code chunk without label ->
|
||||||
should not have been generated! ERROR.
|
should not have been generated! ERROR.
|
||||||
@ -63,7 +94,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
if (chunk.label == nextchunk.label)
|
if (chunk.label == nextchunk.label)
|
||||||
removeChunks += index
|
removeChunks += index
|
||||||
else {
|
else {
|
||||||
// TODO: consolidate labels on same chunk
|
// TODO: merge labels on same chunk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +116,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
if(sub.chunks.isEmpty())
|
if(sub.chunks.isEmpty())
|
||||||
return
|
return
|
||||||
|
|
||||||
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
||||||
if(chunk.label!=null)
|
if(chunk.label!=null)
|
||||||
return false
|
return false
|
||||||
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
||||||
@ -102,12 +133,39 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
chunks += sub.chunks[0]
|
chunks += sub.chunks[0]
|
||||||
for(ix in 1 until sub.chunks.size) {
|
for(ix in 1 until sub.chunks.size) {
|
||||||
val lastChunk = chunks.last()
|
val lastChunk = chunks.last()
|
||||||
if(mayJoin(lastChunk, sub.chunks[ix])) {
|
val candidate = sub.chunks[ix]
|
||||||
lastChunk.instructions += sub.chunks[ix].instructions
|
when(candidate) {
|
||||||
lastChunk.next = sub.chunks[ix].next
|
is IRCodeChunk -> {
|
||||||
|
if(mayJoinCodeChunks(lastChunk, candidate)) {
|
||||||
|
lastChunk.instructions += candidate.instructions
|
||||||
|
lastChunk.next = candidate.next
|
||||||
|
}
|
||||||
|
else
|
||||||
|
chunks += candidate
|
||||||
|
}
|
||||||
|
is IRInlineAsmChunk -> {
|
||||||
|
if(candidate.label!=null)
|
||||||
|
chunks += candidate
|
||||||
|
else if(lastChunk.isEmpty()) {
|
||||||
|
val label = lastChunk.label
|
||||||
|
if(label!=null)
|
||||||
|
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
|
||||||
|
else
|
||||||
|
chunks += candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IRInlineBinaryChunk -> {
|
||||||
|
if(candidate.label!=null)
|
||||||
|
chunks += candidate
|
||||||
|
else if(lastChunk.isEmpty()) {
|
||||||
|
val label = lastChunk.label
|
||||||
|
if(label!=null)
|
||||||
|
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
|
||||||
|
else
|
||||||
|
chunks += candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
chunks += sub.chunks[ix]
|
|
||||||
}
|
}
|
||||||
sub.chunks.clear()
|
sub.chunks.clear()
|
||||||
sub.chunks += chunks
|
sub.chunks += chunks
|
||||||
@ -119,8 +177,8 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
if(ins.opcode== Opcode.PUSH) {
|
if(ins.opcode== Opcode.PUSH) {
|
||||||
if(idx < chunk.instructions.size-1) {
|
if(idx < chunk.instructions.size-1) {
|
||||||
val insAfter = chunk.instructions[idx+1] as? IRInstruction
|
val insAfter = chunk.instructions[idx+1]
|
||||||
if(insAfter!=null && insAfter.opcode == Opcode.POP) {
|
if(insAfter.opcode == Opcode.POP) {
|
||||||
if(ins.reg1==insAfter.reg1) {
|
if(ins.reg1==insAfter.reg1) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
@ -143,16 +201,16 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
|
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
|
||||||
if(idx < chunk.instructions.size-1) {
|
if(idx < chunk.instructions.size-1) {
|
||||||
val insAfter = chunk.instructions[idx+1] as? IRInstruction
|
val insAfter = chunk.instructions[idx+1]
|
||||||
if(insAfter?.opcode == ins.opcode) {
|
if(insAfter.opcode == ins.opcode) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
else if(ins.opcode== Opcode.SEC && insAfter?.opcode== Opcode.CLC) {
|
else if(ins.opcode== Opcode.SEC && insAfter.opcode== Opcode.CLC) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
else if(ins.opcode== Opcode.CLC && insAfter?.opcode== Opcode.SEC) {
|
else if(ins.opcode== Opcode.CLC && insAfter.opcode== Opcode.SEC) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
@ -174,14 +232,36 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove useless RETURN
|
// remove useless RETURN
|
||||||
if(ins.opcode == Opcode.RETURN && idx>0) {
|
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) {
|
||||||
val previous = chunk.instructions[idx-1] as? IRInstruction
|
val previous = chunk.instructions[idx-1]
|
||||||
if(previous?.opcode in OpcodesThatJump) {
|
if(previous.opcode in OpcodesThatJump) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replace subsequent opcodes that jump by just the first
|
||||||
|
if(idx>0 && (ins.opcode in OpcodesThatJump)) {
|
||||||
|
val previous = chunk.instructions[idx-1]
|
||||||
|
if(previous.opcode in OpcodesThatJump) {
|
||||||
|
chunk.instructions.removeAt(idx)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace call + return --> jump
|
||||||
|
// This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
|
||||||
|
// If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
|
||||||
|
// if(idx>0 && ins.opcode==Opcode.RETURN) {
|
||||||
|
// val previous = chunk.instructions[idx-1]
|
||||||
|
// if(previous.opcode==Opcode.CALL) {
|
||||||
|
// chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||||
|
// chunk.instructions.removeAt(idx)
|
||||||
|
// changed = true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
return changed
|
return changed
|
||||||
}
|
}
|
||||||
@ -192,47 +272,47 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|||||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||||
when (ins.opcode) {
|
when (ins.opcode) {
|
||||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||||
if (ins.value == 1) {
|
if (ins.immediate == 1) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.ADD, Opcode.SUB -> {
|
Opcode.ADD, Opcode.SUB -> {
|
||||||
if (ins.value == 1) {
|
if (ins.immediate == 1) {
|
||||||
chunk.instructions[idx] = IRInstruction(
|
chunk.instructions[idx] = IRInstruction(
|
||||||
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
|
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
|
||||||
ins.type,
|
ins.type,
|
||||||
ins.reg1
|
ins.reg1
|
||||||
)
|
)
|
||||||
changed = true
|
changed = true
|
||||||
} else if (ins.value == 0) {
|
} else if (ins.immediate == 0) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.AND -> {
|
Opcode.AND -> {
|
||||||
if (ins.value == 0) {
|
if (ins.immediate == 0) {
|
||||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||||
changed = true
|
changed = true
|
||||||
} else if (ins.value == 255 && ins.type == IRDataType.BYTE) {
|
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
} else if (ins.value == 65535 && ins.type == IRDataType.WORD) {
|
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.OR -> {
|
Opcode.OR -> {
|
||||||
if (ins.value == 0) {
|
if (ins.immediate == 0) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
} else if ((ins.value == 255 && ins.type == IRDataType.BYTE) || (ins.value == 65535 && ins.type == IRDataType.WORD)) {
|
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) || (ins.immediate == 65535 && ins.type == IRDataType.WORD)) {
|
||||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Opcode.XOR -> {
|
Opcode.XOR -> {
|
||||||
if (ins.value == 0) {
|
if (ins.immediate == 0) {
|
||||||
chunk.instructions.removeAt(idx)
|
chunk.instructions.removeAt(idx)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
|
import prog8.intermediate.IRProgram
|
||||||
|
|
||||||
|
|
||||||
|
class IRRegisterOptimizer(private val irProg: IRProgram) {
|
||||||
|
fun optimize() {
|
||||||
|
// reuseRegisters()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: this register re-use renumbering isn't going to work like this,
|
||||||
|
because subroutines will be clobbering the registers that the subroutine
|
||||||
|
which is calling them might be using...
|
||||||
|
|
||||||
|
|
||||||
|
private fun reuseRegisters() {
|
||||||
|
|
||||||
|
fun addToUsage(usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>>,
|
||||||
|
regnum: Int,
|
||||||
|
dt: IRDataType,
|
||||||
|
chunk: IRCodeChunkBase) {
|
||||||
|
val key = regnum to dt
|
||||||
|
val chunks = usage[key] ?: mutableSetOf()
|
||||||
|
chunks.add(chunk)
|
||||||
|
usage[key] = chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
val usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>> = mutableMapOf()
|
||||||
|
|
||||||
|
irProg.foreachCodeChunk { chunk ->
|
||||||
|
chunk.usedRegisters().regsTypes.forEach { (regNum, types) ->
|
||||||
|
types.forEach { dt ->
|
||||||
|
addToUsage(usage, regNum, dt, chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val registerReplacements = usage.asSequence()
|
||||||
|
.filter { it.value.size==1 }
|
||||||
|
.map { it.key to it.value.iterator().next() }
|
||||||
|
.groupBy({ it.second }, {it.first})
|
||||||
|
.asSequence()
|
||||||
|
.associate { (chunk, registers) ->
|
||||||
|
chunk to registers.withIndex().associate { (index, reg) -> reg to 50000+index }
|
||||||
|
}
|
||||||
|
|
||||||
|
registerReplacements.forEach { replaceRegisters(it.key, it.value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceRegisters(chunk: IRCodeChunkBase, replacements: Map<Pair<Int, IRDataType>, Int>) {
|
||||||
|
val (rF, rI) = replacements.asSequence().partition { it.key.second==IRDataType.FLOAT }
|
||||||
|
val replacementsInt = rI.associate { it.key.first to it.value }
|
||||||
|
val replacementsFloat = rF.associate { it.key.first to it.value }
|
||||||
|
|
||||||
|
fun replaceRegs(fcallArgs: FunctionCallArgs?): FunctionCallArgs? {
|
||||||
|
if(fcallArgs==null)
|
||||||
|
return null
|
||||||
|
val args = if(fcallArgs.arguments.isEmpty()) fcallArgs.arguments else {
|
||||||
|
fcallArgs.arguments.map {
|
||||||
|
FunctionCallArgs.ArgumentSpec(
|
||||||
|
it.name,
|
||||||
|
it.address,
|
||||||
|
FunctionCallArgs.RegSpec(
|
||||||
|
it.reg.dt,
|
||||||
|
if(it.reg.dt==IRDataType.FLOAT)
|
||||||
|
replacementsFloat.getOrDefault(it.reg.registerNum, it.reg.registerNum)
|
||||||
|
else
|
||||||
|
replacementsInt.getOrDefault(it.reg.registerNum, it.reg.registerNum),
|
||||||
|
it.reg.cpuRegister
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val rt = fcallArgs.returns
|
||||||
|
val returns = if(rt==null) null else {
|
||||||
|
FunctionCallArgs.RegSpec(
|
||||||
|
rt.dt,
|
||||||
|
if(rt.dt==IRDataType.FLOAT)
|
||||||
|
replacementsFloat.getOrDefault(rt.registerNum, rt.registerNum)
|
||||||
|
else
|
||||||
|
replacementsInt.getOrDefault(rt.registerNum, rt.registerNum),
|
||||||
|
rt.cpuRegister
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return FunctionCallArgs(args, returns)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun replaceRegs(instruction: IRInstruction): IRInstruction {
|
||||||
|
val reg1 = replacementsInt.getOrDefault(instruction.reg1, instruction.reg1)
|
||||||
|
val reg2 = replacementsInt.getOrDefault(instruction.reg2, instruction.reg2)
|
||||||
|
val fpReg1 = replacementsFloat.getOrDefault(instruction.fpReg1, instruction.fpReg1)
|
||||||
|
val fpReg2 = replacementsFloat.getOrDefault(instruction.fpReg2, instruction.fpReg2)
|
||||||
|
return instruction.copy(reg1 = reg1, reg2 = reg2, fpReg1 = fpReg1, fpReg2 = fpReg2, fcallArgs = replaceRegs(instruction.fcallArgs))
|
||||||
|
}
|
||||||
|
val newInstructions = chunk.instructions.map {
|
||||||
|
replaceRegs(it)
|
||||||
|
}
|
||||||
|
chunk.instructions.clear()
|
||||||
|
chunk.instructions.addAll(newInstructions)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
@ -5,34 +5,18 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
|||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
|
|
||||||
|
|
||||||
internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val errors: IErrorReporter) {
|
class IRUnusedCodeRemover(
|
||||||
|
private val irprog: IRProgram,
|
||||||
|
private val errors: IErrorReporter
|
||||||
|
) {
|
||||||
fun optimize(): Int {
|
fun optimize(): Int {
|
||||||
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
|
var numRemoved = removeUnusedSubroutines() + removeUnusedAsmSubroutines()
|
||||||
|
|
||||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
|
||||||
sub.chunks.forEach { chunk ->
|
|
||||||
chunk.label?.let { allLabeledChunks[it] = chunk }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
|
|
||||||
|
|
||||||
// remove empty subs
|
|
||||||
irprog.blocks.forEach { block ->
|
|
||||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
|
||||||
if(sub.isEmpty()) {
|
|
||||||
if(!sub.position.file.startsWith(libraryFilePrefix))
|
|
||||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
|
||||||
block.children.remove(sub)
|
|
||||||
numRemoved++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove empty blocks
|
// remove empty blocks
|
||||||
irprog.blocks.reversed().forEach { block ->
|
irprog.blocks.reversed().forEach { block ->
|
||||||
if(block.isEmpty()) {
|
if(block.isEmpty()) {
|
||||||
irprog.blocks.remove(block)
|
irprog.blocks.remove(block)
|
||||||
|
pruneSymboltable(block.label)
|
||||||
numRemoved++
|
numRemoved++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,10 +24,133 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
|||||||
return numRemoved
|
return numRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun pruneSymboltable(blockLabel: String) {
|
||||||
|
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction
|
||||||
|
val prefix = "$blockLabel."
|
||||||
|
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
|
||||||
|
blockVars.forEach { stVar ->
|
||||||
|
irprog. allSubs().flatMap { it.chunks }.forEach { chunk ->
|
||||||
|
chunk.instructions.forEach { ins ->
|
||||||
|
if(ins.labelSymbol == stVar.name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
irprog.st.removeTree(blockLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUnusedSubroutines(): Int {
|
||||||
|
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
|
||||||
|
irprog.foreachCodeChunk { chunk ->
|
||||||
|
chunk.label?.let { allLabeledChunks[it] = chunk }
|
||||||
|
}
|
||||||
|
|
||||||
|
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
|
||||||
|
irprog.blocks.forEach { block ->
|
||||||
|
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||||
|
if(sub.isEmpty()) {
|
||||||
|
if(!sub.position.file.startsWith(libraryFilePrefix)) {
|
||||||
|
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||||
|
}
|
||||||
|
block.children.remove(sub)
|
||||||
|
irprog.st.removeTree(sub.label)
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUnusedAsmSubroutines(): Int {
|
||||||
|
val allLabeledAsmsubs = irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
|
||||||
|
.associateBy { it.label }
|
||||||
|
|
||||||
|
var numRemoved = removeSimpleUnlinkedAsmsubs(allLabeledAsmsubs)
|
||||||
|
irprog.blocks.forEach { block ->
|
||||||
|
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
|
||||||
|
if(sub.isEmpty()) {
|
||||||
|
if(!sub.position.file.startsWith(libraryFilePrefix)) {
|
||||||
|
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
|
||||||
|
}
|
||||||
|
block.children.remove(sub)
|
||||||
|
irprog.st.removeTree(sub.label)
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeSimpleUnlinkedAsmsubs(allSubs: Map<String, IRAsmSubroutine>): Int {
|
||||||
|
val linkedAsmSubs = mutableSetOf<IRAsmSubroutine>()
|
||||||
|
|
||||||
|
// TODO: asmsubs in library modules are never removed, we can't really tell here if they're actually being called or not...
|
||||||
|
|
||||||
|
// check if asmsub is called from another asmsub
|
||||||
|
irprog.blocks.asSequence().forEach { block ->
|
||||||
|
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
|
||||||
|
if (block.forceOutput || block.library)
|
||||||
|
linkedAsmSubs += sub
|
||||||
|
if (sub.asmChunk.isNotEmpty()) {
|
||||||
|
allSubs.forEach { (label, asmsub) ->
|
||||||
|
if (sub.asmChunk.assembly.contains(label))
|
||||||
|
linkedAsmSubs += asmsub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val inlineAsm = sub.asmChunk.next as? IRInlineAsmChunk
|
||||||
|
if(inlineAsm!=null) {
|
||||||
|
allSubs.forEach { (label, asmsub) ->
|
||||||
|
if (inlineAsm.assembly.contains(label))
|
||||||
|
linkedAsmSubs += asmsub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if asmsub is linked or called from another regular subroutine
|
||||||
|
irprog.foreachCodeChunk { chunk ->
|
||||||
|
chunk.instructions.forEach {
|
||||||
|
it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } }
|
||||||
|
// note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeUnlinkedAsmsubs(linkedAsmSubs)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUnlinkedAsmsubs(linkedAsmSubs: Set<IRAsmSubroutine>): Int {
|
||||||
|
var numRemoved = 0
|
||||||
|
irprog.blocks.asSequence().forEach { block ->
|
||||||
|
block.children.withIndex().reversed().forEach { (index, child) ->
|
||||||
|
if(child is IRAsmSubroutine && child !in linkedAsmSubs) {
|
||||||
|
block.children.removeAt(index)
|
||||||
|
numRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numRemoved
|
||||||
|
}
|
||||||
|
|
||||||
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
|
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
|
||||||
val entrypointSub = irprog.blocks.single { it.name=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
|
val entrypointSub = irprog.blocks.single { it.label=="main" }
|
||||||
|
.children.single { it is IRSubroutine && it.label=="main.start" }
|
||||||
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
|
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
|
||||||
|
|
||||||
|
// all chunks referenced in array initializer values are also 'reachable':
|
||||||
|
irprog.st.allVariables()
|
||||||
|
.filter { !it.uninitialized }
|
||||||
|
.forEach {
|
||||||
|
it.onetimeInitializationArrayValue?.let { array ->
|
||||||
|
array.forEach {elt ->
|
||||||
|
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
|
||||||
|
reachable.add(irprog.getChunkWithLabel(elt.addressOfSymbol!!))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun grow() {
|
fun grow() {
|
||||||
val new = mutableSetOf<IRCodeChunkBase>()
|
val new = mutableSetOf<IRCodeChunkBase>()
|
||||||
reachable.forEach {
|
reachable.forEach {
|
||||||
@ -72,29 +179,39 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
|||||||
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
|
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
|
||||||
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
|
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
|
||||||
|
|
||||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
// all chunks referenced in array initializer values are linked as well!:
|
||||||
sub.chunks.forEach { chunk ->
|
irprog.st.allVariables()
|
||||||
chunk.next?.let { next -> linkedChunks += next }
|
.filter { !it.uninitialized }
|
||||||
chunk.instructions.forEach {
|
.forEach {
|
||||||
if(it.branchTarget==null) {
|
it.onetimeInitializationArrayValue?.let { array ->
|
||||||
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
|
array.forEach {elt ->
|
||||||
} else {
|
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
|
||||||
linkedChunks += it.branchTarget!!
|
linkedChunks += irprog.getChunkWithLabel(elt.addressOfSymbol!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chunk.label == "main.start")
|
|
||||||
linkedChunks += chunk
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irprog.foreachCodeChunk { chunk ->
|
||||||
|
chunk.next?.let { next -> linkedChunks += next }
|
||||||
|
chunk.instructions.forEach {
|
||||||
|
if(it.branchTarget==null) {
|
||||||
|
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
|
||||||
|
} else {
|
||||||
|
linkedChunks += it.branchTarget!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chunk.label == "main.start")
|
||||||
|
linkedChunks += chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
return removeUnlinkedChunks(linkedChunks)
|
return removeUnlinkedChunks(linkedChunks)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeUnlinkedChunks(
|
private fun removeUnlinkedChunks(
|
||||||
linkedChunks: MutableSet<IRCodeChunkBase>
|
linkedChunks: Set<IRCodeChunkBase>
|
||||||
): Int {
|
): Int {
|
||||||
var numRemoved = 0
|
var numRemoved = 0
|
||||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
irprog.foreachSub { sub ->
|
||||||
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
|
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
|
||||||
if (chunk !in linkedChunks) {
|
if (chunk !in linkedChunks) {
|
||||||
if (chunk === sub.chunks[0]) {
|
if (chunk === sub.chunks[0]) {
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
import prog8.code.core.AssemblyError
|
|
||||||
import prog8.intermediate.SyscallRegisterBase
|
|
||||||
|
|
||||||
internal class RegisterPool {
|
internal class RegisterPool {
|
||||||
// reserve 0,1,2 for return values of subroutine calls and syscalls
|
// reserve 0,1,2 for return values of subroutine calls and syscalls in IR assembly code
|
||||||
private var firstFree: Int=3
|
private var firstFree: Int=3
|
||||||
private var firstFreeFloat: Int=3
|
private var firstFreeFloat: Int=3
|
||||||
|
|
||||||
@ -14,16 +11,12 @@ internal class RegisterPool {
|
|||||||
fun nextFree(): Int {
|
fun nextFree(): Int {
|
||||||
val result = firstFree
|
val result = firstFree
|
||||||
firstFree++
|
firstFree++
|
||||||
if(firstFree >= SyscallRegisterBase)
|
|
||||||
throw AssemblyError("out of virtual registers (int)")
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextFreeFloat(): Int {
|
fun nextFreeFloat(): Int {
|
||||||
val result = firstFreeFloat
|
val result = firstFreeFloat
|
||||||
firstFreeFloat++
|
firstFreeFloat++
|
||||||
if(firstFreeFloat >= SyscallRegisterBase)
|
|
||||||
throw AssemblyError("out of virtual registers (fp)")
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class VmCodeGen: ICodeGeneratorBackend {
|
|||||||
|
|
||||||
internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
|
internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||||
// the VM reads the IR file from disk.
|
// the VM reads the IR file from disk.
|
||||||
IRFileWriter(irProgram, null).write()
|
IRFileWriter(irProgram, null).write()
|
||||||
return true
|
return true
|
||||||
|
@ -32,11 +32,19 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
|||||||
val warnings = mutableListOf<String>()
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
override fun err(msg: String, position: Position) {
|
override fun err(msg: String, position: Position) {
|
||||||
errors.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in errors)
|
||||||
|
errors.add(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun warn(msg: String, position: Position) {
|
override fun warn(msg: String, position: Position) {
|
||||||
warnings.add("${position.toClickableStr()} $msg")
|
val text = "${position.toClickableStr()} $msg"
|
||||||
|
if(text !in warnings)
|
||||||
|
warnings.add(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun undefined(symbol: List<String>, position: Position) {
|
||||||
|
err("undefined symbol: ${symbol.joinToString(".")}", position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
@ -8,7 +8,7 @@ import prog8.intermediate.*
|
|||||||
class TestIRPeepholeOpt: FunSpec({
|
class TestIRPeepholeOpt: FunSpec({
|
||||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||||
require(chunks.first().label=="main.start")
|
require(chunks.first().label=="main.start")
|
||||||
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||||
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
||||||
chunks.forEach { sub += it }
|
chunks.forEach { sub += it }
|
||||||
block += sub
|
block += sub
|
||||||
@ -36,17 +36,17 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
return makeIRProgram(listOf(chunk))
|
return makeIRProgram(listOf(chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }
|
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.allSubs().flatMap { it.chunks }.toList()
|
||||||
|
|
||||||
test("remove nops") {
|
test("remove nops") {
|
||||||
val irProg = makeIRProgram(listOf(
|
val irProg = makeIRProgram(listOf(
|
||||||
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=42),
|
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, immediate=42),
|
||||||
IRInstruction(Opcode.NOP),
|
IRInstruction(Opcode.NOP),
|
||||||
IRInstruction(Opcode.NOP)
|
IRInstruction(Opcode.NOP)
|
||||||
))
|
))
|
||||||
irProg.chunks().single().instructions.size shouldBe 3
|
irProg.chunks().single().instructions.size shouldBe 3
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
irProg.chunks().single().instructions.size shouldBe 1
|
irProg.chunks().single().instructions.size shouldBe 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
irProg.chunks().size shouldBe 4
|
irProg.chunks().size shouldBe 4
|
||||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
irProg.chunks().size shouldBe 4
|
irProg.chunks().size shouldBe 4
|
||||||
irProg.chunks()[0].label shouldBe "main.start"
|
irProg.chunks()[0].label shouldBe "main.start"
|
||||||
irProg.chunks()[1].label shouldBe "label"
|
irProg.chunks()[1].label shouldBe "label"
|
||||||
@ -92,7 +92,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
))
|
))
|
||||||
irProg.chunks().single().instructions.size shouldBe 6
|
irProg.chunks().single().instructions.size shouldBe 6
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
val instr = irProg.chunks().single().instructions
|
val instr = irProg.chunks().single().instructions
|
||||||
instr.size shouldBe 1
|
instr.size shouldBe 1
|
||||||
instr[0].opcode shouldBe Opcode.CLC
|
instr[0].opcode shouldBe Opcode.CLC
|
||||||
@ -107,7 +107,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
))
|
))
|
||||||
irProg.chunks().single().instructions.size shouldBe 4
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
val instr = irProg.chunks().single().instructions
|
val instr = irProg.chunks().single().instructions
|
||||||
instr.size shouldBe 1
|
instr.size shouldBe 1
|
||||||
instr[0].opcode shouldBe Opcode.LOADR
|
instr[0].opcode shouldBe Opcode.LOADR
|
||||||
@ -117,31 +117,31 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
|
|
||||||
test("remove useless div/mul, add/sub") {
|
test("remove useless div/mul, add/sub") {
|
||||||
val irProg = makeIRProgram(listOf(
|
val irProg = makeIRProgram(listOf(
|
||||||
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 1),
|
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||||
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 1),
|
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||||
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 1),
|
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||||
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 1),
|
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||||
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 2),
|
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||||
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 2),
|
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||||
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 2),
|
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||||
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 2),
|
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||||
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0),
|
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||||
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
|
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 0)
|
||||||
))
|
))
|
||||||
irProg.chunks().single().instructions.size shouldBe 10
|
irProg.chunks().single().instructions.size shouldBe 10
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
irProg.chunks().single().instructions.size shouldBe 4
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
}
|
}
|
||||||
|
|
||||||
test("replace add/sub 1 by inc/dec") {
|
test("replace add/sub 1 by inc/dec") {
|
||||||
val irProg = makeIRProgram(listOf(
|
val irProg = makeIRProgram(listOf(
|
||||||
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1),
|
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||||
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
|
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 1)
|
||||||
))
|
))
|
||||||
irProg.chunks().single().instructions.size shouldBe 2
|
irProg.chunks().single().instructions.size shouldBe 2
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
val instr = irProg.chunks().single().instructions
|
val instr = irProg.chunks().single().instructions
|
||||||
instr.size shouldBe 2
|
instr.size shouldBe 2
|
||||||
instr[0].opcode shouldBe Opcode.INC
|
instr[0].opcode shouldBe Opcode.INC
|
||||||
@ -150,40 +150,40 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
|
|
||||||
test("remove useless and/or/xor") {
|
test("remove useless and/or/xor") {
|
||||||
val irProg = makeIRProgram(listOf(
|
val irProg = makeIRProgram(listOf(
|
||||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 255),
|
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 255),
|
||||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 65535),
|
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 65535),
|
||||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 0),
|
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||||
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 0),
|
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 200),
|
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 200),
|
||||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 60000),
|
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 60000),
|
||||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1),
|
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||||
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
|
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 1)
|
||||||
))
|
))
|
||||||
irProg.chunks().single().instructions.size shouldBe 8
|
irProg.chunks().single().instructions.size shouldBe 8
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
irProg.chunks().single().instructions.size shouldBe 4
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
}
|
}
|
||||||
|
|
||||||
test("replace and/or/xor by constant number") {
|
test("replace and/or/xor by constant number") {
|
||||||
val irProg = makeIRProgram(listOf(
|
val irProg = makeIRProgram(listOf(
|
||||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 0),
|
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 0),
|
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 0),
|
||||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255),
|
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 255),
|
||||||
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
|
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535)
|
||||||
))
|
))
|
||||||
irProg.chunks().single().instructions.size shouldBe 4
|
irProg.chunks().single().instructions.size shouldBe 4
|
||||||
val opt = IRPeepholeOptimizer(irProg)
|
val opt = IRPeepholeOptimizer(irProg)
|
||||||
opt.optimize()
|
opt.optimize(true, ErrorReporterForTests())
|
||||||
val instr = irProg.chunks().single().instructions
|
val instr = irProg.chunks().single().instructions
|
||||||
instr.size shouldBe 4
|
instr.size shouldBe 4
|
||||||
instr[0].opcode shouldBe Opcode.LOAD
|
instr[0].opcode shouldBe Opcode.LOAD
|
||||||
instr[1].opcode shouldBe Opcode.LOAD
|
instr[1].opcode shouldBe Opcode.LOAD
|
||||||
instr[2].opcode shouldBe Opcode.LOAD
|
instr[2].opcode shouldBe Opcode.LOAD
|
||||||
instr[3].opcode shouldBe Opcode.LOAD
|
instr[3].opcode shouldBe Opcode.LOAD
|
||||||
instr[0].value shouldBe 0
|
instr[0].immediate shouldBe 0
|
||||||
instr[1].value shouldBe 0
|
instr[1].immediate shouldBe 0
|
||||||
instr[2].value shouldBe 255
|
instr[2].immediate shouldBe 255
|
||||||
instr[3].value shouldBe 65535
|
instr[3].immediate shouldBe 65535
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -1,5 +1,6 @@
|
|||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
@ -7,6 +8,7 @@ import prog8.code.target.VMTarget
|
|||||||
import prog8.codegen.vm.VmAssemblyProgram
|
import prog8.codegen.vm.VmAssemblyProgram
|
||||||
import prog8.codegen.vm.VmCodeGen
|
import prog8.codegen.vm.VmCodeGen
|
||||||
import prog8.intermediate.IRSubroutine
|
import prog8.intermediate.IRSubroutine
|
||||||
|
import prog8.intermediate.Opcode
|
||||||
|
|
||||||
class TestVmCodeGen: FunSpec({
|
class TestVmCodeGen: FunSpec({
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
//}
|
//}
|
||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||||
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||||
@ -89,10 +91,73 @@ class TestVmCodeGen: FunSpec({
|
|||||||
program.add(block)
|
program.add(block)
|
||||||
|
|
||||||
// define the "cx16.r0" virtual register
|
// define the "cx16.r0" virtual register
|
||||||
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||||
program.add(cx16block)
|
program.add(cx16block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
test("float comparison expressions against zero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// float @shared f1
|
||||||
|
//
|
||||||
|
// if f1==0
|
||||||
|
// nop
|
||||||
|
// if f1!=0
|
||||||
|
// nop
|
||||||
|
// if f1>0
|
||||||
|
// nop
|
||||||
|
// if f1<0
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
val options = getTestOptions()
|
val options = getTestOptions()
|
||||||
val st = SymbolTableMaker(program, options).make()
|
val st = SymbolTableMaker(program, options).make()
|
||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests()
|
||||||
@ -100,4 +165,309 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
irChunks.size shouldBeGreaterThan 4
|
irChunks.size shouldBeGreaterThan 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("float comparison expressions against nonzero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// float @shared f1
|
||||||
|
//
|
||||||
|
// if f1==42
|
||||||
|
// nop
|
||||||
|
// if f1!=42
|
||||||
|
// nop
|
||||||
|
// if f1>42
|
||||||
|
// nop
|
||||||
|
// if f1<42
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("float conditional jump") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// float @shared f1
|
||||||
|
//
|
||||||
|
// if f1==42
|
||||||
|
// goto $c000
|
||||||
|
// if f1>42
|
||||||
|
// goto $c000
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup())
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup())
|
||||||
|
sub.add(if2)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
test("integer comparison expressions against zero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// byte @shared sb1
|
||||||
|
//
|
||||||
|
// if sb1==0
|
||||||
|
// nop
|
||||||
|
// if sb1!=0
|
||||||
|
// nop
|
||||||
|
// if sb1>0
|
||||||
|
// nop
|
||||||
|
// if sb1<0
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("integer comparison expressions against nonzero") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// byte @shared sb1
|
||||||
|
//
|
||||||
|
// if sb1==42
|
||||||
|
// nop
|
||||||
|
// if sb1!=42
|
||||||
|
// nop
|
||||||
|
// if sb1>42
|
||||||
|
// nop
|
||||||
|
// if sb1<42
|
||||||
|
// nop
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if2)
|
||||||
|
val if3 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if3.add(cmp3)
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if3)
|
||||||
|
val if4 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
|
||||||
|
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||||
|
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
|
||||||
|
if4.add(cmp4)
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||||
|
sub.add(if4)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
|
||||||
|
test("integer conditional jump") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// ubyte @shared ub1
|
||||||
|
//
|
||||||
|
// if ub1==42
|
||||||
|
// goto $c000
|
||||||
|
// if ub1>42
|
||||||
|
// goto $c000
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
|
||||||
|
val if1 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
|
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
|
if1.add(cmp1)
|
||||||
|
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if1.add(PtNodeGroup())
|
||||||
|
sub.add(if1)
|
||||||
|
val if2 = PtIfElse(Position.DUMMY)
|
||||||
|
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
|
||||||
|
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||||
|
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
|
||||||
|
if2.add(cmp2)
|
||||||
|
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
|
||||||
|
if2.add(PtNodeGroup())
|
||||||
|
sub.add(if2)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
test("romsub allowed in ir-codegen") {
|
||||||
|
//main {
|
||||||
|
// romsub $5000 = routine()
|
||||||
|
//
|
||||||
|
// sub start() {
|
||||||
|
// routine()
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
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 romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||||
|
block.add(romsub)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||||
|
sub.add(call)
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
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()
|
||||||
|
callInstr.opcode shouldBe Opcode.CALL
|
||||||
|
callInstr.address shouldBe 0x5000
|
||||||
|
}
|
||||||
})
|
})
|
@ -3,16 +3,17 @@ package prog8.optimizer
|
|||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.UndefinedSymbolError
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.maySwapOperandOrder
|
import prog8.ast.maySwapOperandOrder
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
|
import prog8.ast.statements.RepeatLoop
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.ast.statements.VarDeclType
|
import prog8.ast.statements.VarDeclType
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.AssociativeOperators
|
import prog8.code.core.AssociativeOperators
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||||
@ -307,7 +308,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
val rangeTo = iterableRange.to as? NumericLiteral
|
val rangeTo = iterableRange.to as? NumericLiteral
|
||||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||||
|
|
||||||
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar)
|
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
|
||||||
|
|
||||||
val stepLiteral = iterableRange.step as? NumericLiteral
|
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||||
when(loopvar.datatype) {
|
when(loopvar.datatype) {
|
||||||
@ -364,6 +365,16 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val count = (repeatLoop.iterations as? NumericLiteral)?.number
|
||||||
|
if(count!=null && floor(count)!=count) {
|
||||||
|
val integer = NumericLiteral.optimalInteger(count.toInt(), repeatLoop.position)
|
||||||
|
repeatLoop.iterations = integer
|
||||||
|
integer.linkParents(repeatLoop)
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
private class ShuffleOperands(val expr: BinaryExpression,
|
private class ShuffleOperands(val expr: BinaryExpression,
|
||||||
val exprOperator: String?,
|
val exprOperator: String?,
|
||||||
val subExpr: BinaryExpression,
|
val subExpr: BinaryExpression,
|
||||||
|
@ -53,7 +53,8 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(to==null) {
|
if(to==null) {
|
||||||
if(!range.to.inferType(program).isInteger)
|
val toType = range.to.inferType(program)
|
||||||
|
if(toType.isKnown && !range.to.inferType(program).isInteger)
|
||||||
errors.err("range expression to value must be integer", range.to.position)
|
errors.err("range expression to value must be integer", range.to.position)
|
||||||
} else if(to-to.toInt()>0) {
|
} else if(to-to.toInt()>0) {
|
||||||
errors.err("range expression to value must be integer", range.to.position)
|
errors.err("range expression to value must be integer", range.to.position)
|
||||||
@ -68,6 +69,124 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
|||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
// choose specific builtin function for the given types
|
||||||
|
val func = functionCallExpr.target.nameInSource
|
||||||
|
if(func==listOf("clamp")) {
|
||||||
|
val t1 = functionCallExpr.args[0].inferType(program)
|
||||||
|
if(t1.isKnown) {
|
||||||
|
val replaceFunc: String
|
||||||
|
if(t1.isBytes) {
|
||||||
|
replaceFunc = if(t1.istype(DataType.BYTE)) "clamp__byte" else "clamp__ubyte"
|
||||||
|
} else if(t1.isInteger) {
|
||||||
|
replaceFunc = if(t1.istype(DataType.WORD)) "clamp__word" else "clamp__uword"
|
||||||
|
} else {
|
||||||
|
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||||
|
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||||
|
functionCallExpr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(func==listOf("min") || func==listOf("max")) {
|
||||||
|
val t1 = functionCallExpr.args[0].inferType(program)
|
||||||
|
val t2 = functionCallExpr.args[1].inferType(program)
|
||||||
|
if(t1.isKnown && t2.isKnown) {
|
||||||
|
val funcName = func[0]
|
||||||
|
val replaceFunc: String
|
||||||
|
if(t1.isBytes && t2.isBytes) {
|
||||||
|
replaceFunc = if(t1.istype(DataType.BYTE) || t2.istype(DataType.BYTE))
|
||||||
|
"${funcName}__byte"
|
||||||
|
else
|
||||||
|
"${funcName}__ubyte"
|
||||||
|
} else if(t1.isInteger && t2.isInteger) {
|
||||||
|
replaceFunc = if(t1.istype(DataType.WORD) || t2.istype(DataType.WORD))
|
||||||
|
"${funcName}__word"
|
||||||
|
else
|
||||||
|
"${funcName}__uword"
|
||||||
|
} else if(t1.isNumeric && t2.isNumeric) {
|
||||||
|
errors.err("min/max not supported for floats", functionCallExpr.position)
|
||||||
|
return noModifications
|
||||||
|
} else {
|
||||||
|
errors.err("expected numeric arguments", functionCallExpr.args[0].position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||||
|
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||||
|
functionCallExpr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(func==listOf("abs")) {
|
||||||
|
val t1 = functionCallExpr.args[0].inferType(program)
|
||||||
|
if(t1.isKnown) {
|
||||||
|
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
|
||||||
|
val replaceFunc = when(dt) {
|
||||||
|
DataType.BYTE -> "abs__byte"
|
||||||
|
DataType.WORD -> "abs__word"
|
||||||
|
DataType.FLOAT -> "abs__float"
|
||||||
|
DataType.UBYTE, DataType.UWORD -> {
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
errors.err("expected numeric argument", functionCallExpr.args[0].position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||||
|
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||||
|
functionCallExpr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(func==listOf("sqrt")) {
|
||||||
|
val t1 = functionCallExpr.args[0].inferType(program)
|
||||||
|
if(t1.isKnown) {
|
||||||
|
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
|
||||||
|
val replaceFunc = when(dt) {
|
||||||
|
DataType.UBYTE -> "sqrt__ubyte"
|
||||||
|
DataType.UWORD -> "sqrt__uword"
|
||||||
|
DataType.FLOAT -> "sqrt__float"
|
||||||
|
else -> {
|
||||||
|
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
|
||||||
|
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
|
||||||
|
functionCallExpr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
// choose specific builtin function for the given types
|
||||||
|
val func = functionCallStatement.target.nameInSource
|
||||||
|
if(func==listOf("divmod")) {
|
||||||
|
val argTypes = functionCallStatement.args.map {it.inferType(program)}.toSet()
|
||||||
|
if(argTypes.size!=1) {
|
||||||
|
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
val t1 = argTypes.single()
|
||||||
|
if(t1.isKnown) {
|
||||||
|
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
|
||||||
|
val replaceFunc = when(dt) {
|
||||||
|
DataType.UBYTE -> "divmod__ubyte"
|
||||||
|
DataType.UWORD -> "divmod__uword"
|
||||||
|
else -> {
|
||||||
|
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.SetExpression({functionCallStatement.target = it as IdentifierReference},
|
||||||
|
IdentifierReference(listOf(replaceFunc), functionCallStatement.target.position),
|
||||||
|
functionCallStatement))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -87,8 +206,27 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
if(forloop!=null && identifier===forloop.loopVar)
|
if(forloop!=null && identifier===forloop.loopVar)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
|
val dt = identifier.inferType(program)
|
||||||
|
if(!dt.isKnown || !dt.isNumeric)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val cval = identifier.constValue(program) ?: return noModifications
|
val cval = identifier.constValue(program) ?: return noModifications
|
||||||
|
val arrayIdx = identifier.parent as? ArrayIndexedExpression
|
||||||
|
if(arrayIdx!=null && cval.type in NumericDatatypes) {
|
||||||
|
// special case when the identifier is used as a pointer var
|
||||||
|
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
|
||||||
|
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
|
||||||
|
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||||
|
return if(arrayIdx.parent is AssignTarget) {
|
||||||
|
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||||
|
val assignTarget = AssignTarget(null, null, memwrite, identifier.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||||
|
} else {
|
||||||
|
val memread = DirectMemoryRead(add, identifier.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
return when (cval.type) {
|
return when (cval.type) {
|
||||||
in NumericDatatypes -> listOf(
|
in NumericDatatypes -> listOf(
|
||||||
IAstModification.ReplaceNode(
|
IAstModification.ReplaceNode(
|
||||||
@ -138,7 +276,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> {
|
||||||
val rangeExpr = decl.value as? RangeExpression
|
val rangeExpr = decl.value as? RangeExpression
|
||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array
|
// convert the initializer range expression to an actual array
|
||||||
@ -223,6 +361,17 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// nothing to do for this type
|
// nothing to do for this type
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has
|
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has?
|
||||||
|
|
||||||
class ExpressionSimplifier(private val program: Program,
|
class ExpressionSimplifier(private val program: Program,
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
|
@ -43,9 +43,9 @@ fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget)
|
|||||||
|
|
||||||
fun Program.optimizeStatements(errors: IErrorReporter,
|
fun Program.optimizeStatements(errors: IErrorReporter,
|
||||||
functions: IBuiltinFunctions,
|
functions: IBuiltinFunctions,
|
||||||
compTarget: ICompilationTarget
|
options: CompilationOptions
|
||||||
): Int {
|
): Int {
|
||||||
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
|
val optimizer = StatementOptimizer(this, errors, functions, options)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
val optimizationCount = optimizer.applyModifications()
|
val optimizationCount = optimizer.applyModifications()
|
||||||
|
|
||||||
@ -54,8 +54,8 @@ fun Program.optimizeStatements(errors: IErrorReporter,
|
|||||||
return optimizationCount
|
return optimizationCount
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Program.inlineSubroutines(): Int {
|
fun Program.inlineSubroutines(options: CompilationOptions): Int {
|
||||||
val inliner = Inliner(this)
|
val inliner = Inliner(this, options)
|
||||||
inliner.visit(this)
|
inliner.visit(this)
|
||||||
return inliner.applyModifications()
|
return inliner.applyModifications()
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,9 @@ import prog8.ast.statements.*
|
|||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.InternalCompilerException
|
import prog8.code.core.InternalCompilerException
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
|
||||||
|
|
||||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
||||||
@ -16,7 +18,7 @@ private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.va
|
|||||||
|
|
||||||
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||||
|
|
||||||
class Inliner(val program: Program): AstWalker() {
|
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
|
||||||
|
|
||||||
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
||||||
private val modifications = mutableListOf<IAstModification>()
|
private val modifications = mutableListOf<IAstModification>()
|
||||||
@ -184,6 +186,7 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
}
|
}
|
||||||
return if(sub.isAsmSubroutine) {
|
return if(sub.isAsmSubroutine) {
|
||||||
// simply insert the asm for the argument-less routine
|
// simply insert the asm for the argument-less routine
|
||||||
|
sub.hasBeenInlined=true
|
||||||
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
|
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
|
||||||
} else {
|
} else {
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
@ -192,12 +195,16 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
val fcall = toInline.value as? FunctionCallExpression
|
val fcall = toInline.value as? FunctionCallExpression
|
||||||
if(fcall!=null) {
|
if(fcall!=null) {
|
||||||
// insert the function call expression as a void function call directly
|
// insert the function call expression as a void function call directly
|
||||||
|
sub.hasBeenInlined=true
|
||||||
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
||||||
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
||||||
} else
|
} else
|
||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
else -> listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
|
else -> {
|
||||||
|
sub.hasBeenInlined=true
|
||||||
|
listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +213,7 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||||
return if(sub==null)
|
return if(sub==null || !canInline(sub, functionCallStatement))
|
||||||
noModifications
|
noModifications
|
||||||
else
|
else
|
||||||
possibleInlineFcallStmt(sub, functionCallStatement, parent)
|
possibleInlineFcallStmt(sub, functionCallStatement, parent)
|
||||||
@ -214,7 +221,7 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
|
|
||||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
||||||
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
|
||||||
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
|
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
|
||||||
"invalid inline sub at ${sub.position}"
|
"invalid inline sub at ${sub.position}"
|
||||||
}
|
}
|
||||||
@ -226,8 +233,10 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
is Return -> {
|
is Return -> {
|
||||||
// is an expression, so we have to have a Return here in the inlined sub
|
// is an expression, so we have to have a Return here in the inlined sub
|
||||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||||
if(toInline.value!=null)
|
if(toInline.value!=null) {
|
||||||
|
sub.hasBeenInlined=true
|
||||||
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
||||||
|
}
|
||||||
else
|
else
|
||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
@ -239,5 +248,17 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean {
|
||||||
|
if(!sub.inline)
|
||||||
|
return false
|
||||||
|
if(options.compTarget.name!=VMTarget.NAME) {
|
||||||
|
val stmt = sub.statements.single()
|
||||||
|
if (stmt is IFunctionCall) {
|
||||||
|
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
|
||||||
|
return existing !is VarDecl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import kotlin.math.floor
|
|||||||
class StatementOptimizer(private val program: Program,
|
class StatementOptimizer(private val program: Program,
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val functions: IBuiltinFunctions,
|
private val functions: IBuiltinFunctions,
|
||||||
private val compTarget: ICompilationTarget
|
private val options: CompilationOptions
|
||||||
) : AstWalker() {
|
) : AstWalker() {
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
@ -39,7 +39,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if(string!=null) {
|
if(string!=null) {
|
||||||
val pos = functionCallStatement.position
|
val pos = functionCallStatement.position
|
||||||
if (string.value.length == 1) {
|
if (string.value.length == 1) {
|
||||||
val firstCharEncoded = compTarget.encodeString(string.value, string.encoding)[0]
|
val firstCharEncoded = options.compTarget.encodeString(string.value, string.encoding)[0]
|
||||||
val chrout = FunctionCallStatement(
|
val chrout = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), pos)),
|
mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), pos)),
|
||||||
@ -51,7 +51,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
|
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
|
||||||
)
|
)
|
||||||
} else if (string.value.length == 2) {
|
} else if (string.value.length == 2) {
|
||||||
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.encoding)
|
val firstTwoCharsEncoded = options.compTarget.encodeString(string.value.take(2), string.encoding)
|
||||||
val chrout1 = FunctionCallStatement(
|
val chrout1 = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
|
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
|
||||||
@ -108,6 +108,16 @@ class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove obvious dangling elses (else after a return)
|
||||||
|
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
|
||||||
|
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||||
|
if(options.slowCodegenWarnings)
|
||||||
|
errors.warn("else can be omitted", ifElse.elsepart.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||||
|
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||||
|
)
|
||||||
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +151,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
val size = sv.value.length
|
val size = sv.value.length
|
||||||
if(size==1) {
|
if(size==1) {
|
||||||
// loop over string of length 1 -> just assign the single character
|
// loop over string of length 1 -> just assign the single character
|
||||||
val character = compTarget.encodeString(sv.value, sv.encoding)[0]
|
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
|
||||||
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
|
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
|
||||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
|
||||||
@ -316,13 +326,13 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0 && compTarget.name!=VMTarget.NAME) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
|
||||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||||
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
||||||
}
|
}
|
||||||
listOf(IAstModification.ReplaceNode(assignment, if(incs.statements.size==1) incs.statements[0] else incs, parent))
|
return listOf(IAstModification.ReplaceNode(assignment, if(incs.statements.size==1) incs.statements[0] else incs, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -330,7 +340,7 @@ class StatementOptimizer(private val program: Program,
|
|||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0 && compTarget.name!=VMTarget.NAME) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
|
||||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
@ -374,4 +384,11 @@ class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun before(unrollLoop: UnrollLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
return if(unrollLoop.iterations<1)
|
||||||
|
listOf(IAstModification.Remove(unrollLoop, parent as IStatementContainer))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,20 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
if("force_output" !in block.options()) {
|
if("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars) {
|
if (block.containsNoCodeNorVars) {
|
||||||
if(block.name != internedStringsModuleName)
|
if(block.name != internedStringsModuleName) {
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||||
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
|
}
|
||||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
if(callgraph.unused(block)) {
|
if(callgraph.unused(block)) {
|
||||||
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR})
|
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR}) {
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||||
program.removeInternedStringsFromRemovedBlock(block)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
|
}
|
||||||
|
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
|
||||||
|
program.removeInternedStringsFromRemovedBlock(block)
|
||||||
|
}
|
||||||
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +87,7 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
val forceOutput = "force_output" in subroutine.definingBlock.options()
|
val forceOutput = "force_output" in subroutine.definingBlock.options()
|
||||||
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
|
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.isAsmSubroutine) {
|
||||||
if(callgraph.unused(subroutine)) {
|
if(callgraph.unused(subroutine)) {
|
||||||
if(subroutine.containsNoCodeNorVars) {
|
if(subroutine.containsNoCodeNorVars) {
|
||||||
if(!subroutine.definingModule.isLibrary)
|
if(!subroutine.definingModule.isLibrary)
|
||||||
@ -93,9 +99,12 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
}
|
}
|
||||||
return removals
|
return removals
|
||||||
}
|
}
|
||||||
if(!subroutine.definingModule.isLibrary)
|
if(!subroutine.definingModule.isLibrary && !subroutine.hasBeenInlined) {
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
program.removeInternedStringsFromRemovedSubroutine(subroutine)
|
}
|
||||||
|
if(!subroutine.inline) {
|
||||||
|
program.removeInternedStringsFromRemovedSubroutine(subroutine)
|
||||||
|
}
|
||||||
return listOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +119,7 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
|
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
|
||||||
val usages = callgraph.usages(decl)
|
val usages = callgraph.usages(decl)
|
||||||
if (usages.isEmpty()) {
|
if (usages.isEmpty()) {
|
||||||
// if(!decl.definingModule.isLibrary)
|
if(!decl.definingModule.isLibrary)
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
@ -118,8 +127,8 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
if(usages.size==1) {
|
if(usages.size==1) {
|
||||||
val singleUse = usages[0].parent
|
val singleUse = usages[0].parent
|
||||||
if(singleUse is AssignTarget) {
|
if(singleUse is AssignTarget) {
|
||||||
val assignment = singleUse.parent as Assignment
|
val assignment = singleUse.parent as? Assignment
|
||||||
if(assignment.origin==AssignmentOrigin.VARINIT) {
|
if(assignment!=null && assignment.origin==AssignmentOrigin.VARINIT) {
|
||||||
if(assignment.value.isSimple) {
|
if(assignment.value.isSimple) {
|
||||||
// remove the vardecl
|
// remove the vardecl
|
||||||
if(!decl.definingModule.isLibrary)
|
if(!decl.definingModule.isLibrary)
|
||||||
@ -150,6 +159,12 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(assignment.target isSameAs assignment.value)
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<Statement>, scope: IStatementContainer): List<IAstModification> {
|
private fun deduplicateAssignments(statements: List<Statement>, scope: IStatementContainer): List<IAstModification> {
|
||||||
// removes 'duplicate' assignments that assign the same target directly after another, unless it is a function call
|
// removes 'duplicate' assignments that assign the same target directly after another, unless it is a function call
|
||||||
val linesToRemove = mutableListOf<Assignment>()
|
val linesToRemove = mutableListOf<Assignment>()
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id "org.jetbrains.kotlin.jvm"
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
id 'com.github.johnrengelman.shadow' version '7.1.2'
|
id 'com.github.johnrengelman.shadow' version '7.1.2'
|
||||||
id "io.kotest" version "0.3.9"
|
id 'io.kotest' version '0.3.9'
|
||||||
|
id 'com.peterabeles.gversion' version '1.10.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@ -24,8 +25,6 @@ compileTestKotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
implementation project(':codeOptimizers')
|
implementation project(':codeOptimizers')
|
||||||
@ -34,14 +33,14 @@ dependencies {
|
|||||||
implementation project(':codeGenIntermediate')
|
implementation project(':codeGenIntermediate')
|
||||||
implementation project(':codeGenExperimental')
|
implementation project(':codeGenExperimental')
|
||||||
implementation project(':virtualmachine')
|
implementation project(':virtualmachine')
|
||||||
implementation 'org.antlr:antlr4-runtime:4.11.1'
|
implementation "org.antlr:antlr4-runtime:4.13.0"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
|
||||||
|
|
||||||
testImplementation project(':intermediate')
|
testImplementation project(':intermediate')
|
||||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
@ -82,7 +81,7 @@ application {
|
|||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archiveBaseName = 'prog8compiler'
|
archiveBaseName = 'prog8compiler'
|
||||||
archiveVersion = prog8version
|
archiveVersion = version
|
||||||
// minimize()
|
// minimize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,4 +99,16 @@ test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gversion {
|
||||||
|
srcDir = "src/" // path is relative to the sub-project by default
|
||||||
|
classPackage = "prog8.buildversion"
|
||||||
|
className = "BuildVersion"
|
||||||
|
language = "kotlin"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
build.finalizedBy installDist, installShadowDist
|
build.finalizedBy installDist, installShadowDist
|
||||||
|
|
||||||
|
compileKotlin.dependsOn createVersionFile // , failDirtyNotSnapshot
|
||||||
|
compileJava.dependsOn createVersionFile
|
||||||
|
|
||||||
|
@ -2,44 +2,41 @@
|
|||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
|
|
||||||
atari {
|
atari {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||||
|
|
||||||
; ---- kernal routines ----
|
|
||||||
; TODO
|
|
||||||
|
|
||||||
|
|
||||||
asmsub init_system() {
|
|
||||||
; Initializes the machine to a sane starting state.
|
|
||||||
; Called automatically by the loader program logic.
|
|
||||||
; TODO
|
|
||||||
%asm {{
|
|
||||||
sei
|
|
||||||
cld
|
|
||||||
clc
|
|
||||||
; TODO reset screen mode etc etc
|
|
||||||
clv
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub init_system_phase2() {
|
|
||||||
%asm {{
|
|
||||||
rts ; no phase 2 steps on the Atari
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sys {
|
sys {
|
||||||
; ------- lowlevel system routines --------
|
; ------- lowlevel system routines --------
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
|
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
|
||||||
|
|
||||||
|
asmsub init_system() {
|
||||||
|
; Initializes the machine to a sane starting state.
|
||||||
|
; Called automatically by the loader program logic.
|
||||||
|
; TODO
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
cld
|
||||||
|
clc
|
||||||
|
; TODO reset screen mode etc etc
|
||||||
|
clv
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub init_system_phase2() {
|
||||||
|
%asm {{
|
||||||
|
rts ; no phase 2 steps on the Atari
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub reset_system() {
|
asmsub reset_system() {
|
||||||
; Soft-reset the system back to initial power-on Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
@ -188,6 +185,19 @@ _longcopy
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_set_irqd() {
|
||||||
|
%asm {{
|
||||||
|
php
|
||||||
|
sei
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_clear_irqd() {
|
||||||
|
%asm {{
|
||||||
|
plp
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
inline asmsub exit(ubyte returnvalue @A) {
|
inline asmsub exit(ubyte returnvalue @A) {
|
||||||
; -- immediately exit the program with a return code in the A register
|
; -- immediately exit the program with a return code in the A register
|
||||||
; TODO
|
; TODO
|
||||||
@ -208,6 +218,7 @@ _longcopy
|
|||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||||
; they are simulated on the Atari as well but their location in memory is different
|
; they are simulated on the Atari as well but their location in memory is different
|
||||||
@ -313,4 +324,31 @@ cx16 {
|
|||||||
&byte r13sH = $1b1b
|
&byte r13sH = $1b1b
|
||||||
&byte r14sH = $1b1d
|
&byte r14sH = $1b1d
|
||||||
&byte r15sH = $1b1f
|
&byte r15sH = $1b1f
|
||||||
|
|
||||||
|
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||||
|
%asm {{
|
||||||
|
ldy #31
|
||||||
|
- lda cx16.r0,y
|
||||||
|
sta _cx16_vreg_storage,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
|
||||||
|
_cx16_vreg_storage
|
||||||
|
.word 0,0,0,0,0,0,0,0
|
||||||
|
.word 0,0,0,0,0,0,0,0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub restore_virtual_registers() clobbers(A,Y) {
|
||||||
|
%asm {{
|
||||||
|
ldy #31
|
||||||
|
- lda save_virtual_registers._cx16_vreg_storage,y
|
||||||
|
sta cx16.r0,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
txt {
|
txt {
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const ubyte DEFAULT_WIDTH = 40
|
const ubyte DEFAULT_WIDTH = 40
|
||||||
const ubyte DEFAULT_HEIGHT = 24
|
const ubyte DEFAULT_HEIGHT = 24
|
||||||
|
|
||||||
@ -70,7 +72,7 @@ sub uppercase() {
|
|||||||
; TODO
|
; TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||||
; ---- scroll the whole screen 1 character to the left
|
; ---- scroll the whole screen 1 character to the left
|
||||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -81,7 +83,7 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character to the right
|
; ---- scroll the whole screen 1 character to the right
|
||||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -91,7 +93,7 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character up
|
; ---- scroll the whole screen 1 character up
|
||||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -101,7 +103,7 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character down
|
; ---- scroll the whole screen 1 character down
|
||||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -118,7 +120,6 @@ romsub $F2Fd = waitkey()
|
|||||||
asmsub chrout(ubyte char @ A) {
|
asmsub chrout(ubyte char @ A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _tmp_outchar+1
|
sta _tmp_outchar+1
|
||||||
pha
|
|
||||||
txa
|
txa
|
||||||
pha
|
pha
|
||||||
tya
|
tya
|
||||||
@ -130,7 +131,6 @@ _tmp_outchar
|
|||||||
tay
|
tay
|
||||||
pla
|
pla
|
||||||
tax
|
tax
|
||||||
pla
|
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -210,7 +210,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
@ -228,7 +228,7 @@ asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
@ -249,7 +249,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
@ -261,7 +261,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -1,664 +0,0 @@
|
|||||||
; --- low level floating point assembly routines for the C128
|
|
||||||
; these are almost all identical to the C64 except for a few details
|
|
||||||
; so we have to have a separate library file for the C128 unfortunately.
|
|
||||||
|
|
||||||
|
|
||||||
FL_ONE_const .byte 129 ; 1.0
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
ub2float .proc
|
|
||||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
|
||||||
; clobbers A, Y
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
sty P8ZP_SCRATCH_W2+1
|
|
||||||
ldy P8ZP_SCRATCH_B1
|
|
||||||
lda #0
|
|
||||||
jsr GIVAYF
|
|
||||||
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
|
||||||
ldy P8ZP_SCRATCH_W2+1
|
|
||||||
jsr MOVMF
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
b2float .proc
|
|
||||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
|
||||||
; clobbers A, Y
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
sty P8ZP_SCRATCH_W2+1
|
|
||||||
lda P8ZP_SCRATCH_B1
|
|
||||||
jsr FREADSA
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
uw2float .proc
|
|
||||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
sty P8ZP_SCRATCH_W2+1
|
|
||||||
lda P8ZP_SCRATCH_W1
|
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
|
||||||
jsr GIVUAYFAY
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
w2float .proc
|
|
||||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
sty P8ZP_SCRATCH_W2+1
|
|
||||||
ldy P8ZP_SCRATCH_W1
|
|
||||||
lda P8ZP_SCRATCH_W1+1
|
|
||||||
jsr GIVAYF
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
cast_from_uw .proc
|
|
||||||
; -- uword in A/Y into float var at (P8ZP_SCRATCH_W2)
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr GIVUAYFAY
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
cast_from_w .proc
|
|
||||||
; -- word in A/Y into float var at (P8ZP_SCRATCH_W2)
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr GIVAYFAY
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
cast_from_ub .proc
|
|
||||||
; -- ubyte in Y into float var at (P8ZP_SCRATCH_W2)
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr FREADUY
|
|
||||||
jmp ub2float._fac_to_mem
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
cast_from_b .proc
|
|
||||||
; -- byte in A into float var at (P8ZP_SCRATCH_W2)
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
.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
|
|
||||||
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 SCRATCH_ZPWORD1,
|
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
|
||||||
sta _target+1
|
|
||||||
sty _target+2
|
|
||||||
ldy #4
|
|
||||||
_loop lda (P8ZP_SCRATCH_W1),y
|
|
||||||
_target sta $ffff,y ; modified
|
|
||||||
dey
|
|
||||||
bpl _loop
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
inc_var_f .proc
|
|
||||||
; -- add 1 to float pointed to by A/Y
|
|
||||||
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
|
|
||||||
.pend
|
|
||||||
|
|
||||||
dec_var_f .proc
|
|
||||||
; -- subtract 1 from float pointed to by A/Y
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
lda #<FL_ONE_const
|
|
||||||
ldy #>FL_ONE_const
|
|
||||||
jsr MOVFM
|
|
||||||
lda P8ZP_SCRATCH_W1
|
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
|
||||||
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
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
jsr FCOMP
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
cmp #255
|
|
||||||
beq +
|
|
||||||
lda #0
|
|
||||||
rts
|
|
||||||
+ lda #1
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
var_fac1_lesseq_f .proc
|
|
||||||
; -- is the float in FAC1 <= the variable AY?
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr FCOMP
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
cmp #0
|
|
||||||
beq +
|
|
||||||
cmp #255
|
|
||||||
beq +
|
|
||||||
lda #0
|
|
||||||
rts
|
|
||||||
+ lda #1
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
var_fac1_greater_f .proc
|
|
||||||
; -- is the float in FAC1 > the variable AY?
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr FCOMP
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
cmp #1
|
|
||||||
beq +
|
|
||||||
lda #0
|
|
||||||
rts
|
|
||||||
+ lda #1
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
var_fac1_greatereq_f .proc
|
|
||||||
; -- is the float in FAC1 >= the variable AY?
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr FCOMP
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
cmp #0
|
|
||||||
beq +
|
|
||||||
cmp #1
|
|
||||||
beq +
|
|
||||||
lda #0
|
|
||||||
rts
|
|
||||||
+ lda #1
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
var_fac1_notequal_f .proc
|
|
||||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
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?
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
sty P8ZP_SCRATCH_W2+1
|
|
||||||
ldy #0
|
|
||||||
lda (P8ZP_SCRATCH_W1),y
|
|
||||||
cmp (P8ZP_SCRATCH_W2),y
|
|
||||||
bne _false
|
|
||||||
iny
|
|
||||||
lda (P8ZP_SCRATCH_W1),y
|
|
||||||
cmp (P8ZP_SCRATCH_W2),y
|
|
||||||
bne _false
|
|
||||||
iny
|
|
||||||
lda (P8ZP_SCRATCH_W1),y
|
|
||||||
cmp (P8ZP_SCRATCH_W2),y
|
|
||||||
bne _false
|
|
||||||
iny
|
|
||||||
lda (P8ZP_SCRATCH_W1),y
|
|
||||||
cmp (P8ZP_SCRATCH_W2),y
|
|
||||||
bne _false
|
|
||||||
iny
|
|
||||||
lda (P8ZP_SCRATCH_W1),y
|
|
||||||
cmp (P8ZP_SCRATCH_W2),y
|
|
||||||
bne _false
|
|
||||||
lda #1
|
|
||||||
rts
|
|
||||||
_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 ?
|
|
||||||
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
|
|
||||||
rts
|
|
||||||
+ lda #0
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
vars_lesseq_f .proc
|
|
||||||
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ?
|
|
||||||
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
|
|
||||||
rts
|
|
||||||
+ cmp #0
|
|
||||||
beq -
|
|
||||||
lda #0
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
less_f .proc
|
|
||||||
; -- is f1 < f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #255
|
|
||||||
beq compare_floats._return_true
|
|
||||||
bne compare_floats._return_false
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
lesseq_f .proc
|
|
||||||
; -- is f1 <= f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #255
|
|
||||||
beq compare_floats._return_true
|
|
||||||
cmp #0
|
|
||||||
beq compare_floats._return_true
|
|
||||||
bne compare_floats._return_false
|
|
||||||
.pend
|
|
||||||
|
|
||||||
greater_f .proc
|
|
||||||
; -- is f1 > f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #1
|
|
||||||
beq compare_floats._return_true
|
|
||||||
bne compare_floats._return_false
|
|
||||||
.pend
|
|
||||||
|
|
||||||
greatereq_f .proc
|
|
||||||
; -- is f1 >= f2?
|
|
||||||
jsr compare_floats
|
|
||||||
cmp #1
|
|
||||||
beq compare_floats._return_true
|
|
||||||
cmp #0
|
|
||||||
beq compare_floats._return_true
|
|
||||||
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)
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc P8ZP_SCRATCH_B1
|
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
|
||||||
clc
|
|
||||||
adc P8ZP_SCRATCH_W1
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ stx floats_store_reg
|
|
||||||
tax
|
|
||||||
jsr MOVMF
|
|
||||||
ldx floats_store_reg
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
set_0_array_float .proc
|
|
||||||
; -- set a float in an array to zero (index in A, array in P8ZP_SCRATCH_W1)
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc P8ZP_SCRATCH_B1
|
|
||||||
tay
|
|
||||||
lda #0
|
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
|
||||||
iny
|
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
|
||||||
iny
|
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
|
||||||
iny
|
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
|
||||||
iny
|
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
set_array_float .proc
|
|
||||||
; -- set a float in an array to a value (index in A, float in P8ZP_SCRATCH_W1, array in P8ZP_SCRATCH_W2)
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc P8ZP_SCRATCH_B1
|
|
||||||
adc P8ZP_SCRATCH_W2
|
|
||||||
ldy P8ZP_SCRATCH_W2+1
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jmp copy_float
|
|
||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
|||||||
; Prog8 definitions for floating point handling on the Commodore 128
|
|
||||||
|
|
||||||
%option enable_floats
|
|
||||||
%import floats_functions
|
|
||||||
|
|
||||||
floats {
|
|
||||||
; ---- this block contains C-128 compatible floating point related functions ----
|
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
|
||||||
const float TWOPI = 6.283185307179586
|
|
||||||
|
|
||||||
|
|
||||||
; ---- ROM float functions ----
|
|
||||||
|
|
||||||
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
|
||||||
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
|
||||||
|
|
||||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
|
||||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
|
||||||
|
|
||||||
romsub $af00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 102-103 ($66-$67) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
|
||||||
|
|
||||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
|
||||||
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
|
||||||
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
|
||||||
romsub $af03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
|
||||||
|
|
||||||
romsub $af06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
|
||||||
; romsub $af09 = VAL_1() clobbers(A,X,Y) ; convert ASCII string to floating point [not yet implemented!!!]
|
|
||||||
|
|
||||||
; 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)
|
|
||||||
romsub $af0c = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
|
||||||
romsub $af0f = FLOATC() clobbers(A,X,Y) ; convert address to floating point
|
|
||||||
|
|
||||||
romsub $af12 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
|
||||||
romsub $af15 = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands NOTE: use FSUBT2() instead!
|
|
||||||
romsub $af18 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
|
|
||||||
romsub $af1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2 NOTE: use FADDT2() instead!
|
|
||||||
romsub $af1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
|
||||||
romsub $af21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2 NOTE: use FMULTT2() instead!
|
|
||||||
romsub $af24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
|
||||||
romsub $af27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands NOTE: use FDIVT2() instead!
|
|
||||||
romsub $af2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
|
||||||
romsub $af2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
|
||||||
romsub $af30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
|
||||||
romsub $af33 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
|
||||||
romsub $af36 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** float in A/Y
|
|
||||||
romsub $af39 = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1 NOTE: use FPWRT2() instead!
|
|
||||||
romsub $af3c = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
|
||||||
romsub $af3f = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
|
||||||
romsub $af42 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
|
||||||
romsub $af45 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
|
||||||
romsub $af48 = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
|
||||||
romsub $af4b = ROUND() clobbers(A,X,Y) ; round fac1
|
|
||||||
romsub $af4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
|
|
||||||
romsub $af51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
|
||||||
romsub $af54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
|
||||||
romsub $af57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
|
||||||
romsub $af57 = RND() clobbers(A,X,Y) ; alias for RND_0
|
|
||||||
romsub $af5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
|
|
||||||
romsub $af5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2
|
|
||||||
romsub $af60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead)
|
|
||||||
romsub $af63 = MOVFM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1
|
|
||||||
romsub $af66 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
|
||||||
romsub $af69 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
|
||||||
romsub $af6c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
|
||||||
|
|
||||||
; X16 additions
|
|
||||||
romsub $af6f = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
|
||||||
romsub $af72 = FADDT2() clobbers(A,X,Y) ; fac1 += fac2
|
|
||||||
romsub $af75 = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
|
|
||||||
romsub $af78 = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
|
|
||||||
romsub $af7b = NEGFAC() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1) (juse use NEGOP() instead!)
|
|
||||||
romsub $af7e = FMULTT2() clobbers(A,X,Y) ; fac1 *= fac2
|
|
||||||
romsub $af81 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
|
||||||
romsub $af84 = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
|
||||||
romsub $af87 = FDIVT2() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
|
||||||
romsub $af8a = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
|
||||||
romsub $af8d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
|
||||||
romsub $af90 = FLOAT() clobbers(A,X,Y) ; FAC = (u8).A
|
|
||||||
romsub $af93 = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
|
||||||
romsub $af9C = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
|
||||||
romsub $af9f = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
|
||||||
romsub $afa5 = FPWRT2() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
|
||||||
|
|
||||||
asmsub FREADSA (byte value @A) clobbers(A,X,Y) {
|
|
||||||
; ---- 8 bit signed A -> float in fac1
|
|
||||||
%asm {{
|
|
||||||
tay
|
|
||||||
bpl +
|
|
||||||
lda #$ff
|
|
||||||
jmp GIVAYF
|
|
||||||
+ lda #0
|
|
||||||
jmp GIVAYF
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|
||||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
sty $64 ; facmo
|
|
||||||
sta $65 ; facmo+1
|
|
||||||
ldx #$90
|
|
||||||
sec
|
|
||||||
jsr FLOATC
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|
||||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
|
||||||
%asm {{
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
tya
|
|
||||||
ldy P8ZP_SCRATCH_B1
|
|
||||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|
||||||
; ---- fac1 to unsigned word in A/Y
|
|
||||||
%asm {{
|
|
||||||
jsr GETADR ; this uses the inverse order, Y/A
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
tya
|
|
||||||
ldy P8ZP_SCRATCH_B1
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub FREADUY (ubyte value @Y) {
|
|
||||||
; -- 8 bit unsigned Y -> float in fac1
|
|
||||||
%asm {{
|
|
||||||
tya
|
|
||||||
jmp FLOAT
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
&uword AYINT_facmo = $66 ; $66/$67 contain result of AYINT
|
|
||||||
|
|
||||||
sub rndf() -> float {
|
|
||||||
%asm {{
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
lda #1
|
|
||||||
jsr RND_0
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
%asminclude "library:c128/floats.asm"
|
|
||||||
%asminclude "library:c64/floats_funcs.asm"
|
|
||||||
|
|
||||||
}
|
|
@ -6,6 +6,8 @@
|
|||||||
; TODO c128 actually implement the graphics routines. Ideally a way to 'borrow' the code form the C64 version without just copy-pasting that here?
|
; TODO c128 actually implement the graphics routines. Ideally a way to 'borrow' the code form the C64 version without just copy-pasting that here?
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const uword WIDTH = 320
|
const uword WIDTH = 320
|
||||||
const ubyte HEIGHT = 200
|
const ubyte HEIGHT = 200
|
||||||
|
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
; Prog8 definitions for the Commodore-128
|
; Prog8 definitions for the Commodore-128
|
||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
|
|
||||||
c64 {
|
cbm {
|
||||||
|
; Commodore (CBM) common variables, vectors and kernal routines
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
&ubyte STATUS = $90 ; kernal status variable for I/O
|
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
;;&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ) // TODO c128 ??
|
&ubyte SHFLAG = $d3 ; various modifier key status (updated by IRQ)
|
||||||
|
&ubyte SFDX = $d4 ; current key pressed (matrix value) (updated by IRQ)
|
||||||
&ubyte COLOR = $00f1 ; cursor color
|
&ubyte COLOR = $f1 ; cursor color
|
||||||
;;&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address) // TODO c128 ??
|
|
||||||
|
|
||||||
&uword IERROR = $0300
|
&uword IERROR = $0300
|
||||||
&uword IMAIN = $0302
|
&uword IMAIN = $0302
|
||||||
@ -48,6 +49,100 @@ c64 {
|
|||||||
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||||
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||||
|
|
||||||
|
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||||
|
|
||||||
|
; STROUT --> use txt.print
|
||||||
|
; CLEARSCR -> use txt.clear_screen
|
||||||
|
; HOMECRSR -> use txt.home or txt.plot
|
||||||
|
|
||||||
|
romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||||
|
romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||||
|
|
||||||
|
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
|
||||||
|
|
||||||
|
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||||
|
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||||
|
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||||
|
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||||
|
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||||
|
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||||
|
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||||
|
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||||
|
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||||
|
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||||
|
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
|
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||||
|
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||||
|
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||||
|
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||||
|
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||||
|
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||||
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
|
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||||
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
|
||||||
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
|
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||||
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||||
|
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||||
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||||
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
|
; ---- end of C64 compatible ROM kernal routines ----
|
||||||
|
|
||||||
|
; ---- utilities -----
|
||||||
|
|
||||||
|
asmsub STOP2() -> ubyte @A {
|
||||||
|
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||||
|
%asm {{
|
||||||
|
txa
|
||||||
|
pha
|
||||||
|
jsr cbm.STOP
|
||||||
|
beq +
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
lda #0
|
||||||
|
rts
|
||||||
|
+ pla
|
||||||
|
tax
|
||||||
|
lda #1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub RDTIM16() -> uword @AY {
|
||||||
|
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr cbm.RDTIM
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
c64 {
|
||||||
|
; C64 I/O registers (VIC, SID, CIA)
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||||
&ubyte SPRPTR0 = 2040
|
&ubyte SPRPTR0 = 2040
|
||||||
&ubyte SPRPTR1 = 2041
|
&ubyte SPRPTR1 = 2041
|
||||||
@ -197,276 +292,11 @@ c64 {
|
|||||||
|
|
||||||
; ---- end of SID registers ----
|
; ---- end of SID registers ----
|
||||||
|
|
||||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
|
||||||
|
|
||||||
; STROUT --> use txt.print
|
|
||||||
; CLEARSCR -> use txt.clear_screen
|
|
||||||
; HOMECRSR -> use txt.home or txt.plot
|
|
||||||
|
|
||||||
romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
|
||||||
romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
|
||||||
|
|
||||||
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
|
|
||||||
|
|
||||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
|
||||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
|
||||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
|
||||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
|
||||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
|
||||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
|
||||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
|
||||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
|
||||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
|
||||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
|
||||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
|
||||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
|
||||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
|
||||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
|
||||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
|
||||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
|
||||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
|
||||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
|
||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
|
||||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
|
||||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
|
||||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
|
||||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
|
||||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
|
||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
|
||||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
|
||||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
|
||||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
|
||||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
|
||||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
|
||||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
|
||||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
|
||||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
|
||||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
|
||||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
|
||||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
|
||||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
|
||||||
|
|
||||||
; ---- end of C64 compatible ROM kernal routines ----
|
|
||||||
|
|
||||||
; ---- utilities -----
|
|
||||||
|
|
||||||
asmsub STOP2() -> ubyte @A {
|
|
||||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
|
||||||
%asm {{
|
|
||||||
txa
|
|
||||||
pha
|
|
||||||
jsr c64.STOP
|
|
||||||
beq +
|
|
||||||
pla
|
|
||||||
tax
|
|
||||||
lda #0
|
|
||||||
rts
|
|
||||||
+ pla
|
|
||||||
tax
|
|
||||||
lda #1
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub RDTIM16() -> uword @AY {
|
|
||||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
|
||||||
%asm {{
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr c64.RDTIM
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
tay
|
|
||||||
pla
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
; ---- system utility routines that are essentially the same as on the C64: -----
|
|
||||||
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
|
||||||
%asm {{
|
|
||||||
lda #$80
|
|
||||||
sta 247 ; disable charset switching
|
|
||||||
lda #112
|
|
||||||
sta 808 ; disable run/stop key
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub enable_runstop_and_charsetswitch() clobbers(A) {
|
|
||||||
%asm {{
|
|
||||||
lda #0
|
|
||||||
sta 247 ; enable charset switching
|
|
||||||
lda #110
|
|
||||||
sta 808 ; enable run/stop key
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
|
||||||
%asm {{
|
|
||||||
sta _modified+1
|
|
||||||
sty _modified+2
|
|
||||||
lda #0
|
|
||||||
adc #0
|
|
||||||
sta _use_kernal
|
|
||||||
sei
|
|
||||||
lda #<_irq_handler
|
|
||||||
sta c64.CINV
|
|
||||||
lda #>_irq_handler
|
|
||||||
sta c64.CINV+1
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
_irq_handler jsr _irq_handler_init
|
|
||||||
_modified jsr $ffff ; modified
|
|
||||||
jsr _irq_handler_end
|
|
||||||
lda _use_kernal
|
|
||||||
bne +
|
|
||||||
lda #$ff
|
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
|
||||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
|
||||||
; end irq processing - don't use kernal's irq handling
|
|
||||||
pla
|
|
||||||
tay
|
|
||||||
pla
|
|
||||||
tax
|
|
||||||
pla
|
|
||||||
rti
|
|
||||||
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
|
|
||||||
|
|
||||||
_use_kernal .byte 0
|
|
||||||
|
|
||||||
_irq_handler_init
|
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
|
||||||
stx IRQ_X_REG
|
|
||||||
lda P8ZP_SCRATCH_B1
|
|
||||||
sta IRQ_SCRATCH_ZPB1
|
|
||||||
lda P8ZP_SCRATCH_REG
|
|
||||||
sta IRQ_SCRATCH_ZPREG
|
|
||||||
lda P8ZP_SCRATCH_W1
|
|
||||||
sta IRQ_SCRATCH_ZPWORD1
|
|
||||||
lda P8ZP_SCRATCH_W1+1
|
|
||||||
sta IRQ_SCRATCH_ZPWORD1+1
|
|
||||||
lda P8ZP_SCRATCH_W2
|
|
||||||
sta IRQ_SCRATCH_ZPWORD2
|
|
||||||
lda P8ZP_SCRATCH_W2+1
|
|
||||||
sta IRQ_SCRATCH_ZPWORD2+1
|
|
||||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
cld
|
|
||||||
rts
|
|
||||||
|
|
||||||
_irq_handler_end
|
|
||||||
; restore all zp scratch registers and the X register
|
|
||||||
lda IRQ_SCRATCH_ZPB1
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
lda IRQ_SCRATCH_ZPREG
|
|
||||||
sta P8ZP_SCRATCH_REG
|
|
||||||
lda IRQ_SCRATCH_ZPWORD1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda IRQ_SCRATCH_ZPWORD1+1
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda IRQ_SCRATCH_ZPWORD2
|
|
||||||
sta P8ZP_SCRATCH_W2
|
|
||||||
lda IRQ_SCRATCH_ZPWORD2+1
|
|
||||||
sta P8ZP_SCRATCH_W2+1
|
|
||||||
ldx IRQ_X_REG
|
|
||||||
rts
|
|
||||||
|
|
||||||
IRQ_X_REG .byte 0
|
|
||||||
IRQ_SCRATCH_ZPB1 .byte 0
|
|
||||||
IRQ_SCRATCH_ZPREG .byte 0
|
|
||||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
|
||||||
IRQ_SCRATCH_ZPWORD2 .word 0
|
|
||||||
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub restore_irq() clobbers(A) {
|
|
||||||
%asm {{
|
|
||||||
sei
|
|
||||||
lda #<c64.IRQDFRT
|
|
||||||
sta c64.CINV
|
|
||||||
lda #>c64.IRQDFRT
|
|
||||||
sta c64.CINV+1
|
|
||||||
lda #0
|
|
||||||
sta c64.IREQMASK ; disable raster irq
|
|
||||||
lda #%10000001
|
|
||||||
sta c64.CIA1ICR ; restore CIA1 irq
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
|
||||||
%asm {{
|
|
||||||
sta _modified+1
|
|
||||||
sty _modified+2
|
|
||||||
lda #0
|
|
||||||
adc #0
|
|
||||||
sta set_irq._use_kernal
|
|
||||||
lda cx16.r0
|
|
||||||
ldy cx16.r0+1
|
|
||||||
sei
|
|
||||||
jsr _setup_raster_irq
|
|
||||||
lda #<_raster_irq_handler
|
|
||||||
sta c64.CINV
|
|
||||||
lda #>_raster_irq_handler
|
|
||||||
sta c64.CINV+1
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
|
|
||||||
_raster_irq_handler
|
|
||||||
jsr set_irq._irq_handler_init
|
|
||||||
_modified jsr $ffff ; modified
|
|
||||||
jsr set_irq._irq_handler_end
|
|
||||||
lda #$ff
|
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
|
||||||
lda set_irq._use_kernal
|
|
||||||
bne +
|
|
||||||
; end irq processing - don't use kernal's irq handling
|
|
||||||
pla
|
|
||||||
tay
|
|
||||||
pla
|
|
||||||
tax
|
|
||||||
pla
|
|
||||||
rti
|
|
||||||
+ jmp c64.IRQDFRT ; continue with kernal irq routine
|
|
||||||
|
|
||||||
_setup_raster_irq
|
|
||||||
pha
|
|
||||||
lda #%01111111
|
|
||||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
|
||||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
|
||||||
and c64.SCROLY
|
|
||||||
sta c64.SCROLY ; clear most significant bit of raster position
|
|
||||||
lda c64.CIA1ICR ; ack previous irq
|
|
||||||
lda c64.CIA2ICR ; ack previous irq
|
|
||||||
pla
|
|
||||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
|
||||||
cpy #0
|
|
||||||
beq +
|
|
||||||
lda c64.SCROLY
|
|
||||||
ora #%10000000
|
|
||||||
sta c64.SCROLY ; set most significant bit of raster position
|
|
||||||
+ lda #%00000001
|
|
||||||
sta c64.IREQMASK ;enable raster interrupt signals from vic
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c128 {
|
c128 {
|
||||||
; ---- C128 specific registers ----
|
; ---- C128 specific registers ----
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
|
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
|
||||||
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
|
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
|
||||||
@ -476,50 +306,6 @@ c128 {
|
|||||||
|
|
||||||
; ---- C128 specific system utility routines: ----
|
; ---- C128 specific system utility routines: ----
|
||||||
|
|
||||||
asmsub init_system() {
|
|
||||||
; Initializes the machine to a sane starting state.
|
|
||||||
; Called automatically by the loader program logic.
|
|
||||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
|
||||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
|
||||||
; Also a different color scheme is chosen to identify ourselves a little.
|
|
||||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
|
||||||
%asm {{
|
|
||||||
sei
|
|
||||||
cld
|
|
||||||
;;lda #%00101111 ; TODO c128 ram and rom bank selection how?
|
|
||||||
;;sta $00
|
|
||||||
;;lda #%00100111
|
|
||||||
;;sta $01
|
|
||||||
jsr c64.IOINIT
|
|
||||||
jsr c64.RESTOR
|
|
||||||
jsr c64.CINT
|
|
||||||
lda #6
|
|
||||||
sta c64.EXTCOL
|
|
||||||
lda #7
|
|
||||||
sta c64.COLOR
|
|
||||||
lda #0
|
|
||||||
sta c64.BGCOL0
|
|
||||||
jsr c64.disable_runstop_and_charsetswitch
|
|
||||||
clc
|
|
||||||
clv
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub init_system_phase2() {
|
|
||||||
%asm {{
|
|
||||||
rts ; no phase 2 steps on the C128
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub cleanup_at_exit() {
|
|
||||||
; executed when the main subroutine does rts
|
|
||||||
%asm {{
|
|
||||||
jmp c64.enable_runstop_and_charsetswitch
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub disable_basic() clobbers(A) {
|
asmsub disable_basic() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda $0a04 ; disable BASIC shadow registers
|
lda $0a04 ; disable BASIC shadow registers
|
||||||
@ -547,17 +333,230 @@ asmsub disable_basic() clobbers(A) {
|
|||||||
|
|
||||||
sys {
|
sys {
|
||||||
; ------- lowlevel system routines --------
|
; ------- lowlevel system routines --------
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||||
|
|
||||||
|
asmsub init_system() {
|
||||||
|
; Initializes the machine to a sane starting state.
|
||||||
|
; Called automatically by the loader program logic.
|
||||||
|
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||||
|
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||||
|
; Also a different color scheme is chosen to identify ourselves a little.
|
||||||
|
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
cld
|
||||||
|
lda #0
|
||||||
|
sta $ff00 ; select default bank 15
|
||||||
|
jsr cbm.IOINIT
|
||||||
|
jsr cbm.RESTOR
|
||||||
|
jsr cbm.CINT
|
||||||
|
lda #6
|
||||||
|
sta c64.EXTCOL
|
||||||
|
lda #7
|
||||||
|
sta cbm.COLOR
|
||||||
|
lda #0
|
||||||
|
sta c64.BGCOL0
|
||||||
|
jsr disable_runstop_and_charsetswitch
|
||||||
|
clc
|
||||||
|
clv
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub init_system_phase2() {
|
||||||
|
%asm {{
|
||||||
|
rts ; no phase 2 steps on the C128
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub cleanup_at_exit() {
|
||||||
|
; executed when the main subroutine does rts
|
||||||
|
%asm {{
|
||||||
|
jmp sys.enable_runstop_and_charsetswitch
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
lda #$80
|
||||||
|
sta 247 ; disable charset switching
|
||||||
|
lda #112
|
||||||
|
sta 808 ; disable run/stop key
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub enable_runstop_and_charsetswitch() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
lda #0
|
||||||
|
sta 247 ; enable charset switching
|
||||||
|
lda #110
|
||||||
|
sta 808 ; enable run/stop key
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda #0
|
||||||
|
rol a
|
||||||
|
sta _use_kernal
|
||||||
|
sei
|
||||||
|
lda #<_irq_handler
|
||||||
|
sta cbm.CINV
|
||||||
|
lda #>_irq_handler
|
||||||
|
sta cbm.CINV+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
_irq_handler jsr _irq_handler_init
|
||||||
|
_modified jsr $ffff ; modified
|
||||||
|
jsr _irq_handler_end
|
||||||
|
lda _use_kernal
|
||||||
|
bne +
|
||||||
|
lda #$ff
|
||||||
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
|
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||||
|
; end irq processing - don't use kernal's irq handling
|
||||||
|
pla
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
pla
|
||||||
|
rti
|
||||||
|
+ jmp cbm.IRQDFRT ; continue with normal kernal irq routine
|
||||||
|
|
||||||
|
_use_kernal .byte 0
|
||||||
|
|
||||||
|
_irq_handler_init
|
||||||
|
; save all zp scratch registers as these might be clobbered by the irq routine
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
sta IRQ_SCRATCH_ZPB1
|
||||||
|
lda P8ZP_SCRATCH_REG
|
||||||
|
sta IRQ_SCRATCH_ZPREG
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD1
|
||||||
|
lda P8ZP_SCRATCH_W1+1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD1+1
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
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
|
||||||
|
|
||||||
|
_irq_handler_end
|
||||||
|
; restore all zp scratch registers
|
||||||
|
lda IRQ_SCRATCH_ZPB1
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
lda IRQ_SCRATCH_ZPREG
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
lda IRQ_SCRATCH_ZPWORD1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda IRQ_SCRATCH_ZPWORD1+1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda IRQ_SCRATCH_ZPWORD2
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
rts
|
||||||
|
|
||||||
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
|
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub restore_irq() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #<cbm.IRQDFRT
|
||||||
|
sta cbm.CINV
|
||||||
|
lda #>cbm.IRQDFRT
|
||||||
|
sta cbm.CINV+1
|
||||||
|
lda #0
|
||||||
|
sta c64.IREQMASK ; disable raster irq
|
||||||
|
lda #%10000001
|
||||||
|
sta c64.CIA1ICR ; restore CIA1 irq
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda #0
|
||||||
|
rol a
|
||||||
|
sta set_irq._use_kernal
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
sei
|
||||||
|
jsr _setup_raster_irq
|
||||||
|
lda #<_raster_irq_handler
|
||||||
|
sta cbm.CINV
|
||||||
|
lda #>_raster_irq_handler
|
||||||
|
sta cbm.CINV+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
|
||||||
|
_raster_irq_handler
|
||||||
|
jsr set_irq._irq_handler_init
|
||||||
|
_modified jsr $ffff ; modified
|
||||||
|
jsr set_irq._irq_handler_end
|
||||||
|
lda #$ff
|
||||||
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
|
lda set_irq._use_kernal
|
||||||
|
bne +
|
||||||
|
; end irq processing - don't use kernal's irq handling
|
||||||
|
pla
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
pla
|
||||||
|
rti
|
||||||
|
+ jmp cbm.IRQDFRT ; continue with kernal irq routine
|
||||||
|
|
||||||
|
_setup_raster_irq
|
||||||
|
pha
|
||||||
|
lda #%01111111
|
||||||
|
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||||
|
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||||
|
and c64.SCROLY
|
||||||
|
sta c64.SCROLY ; clear most significant bit of raster position
|
||||||
|
lda c64.CIA1ICR ; ack previous irq
|
||||||
|
lda c64.CIA2ICR ; ack previous irq
|
||||||
|
pla
|
||||||
|
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||||
|
cpy #0
|
||||||
|
beq +
|
||||||
|
lda c64.SCROLY
|
||||||
|
ora #%10000000
|
||||||
|
sta c64.SCROLY ; set most significant bit of raster position
|
||||||
|
+ lda #%00000001
|
||||||
|
sta c64.IREQMASK ;enable raster interrupt signals from vic
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub reset_system() {
|
asmsub reset_system() {
|
||||||
; Soft-reset the system back to initial power-on Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
;lda #14
|
lda #0
|
||||||
;sta $01 ; bank the kernal in TODO c128 how to do this?
|
sta $ff00 ; default bank 15
|
||||||
jmp (c64.RESET_VEC)
|
jmp (cbm.RESET_VEC)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,9 +574,9 @@ _loop lda P8ZP_SCRATCH_W1
|
|||||||
ldx P8ZP_SCRATCH_B1
|
ldx P8ZP_SCRATCH_B1
|
||||||
rts
|
rts
|
||||||
|
|
||||||
+ lda c64.TIME_LO
|
+ lda cbm.TIME_LO
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda c64.TIME_LO
|
- lda cbm.TIME_LO
|
||||||
cmp P8ZP_SCRATCH_B1
|
cmp P8ZP_SCRATCH_B1
|
||||||
beq -
|
beq -
|
||||||
|
|
||||||
@ -734,13 +733,26 @@ _longcopy
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_set_irqd() {
|
||||||
|
%asm {{
|
||||||
|
php
|
||||||
|
sei
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_clear_irqd() {
|
||||||
|
%asm {{
|
||||||
|
plp
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
inline asmsub exit(ubyte returnvalue @A) {
|
inline asmsub exit(ubyte returnvalue @A) {
|
||||||
; -- immediately exit the program with a return code in the A register
|
; -- immediately exit the program with a return code in the A register
|
||||||
%asm {{
|
%asm {{
|
||||||
;lda #14
|
lda #0
|
||||||
;sta $01 ; bank the kernal in TODO c128 how to do this?
|
sta $ff00 ; default bank 15
|
||||||
jsr c64.CLRCHN ; reset i/o channels
|
jsr cbm.CLRCHN ; reset i/o channels
|
||||||
jsr c64.enable_runstop_and_charsetswitch
|
jsr sys.enable_runstop_and_charsetswitch
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
txs
|
txs
|
||||||
rts ; return to original caller
|
rts ; return to original caller
|
||||||
@ -757,6 +769,7 @@ _longcopy
|
|||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||||
; they are simulated on the C128 as well but their location in memory is different
|
; they are simulated on the C128 as well but their location in memory is different
|
||||||
@ -866,4 +879,30 @@ cx16 {
|
|||||||
&byte r13sH = $1b1b
|
&byte r13sH = $1b1b
|
||||||
&byte r14sH = $1b1d
|
&byte r14sH = $1b1d
|
||||||
&byte r15sH = $1b1f
|
&byte r15sH = $1b1f
|
||||||
|
|
||||||
|
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||||
|
%asm {{
|
||||||
|
ldy #31
|
||||||
|
- lda cx16.r0,y
|
||||||
|
sta _cx16_vreg_storage,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
|
||||||
|
_cx16_vreg_storage
|
||||||
|
.word 0,0,0,0,0,0,0,0
|
||||||
|
.word 0,0,0,0,0,0,0,0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub restore_virtual_registers() clobbers(A,Y) {
|
||||||
|
%asm {{
|
||||||
|
ldy #31
|
||||||
|
- lda save_virtual_registers._cx16_vreg_storage,y
|
||||||
|
sta cx16.r0,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
txt {
|
txt {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const ubyte DEFAULT_WIDTH = 40
|
const ubyte DEFAULT_WIDTH = 40
|
||||||
const ubyte DEFAULT_HEIGHT = 25
|
const ubyte DEFAULT_HEIGHT = 25
|
||||||
@ -30,10 +31,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
|||||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||||
%asm {{
|
%asm {{
|
||||||
sec
|
sec
|
||||||
jsr c64.PLOT
|
jsr cbm.PLOT
|
||||||
tay
|
tay
|
||||||
clc
|
clc
|
||||||
jmp c64.PLOT
|
jmp cbm.PLOT
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,10 +58,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
|||||||
; (assumes screen matrix is at the default address)
|
; (assumes screen matrix is at the default address)
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #250
|
ldy #250
|
||||||
- sta c64.Screen+250*0-1,y
|
- sta cbm.Screen+250*0-1,y
|
||||||
sta c64.Screen+250*1-1,y
|
sta cbm.Screen+250*1-1,y
|
||||||
sta c64.Screen+250*2-1,y
|
sta cbm.Screen+250*2-1,y
|
||||||
sta c64.Screen+250*3-1,y
|
sta cbm.Screen+250*3-1,y
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
rts
|
rts
|
||||||
@ -72,10 +73,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
|||||||
; (assumes color matrix is at the default address)
|
; (assumes color matrix is at the default address)
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #250
|
ldy #250
|
||||||
- sta c64.Colors+250*0-1,y
|
- sta cbm.Colors+250*0-1,y
|
||||||
sta c64.Colors+250*1-1,y
|
sta cbm.Colors+250*1-1,y
|
||||||
sta c64.Colors+250*2-1,y
|
sta cbm.Colors+250*2-1,y
|
||||||
sta c64.Colors+250*3-1,y
|
sta cbm.Colors+250*3-1,y
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
rts
|
rts
|
||||||
@ -83,7 +84,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub color (ubyte txtcol) {
|
sub color (ubyte txtcol) {
|
||||||
c64.COLOR = txtcol
|
cbm.COLOR = txtcol
|
||||||
}
|
}
|
||||||
|
|
||||||
sub lowercase() {
|
sub lowercase() {
|
||||||
@ -96,7 +97,7 @@ sub uppercase() {
|
|||||||
c128.VM1 &= ~2
|
c128.VM1 &= ~2
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||||
; ---- scroll the whole screen 1 character to the left
|
; ---- scroll the whole screen 1 character to the left
|
||||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -110,10 +111,10 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
|||||||
ldy #38
|
ldy #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 1,x
|
lda cbm.Screen + 40*row + 1,x
|
||||||
sta c64.Screen + 40*row + 0,x
|
sta cbm.Screen + 40*row + 0,x
|
||||||
lda c64.Colors + 40*row + 1,x
|
lda cbm.Colors + 40*row + 1,x
|
||||||
sta c64.Colors + 40*row + 0,x
|
sta cbm.Colors + 40*row + 0,x
|
||||||
.next
|
.next
|
||||||
inx
|
inx
|
||||||
dey
|
dey
|
||||||
@ -125,8 +126,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldy #38
|
ldy #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 1,x
|
lda cbm.Screen + 40*row + 1,x
|
||||||
sta c64.Screen + 40*row + 0,x
|
sta cbm.Screen + 40*row + 0,x
|
||||||
.next
|
.next
|
||||||
inx
|
inx
|
||||||
dey
|
dey
|
||||||
@ -137,7 +138,7 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character to the right
|
; ---- scroll the whole screen 1 character to the right
|
||||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -149,10 +150,10 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
ldx #38
|
ldx #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 0,x
|
lda cbm.Screen + 40*row + 0,x
|
||||||
sta c64.Screen + 40*row + 1,x
|
sta cbm.Screen + 40*row + 1,x
|
||||||
lda c64.Colors + 40*row + 0,x
|
lda cbm.Colors + 40*row + 0,x
|
||||||
sta c64.Colors + 40*row + 1,x
|
sta cbm.Colors + 40*row + 1,x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -162,8 +163,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldx #38
|
ldx #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 0,x
|
lda cbm.Screen + 40*row + 0,x
|
||||||
sta c64.Screen + 40*row + 1,x
|
sta cbm.Screen + 40*row + 1,x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -173,7 +174,7 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character up
|
; ---- scroll the whole screen 1 character up
|
||||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -185,10 +186,10 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=1, row<=24, row+=1
|
.for row=1, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row-1),x
|
sta cbm.Screen + 40*(row-1),x
|
||||||
lda c64.Colors + 40*row,x
|
lda cbm.Colors + 40*row,x
|
||||||
sta c64.Colors + 40*(row-1),x
|
sta cbm.Colors + 40*(row-1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -198,8 +199,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=1, row<=24, row+=1
|
.for row=1, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row-1),x
|
sta cbm.Screen + 40*(row-1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -209,7 +210,7 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character down
|
; ---- scroll the whole screen 1 character down
|
||||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -221,10 +222,10 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=23, row>=0, row-=1
|
.for row=23, row>=0, row-=1
|
||||||
lda c64.Colors + 40*row,x
|
lda cbm.Colors + 40*row,x
|
||||||
sta c64.Colors + 40*(row+1),x
|
sta cbm.Colors + 40*(row+1),x
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row+1),x
|
sta cbm.Screen + 40*(row+1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -234,8 +235,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=23, row>=0, row-=1
|
.for row=23, row>=0, row-=1
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row+1),x
|
sta cbm.Screen + 40*(row+1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -245,20 +246,20 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||||
|
|
||||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||||
; ---- print null terminated string from A/Y
|
; ---- print null terminated string from A/Y
|
||||||
; note: the compiler contains an optimization that will replace
|
; note: the compiler contains an optimization that will replace
|
||||||
; a call to this subroutine with a string argument of just one char,
|
; a call to this subroutine with a string argument of just one char,
|
||||||
; by just one call to c64.CHROUT of that single char.
|
; by just one call to cbm.CHROUT of that single char.
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
sty P8ZP_SCRATCH_REG
|
sty P8ZP_SCRATCH_REG
|
||||||
ldy #0
|
ldy #0
|
||||||
- lda (P8ZP_SCRATCH_B1),y
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
beq +
|
beq +
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
+ rts
|
+ rts
|
||||||
@ -272,11 +273,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
|||||||
jsr conv.ubyte2decimal
|
jsr conv.ubyte2decimal
|
||||||
pha
|
pha
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
txa
|
txa
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -292,16 +293,16 @@ _print_byte_digits
|
|||||||
cpy #'0'
|
cpy #'0'
|
||||||
beq +
|
beq +
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
jmp _ones
|
jmp _ones
|
||||||
+ pla
|
+ pla
|
||||||
cmp #'0'
|
cmp #'0'
|
||||||
beq _ones
|
beq _ones
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
_ones txa
|
_ones txa
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -315,45 +316,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
|||||||
cmp #0
|
cmp #0
|
||||||
bpl +
|
bpl +
|
||||||
lda #'-'
|
lda #'-'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
+ pla
|
+ pla
|
||||||
jsr conv.byte2decimal
|
jsr conv.byte2decimal
|
||||||
jmp print_ub._print_byte_digits
|
jmp print_ub._print_byte_digits
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
bcc +
|
bcc +
|
||||||
pha
|
pha
|
||||||
lda #'$'
|
lda #'$'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
+ jsr conv.ubyte2hex
|
+ jsr conv.ubyte2hex
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
bcc +
|
bcc +
|
||||||
lda #'%'
|
lda #'%'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
+ ldy #8
|
+ ldy #8
|
||||||
- lda #'0'
|
- lda #'0'
|
||||||
asl P8ZP_SCRATCH_B1
|
asl P8ZP_SCRATCH_B1
|
||||||
bcc +
|
bcc +
|
||||||
lda #'1'
|
lda #'1'
|
||||||
+ jsr c64.CHROUT
|
+ jsr cbm.CHROUT
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
@ -361,7 +362,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
@ -373,7 +374,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -394,7 +395,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
|||||||
ldy #0
|
ldy #0
|
||||||
- lda conv.uword2decimal.decTenThousands,y
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
beq +
|
beq +
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
+ ldx P8ZP_SCRATCH_REG
|
+ ldx P8ZP_SCRATCH_REG
|
||||||
@ -417,14 +418,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
|||||||
bne -
|
bne -
|
||||||
|
|
||||||
_gotdigit
|
_gotdigit
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
lda conv.uword2decimal.decTenThousands,y
|
lda conv.uword2decimal.decTenThousands,y
|
||||||
bne _gotdigit
|
bne _gotdigit
|
||||||
rts
|
rts
|
||||||
_allzero
|
_allzero
|
||||||
lda #'0'
|
lda #'0'
|
||||||
jmp c64.CHROUT
|
jmp cbm.CHROUT
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +436,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
|||||||
bpl +
|
bpl +
|
||||||
pha
|
pha
|
||||||
lda #'-'
|
lda #'-'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
tya
|
tya
|
||||||
eor #255
|
eor #255
|
||||||
tay
|
tay
|
||||||
@ -457,7 +458,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #0 ; char counter = 0
|
ldy #0 ; char counter = 0
|
||||||
- jsr c64.CHRIN
|
- jsr cbm.CHRIN
|
||||||
cmp #$0d ; return (ascii 13) pressed?
|
cmp #$0d ; return (ascii 13) pressed?
|
||||||
beq + ; yes, end.
|
beq + ; yes, end.
|
||||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||||
@ -588,7 +589,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
tax
|
tax
|
||||||
clc
|
clc
|
||||||
jsr c64.PLOT
|
jsr cbm.PLOT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -597,7 +598,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|||||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||||
; -- returns the text screen width (number of columns)
|
; -- returns the text screen width (number of columns)
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
txa
|
txa
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -606,7 +607,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
|
|||||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||||
; -- returns the text screen height (number of rows)
|
; -- returns the text screen height (number of rows)
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
tya
|
tya
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
|
@ -6,7 +6,7 @@ FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
|||||||
|
|
||||||
|
|
||||||
floats_store_reg .byte 0 ; temp storage
|
floats_store_reg .byte 0 ; temp storage
|
||||||
|
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
|
||||||
|
|
||||||
ub2float .proc
|
ub2float .proc
|
||||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
floats {
|
floats {
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- this block contains C-64 floating point related functions ----
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
@ -60,8 +61,8 @@ romsub $b853 = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1
|
|||||||
romsub $b850 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
romsub $b850 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||||
romsub $ba2b = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
romsub $ba2b = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||||
romsub $ba28 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
romsub $ba28 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||||
romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
|
||||||
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
|
||||||
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||||
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
||||||
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
|
@ -144,3 +144,19 @@ func_all_f_stack .proc
|
|||||||
jsr a_times_5
|
jsr a_times_5
|
||||||
jmp prog8_lib.func_all_b_stack
|
jmp prog8_lib.func_all_b_stack
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
func_abs_f_into_FAC1 .proc
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr MOVFM
|
||||||
|
jsr ABS
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
func_sqrt_into_FAC1 .proc
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr MOVFM
|
||||||
|
jsr SQR
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
; assumes bitmap screen memory is $2000-$3fff
|
; assumes bitmap screen memory is $2000-$3fff
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const uword BITMAP_ADDRESS = $2000
|
const uword BITMAP_ADDRESS = $2000
|
||||||
const uword WIDTH = 320
|
const uword WIDTH = 320
|
||||||
const ubyte HEIGHT = 200
|
const ubyte HEIGHT = 200
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
; Prog8 definitions for the Commodore-64
|
; Prog8 definitions for the Commodore-64
|
||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
|
|
||||||
c64 {
|
|
||||||
|
cbm {
|
||||||
|
|
||||||
|
; Commodore (CBM) common variables, vectors and kernal routines
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
&ubyte STATUS = $90 ; kernal status variable for I/O
|
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||||
|
&ubyte SHFLAG = $028d ; various modifier key status (updated by IRQ)
|
||||||
|
|
||||||
&ubyte COLOR = $0286 ; cursor color
|
&ubyte COLOR = $0286 ; cursor color
|
||||||
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
||||||
@ -49,6 +56,93 @@ c64 {
|
|||||||
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||||
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
|
||||||
|
|
||||||
|
|
||||||
|
; ---- CBM ROM kernal routines (C64 addresses) ----
|
||||||
|
|
||||||
|
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||||
|
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||||
|
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||||
|
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||||
|
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||||
|
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||||
|
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||||
|
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||||
|
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||||
|
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||||
|
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||||
|
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||||
|
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||||
|
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||||
|
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||||
|
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
|
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||||
|
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||||
|
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||||
|
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||||
|
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||||
|
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||||
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
|
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||||
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
|
||||||
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
|
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||||
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||||
|
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||||
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||||
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
|
asmsub STOP2() -> ubyte @A {
|
||||||
|
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||||
|
%asm {{
|
||||||
|
txa
|
||||||
|
pha
|
||||||
|
jsr cbm.STOP
|
||||||
|
beq +
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
lda #0
|
||||||
|
rts
|
||||||
|
+ pla
|
||||||
|
tax
|
||||||
|
lda #1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub RDTIM16() -> uword @AY {
|
||||||
|
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr cbm.RDTIM
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
c64 {
|
||||||
|
; C64 I/O registers (VIC, SID, CIA)
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
; the default locations of the 8 sprite pointers (store address of sprite / 64)
|
||||||
&ubyte SPRPTR0 = 2040
|
&ubyte SPRPTR0 = 2040
|
||||||
&ubyte SPRPTR1 = 2041
|
&ubyte SPRPTR1 = 2041
|
||||||
@ -198,93 +292,15 @@ c64 {
|
|||||||
|
|
||||||
; ---- end of SID registers ----
|
; ---- end of SID registers ----
|
||||||
|
|
||||||
|
|
||||||
; ---- C64 ROM kernal routines ----
|
|
||||||
|
|
||||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
|
||||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
|
||||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
|
||||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
|
||||||
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
|
||||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
|
||||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
|
||||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
|
||||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
|
||||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
|
||||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
|
||||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
|
||||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
|
||||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
|
||||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
|
||||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
|
||||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
|
||||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
|
||||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
|
||||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
|
||||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
|
||||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
|
||||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
|
||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
|
||||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
|
||||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
|
||||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
|
||||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
|
||||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
|
||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
|
||||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
|
||||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
|
||||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
|
||||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
|
||||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
|
||||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
|
||||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
|
||||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
|
||||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
|
||||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
|
||||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
|
||||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
|
||||||
|
|
||||||
; ---- end of C64 ROM kernal routines ----
|
|
||||||
|
|
||||||
; ---- utilities -----
|
|
||||||
|
|
||||||
asmsub STOP2() -> ubyte @A {
|
|
||||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
|
||||||
%asm {{
|
|
||||||
txa
|
|
||||||
pha
|
|
||||||
jsr c64.STOP
|
|
||||||
beq +
|
|
||||||
pla
|
|
||||||
tax
|
|
||||||
lda #0
|
|
||||||
rts
|
|
||||||
+ pla
|
|
||||||
tax
|
|
||||||
lda #1
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub RDTIM16() -> uword @AY {
|
sys {
|
||||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
; ------- lowlevel system routines --------
|
||||||
%asm {{
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr c64.RDTIM
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
tay
|
|
||||||
pla
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
|
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||||
|
|
||||||
; ---- C64 specific system utility routines: ----
|
|
||||||
|
|
||||||
asmsub init_system() {
|
asmsub init_system() {
|
||||||
; Initializes the machine to a sane starting state.
|
; Initializes the machine to a sane starting state.
|
||||||
@ -300,13 +316,13 @@ asmsub init_system() {
|
|||||||
sta $00
|
sta $00
|
||||||
lda #%00100111
|
lda #%00100111
|
||||||
sta $01
|
sta $01
|
||||||
jsr c64.IOINIT
|
jsr cbm.IOINIT
|
||||||
jsr c64.RESTOR
|
jsr cbm.RESTOR
|
||||||
jsr c64.CINT
|
jsr cbm.CINT
|
||||||
lda #6
|
lda #6
|
||||||
sta c64.EXTCOL
|
sta c64.EXTCOL
|
||||||
lda #7
|
lda #7
|
||||||
sta c64.COLOR
|
sta cbm.COLOR
|
||||||
lda #0
|
lda #0
|
||||||
sta c64.BGCOL0
|
sta c64.BGCOL0
|
||||||
jsr disable_runstop_and_charsetswitch
|
jsr disable_runstop_and_charsetswitch
|
||||||
@ -326,7 +342,7 @@ asmsub init_system_phase2() {
|
|||||||
asmsub cleanup_at_exit() {
|
asmsub cleanup_at_exit() {
|
||||||
; executed when the main subroutine does rts
|
; executed when the main subroutine does rts
|
||||||
%asm {{
|
%asm {{
|
||||||
jmp c64.enable_runstop_and_charsetswitch
|
jmp sys.enable_runstop_and_charsetswitch
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,18 +366,18 @@ asmsub enable_runstop_and_charsetswitch() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta _use_kernal
|
sta _use_kernal
|
||||||
sei
|
sei
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
sta c64.CINV
|
sta cbm.CINV
|
||||||
lda #>_irq_handler
|
lda #>_irq_handler
|
||||||
sta c64.CINV+1
|
sta cbm.CINV+1
|
||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
_irq_handler jsr _irq_handler_init
|
_irq_handler jsr _irq_handler_init
|
||||||
@ -379,13 +395,12 @@ _modified jsr $ffff ; modified
|
|||||||
tax
|
tax
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
|
+ jmp cbm.IRQDFRT ; continue with normal kernal irq routine
|
||||||
|
|
||||||
_use_kernal .byte 0
|
_use_kernal .byte 0
|
||||||
|
|
||||||
_irq_handler_init
|
_irq_handler_init
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
; save all zp scratch registers as these might be clobbered by the irq routine
|
||||||
stx IRQ_X_REG
|
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta IRQ_SCRATCH_ZPB1
|
sta IRQ_SCRATCH_ZPB1
|
||||||
lda P8ZP_SCRATCH_REG
|
lda P8ZP_SCRATCH_REG
|
||||||
@ -398,18 +413,15 @@ _irq_handler_init
|
|||||||
sta IRQ_SCRATCH_ZPWORD2
|
sta IRQ_SCRATCH_ZPWORD2
|
||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta IRQ_SCRATCH_ZPWORD2+1
|
sta IRQ_SCRATCH_ZPWORD2+1
|
||||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||||
dex
|
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||||
dex
|
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||||
dex
|
ldx #32
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
cld
|
cld
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_irq_handler_end
|
_irq_handler_end
|
||||||
; restore all zp scratch registers and the X register
|
; restore all zp scratch registers
|
||||||
lda IRQ_SCRATCH_ZPB1
|
lda IRQ_SCRATCH_ZPB1
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda IRQ_SCRATCH_ZPREG
|
lda IRQ_SCRATCH_ZPREG
|
||||||
@ -422,10 +434,8 @@ _irq_handler_end
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda IRQ_SCRATCH_ZPWORD2+1
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
ldx IRQ_X_REG
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
IRQ_X_REG .byte 0
|
|
||||||
IRQ_SCRATCH_ZPB1 .byte 0
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
IRQ_SCRATCH_ZPREG .byte 0
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
@ -437,10 +447,10 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
|||||||
asmsub restore_irq() clobbers(A) {
|
asmsub restore_irq() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda #<c64.IRQDFRT
|
lda #<cbm.IRQDFRT
|
||||||
sta c64.CINV
|
sta cbm.CINV
|
||||||
lda #>c64.IRQDFRT
|
lda #>cbm.IRQDFRT
|
||||||
sta c64.CINV+1
|
sta cbm.CINV+1
|
||||||
lda #0
|
lda #0
|
||||||
sta c64.IREQMASK ; disable raster irq
|
sta c64.IREQMASK ; disable raster irq
|
||||||
lda #%10000001
|
lda #%10000001
|
||||||
@ -450,21 +460,21 @@ asmsub restore_irq() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta set_irq._use_kernal
|
sta set_irq._use_kernal
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
ldy cx16.r0+1
|
ldy cx16.r0+1
|
||||||
sei
|
sei
|
||||||
jsr _setup_raster_irq
|
jsr _setup_raster_irq
|
||||||
lda #<_raster_irq_handler
|
lda #<_raster_irq_handler
|
||||||
sta c64.CINV
|
sta cbm.CINV
|
||||||
lda #>_raster_irq_handler
|
lda #>_raster_irq_handler
|
||||||
sta c64.CINV+1
|
sta cbm.CINV+1
|
||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
|
|
||||||
@ -472,8 +482,8 @@ _raster_irq_handler
|
|||||||
jsr set_irq._irq_handler_init
|
jsr set_irq._irq_handler_init
|
||||||
_modified jsr $ffff ; modified
|
_modified jsr $ffff ; modified
|
||||||
jsr set_irq._irq_handler_end
|
jsr set_irq._irq_handler_end
|
||||||
lda #$ff
|
lda #$ff
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
lda set_irq._use_kernal
|
lda set_irq._use_kernal
|
||||||
bne +
|
bne +
|
||||||
; end irq processing - don't use kernal's irq handling
|
; end irq processing - don't use kernal's irq handling
|
||||||
@ -483,7 +493,7 @@ _modified jsr $ffff ; modified
|
|||||||
tax
|
tax
|
||||||
pla
|
pla
|
||||||
rti
|
rti
|
||||||
+ jmp c64.IRQDFRT ; continue with kernal irq routine
|
+ jmp cbm.IRQDFRT ; continue with kernal irq routine
|
||||||
|
|
||||||
_setup_raster_irq
|
_setup_raster_irq
|
||||||
pha
|
pha
|
||||||
@ -507,23 +517,14 @@ _setup_raster_irq
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
; ---- end of C64 specific system utility routines ----
|
|
||||||
|
|
||||||
}
|
asmsub reset_system() {
|
||||||
|
|
||||||
sys {
|
|
||||||
; ------- lowlevel system routines --------
|
|
||||||
|
|
||||||
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
|
||||||
|
|
||||||
|
|
||||||
asmsub reset_system() {
|
|
||||||
; Soft-reset the system back to initial power-on Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda #14
|
lda #14
|
||||||
sta $01 ; bank the kernal in
|
sta $01 ; bank the kernal in
|
||||||
jmp (c64.RESET_VEC)
|
jmp (cbm.RESET_VEC)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,9 +542,9 @@ _loop lda P8ZP_SCRATCH_W1
|
|||||||
ldx P8ZP_SCRATCH_B1
|
ldx P8ZP_SCRATCH_B1
|
||||||
rts
|
rts
|
||||||
|
|
||||||
+ lda c64.TIME_LO
|
+ lda cbm.TIME_LO
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda c64.TIME_LO
|
- lda cbm.TIME_LO
|
||||||
cmp P8ZP_SCRATCH_B1
|
cmp P8ZP_SCRATCH_B1
|
||||||
beq -
|
beq -
|
||||||
|
|
||||||
@ -700,13 +701,26 @@ _longcopy
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_set_irqd() {
|
||||||
|
%asm {{
|
||||||
|
php
|
||||||
|
sei
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_clear_irqd() {
|
||||||
|
%asm {{
|
||||||
|
plp
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
inline asmsub exit(ubyte returnvalue @A) {
|
inline asmsub exit(ubyte returnvalue @A) {
|
||||||
; -- immediately exit the program with a return code in the A register
|
; -- immediately exit the program with a return code in the A register
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #31
|
lda #31
|
||||||
sta $01 ; bank the kernal in
|
sta $01 ; bank the kernal in
|
||||||
jsr c64.CLRCHN ; reset i/o channels
|
jsr cbm.CLRCHN ; reset i/o channels
|
||||||
jsr c64.enable_runstop_and_charsetswitch
|
jsr sys.enable_runstop_and_charsetswitch
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
txs
|
txs
|
||||||
rts ; return to original caller
|
rts ; return to original caller
|
||||||
@ -724,6 +738,8 @@ _longcopy
|
|||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||||
; they are simulated on the C64 as well but their location in memory is different
|
; they are simulated on the C64 as well but their location in memory is different
|
||||||
; (because there's no room for them in the zeropage)
|
; (because there's no room for them in the zeropage)
|
||||||
@ -831,4 +847,31 @@ cx16 {
|
|||||||
&byte r13sH = $cf1b
|
&byte r13sH = $cf1b
|
||||||
&byte r14sH = $cf1d
|
&byte r14sH = $cf1d
|
||||||
&byte r15sH = $cf1f
|
&byte r15sH = $cf1f
|
||||||
|
|
||||||
|
asmsub save_virtual_registers() clobbers(A,Y) {
|
||||||
|
%asm {{
|
||||||
|
ldy #31
|
||||||
|
- lda cx16.r0,y
|
||||||
|
sta _cx16_vreg_storage,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
|
||||||
|
_cx16_vreg_storage
|
||||||
|
.word 0,0,0,0,0,0,0,0
|
||||||
|
.word 0,0,0,0,0,0,0,0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub restore_virtual_registers() clobbers(A,Y) {
|
||||||
|
%asm {{
|
||||||
|
ldy #31
|
||||||
|
- lda save_virtual_registers._cx16_vreg_storage,y
|
||||||
|
sta cx16.r0,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
txt {
|
txt {
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const ubyte DEFAULT_WIDTH = 40
|
const ubyte DEFAULT_WIDTH = 40
|
||||||
const ubyte DEFAULT_HEIGHT = 25
|
const ubyte DEFAULT_HEIGHT = 25
|
||||||
|
|
||||||
@ -30,10 +32,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
|||||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||||
%asm {{
|
%asm {{
|
||||||
sec
|
sec
|
||||||
jsr c64.PLOT
|
jsr cbm.PLOT
|
||||||
tay
|
tay
|
||||||
clc
|
clc
|
||||||
jmp c64.PLOT
|
jmp cbm.PLOT
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,10 +59,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
|||||||
; (assumes screen matrix is at the default address)
|
; (assumes screen matrix is at the default address)
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #250
|
ldy #250
|
||||||
- sta c64.Screen+250*0-1,y
|
- sta cbm.Screen+250*0-1,y
|
||||||
sta c64.Screen+250*1-1,y
|
sta cbm.Screen+250*1-1,y
|
||||||
sta c64.Screen+250*2-1,y
|
sta cbm.Screen+250*2-1,y
|
||||||
sta c64.Screen+250*3-1,y
|
sta cbm.Screen+250*3-1,y
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
rts
|
rts
|
||||||
@ -72,10 +74,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
|||||||
; (assumes color matrix is at the default address)
|
; (assumes color matrix is at the default address)
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #250
|
ldy #250
|
||||||
- sta c64.Colors+250*0-1,y
|
- sta cbm.Colors+250*0-1,y
|
||||||
sta c64.Colors+250*1-1,y
|
sta cbm.Colors+250*1-1,y
|
||||||
sta c64.Colors+250*2-1,y
|
sta cbm.Colors+250*2-1,y
|
||||||
sta c64.Colors+250*3-1,y
|
sta cbm.Colors+250*3-1,y
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
rts
|
rts
|
||||||
@ -83,7 +85,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub color (ubyte txtcol) {
|
sub color (ubyte txtcol) {
|
||||||
c64.COLOR = txtcol
|
cbm.COLOR = txtcol
|
||||||
}
|
}
|
||||||
|
|
||||||
sub lowercase() {
|
sub lowercase() {
|
||||||
@ -94,7 +96,7 @@ sub uppercase() {
|
|||||||
c64.VMCSB &= ~2
|
c64.VMCSB &= ~2
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
|
||||||
; ---- scroll the whole screen 1 character to the left
|
; ---- scroll the whole screen 1 character to the left
|
||||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -108,10 +110,10 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
|||||||
ldy #38
|
ldy #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 1,x
|
lda cbm.Screen + 40*row + 1,x
|
||||||
sta c64.Screen + 40*row + 0,x
|
sta cbm.Screen + 40*row + 0,x
|
||||||
lda c64.Colors + 40*row + 1,x
|
lda cbm.Colors + 40*row + 1,x
|
||||||
sta c64.Colors + 40*row + 0,x
|
sta cbm.Colors + 40*row + 0,x
|
||||||
.next
|
.next
|
||||||
inx
|
inx
|
||||||
dey
|
dey
|
||||||
@ -123,8 +125,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldy #38
|
ldy #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 1,x
|
lda cbm.Screen + 40*row + 1,x
|
||||||
sta c64.Screen + 40*row + 0,x
|
sta cbm.Screen + 40*row + 0,x
|
||||||
.next
|
.next
|
||||||
inx
|
inx
|
||||||
dey
|
dey
|
||||||
@ -135,7 +137,7 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character to the right
|
; ---- scroll the whole screen 1 character to the right
|
||||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -147,10 +149,10 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
ldx #38
|
ldx #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 0,x
|
lda cbm.Screen + 40*row + 0,x
|
||||||
sta c64.Screen + 40*row + 1,x
|
sta cbm.Screen + 40*row + 1,x
|
||||||
lda c64.Colors + 40*row + 0,x
|
lda cbm.Colors + 40*row + 0,x
|
||||||
sta c64.Colors + 40*row + 1,x
|
sta cbm.Colors + 40*row + 1,x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -160,8 +162,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldx #38
|
ldx #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=24, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 0,x
|
lda cbm.Screen + 40*row + 0,x
|
||||||
sta c64.Screen + 40*row + 1,x
|
sta cbm.Screen + 40*row + 1,x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -171,7 +173,7 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character up
|
; ---- scroll the whole screen 1 character up
|
||||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -183,10 +185,10 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=1, row<=24, row+=1
|
.for row=1, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row-1),x
|
sta cbm.Screen + 40*(row-1),x
|
||||||
lda c64.Colors + 40*row,x
|
lda cbm.Colors + 40*row,x
|
||||||
sta c64.Colors + 40*(row-1),x
|
sta cbm.Colors + 40*(row-1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -196,8 +198,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=1, row<=24, row+=1
|
.for row=1, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row-1),x
|
sta cbm.Screen + 40*(row-1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -207,7 +209,7 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
|
||||||
; ---- scroll the whole screen 1 character down
|
; ---- scroll the whole screen 1 character down
|
||||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
@ -219,10 +221,10 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=23, row>=0, row-=1
|
.for row=23, row>=0, row-=1
|
||||||
lda c64.Colors + 40*row,x
|
lda cbm.Colors + 40*row,x
|
||||||
sta c64.Colors + 40*(row+1),x
|
sta cbm.Colors + 40*(row+1),x
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row+1),x
|
sta cbm.Screen + 40*(row+1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -232,8 +234,8 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=23, row>=0, row-=1
|
.for row=23, row>=0, row-=1
|
||||||
lda c64.Screen + 40*row,x
|
lda cbm.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row+1),x
|
sta cbm.Screen + 40*(row+1),x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
@ -243,20 +245,20 @@ _scroll_screen ; scroll only the screen memory
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||||
|
|
||||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||||
; ---- print null terminated string from A/Y
|
; ---- print null terminated string from A/Y
|
||||||
; note: the compiler contains an optimization that will replace
|
; note: the compiler contains an optimization that will replace
|
||||||
; a call to this subroutine with a string argument of just one char,
|
; a call to this subroutine with a string argument of just one char,
|
||||||
; by just one call to c64.CHROUT of that single char.
|
; by just one call to cbm.CHROUT of that single char.
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
sty P8ZP_SCRATCH_REG
|
sty P8ZP_SCRATCH_REG
|
||||||
ldy #0
|
ldy #0
|
||||||
- lda (P8ZP_SCRATCH_B1),y
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
beq +
|
beq +
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
+ rts
|
+ rts
|
||||||
@ -270,11 +272,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
|||||||
jsr conv.ubyte2decimal
|
jsr conv.ubyte2decimal
|
||||||
pha
|
pha
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
txa
|
txa
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -290,16 +292,16 @@ _print_byte_digits
|
|||||||
cpy #'0'
|
cpy #'0'
|
||||||
beq +
|
beq +
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
jmp _ones
|
jmp _ones
|
||||||
+ pla
|
+ pla
|
||||||
cmp #'0'
|
cmp #'0'
|
||||||
beq _ones
|
beq _ones
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
_ones txa
|
_ones txa
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -313,45 +315,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
|||||||
cmp #0
|
cmp #0
|
||||||
bpl +
|
bpl +
|
||||||
lda #'-'
|
lda #'-'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
+ pla
|
+ pla
|
||||||
jsr conv.byte2decimal
|
jsr conv.byte2decimal
|
||||||
jmp print_ub._print_byte_digits
|
jmp print_ub._print_byte_digits
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
bcc +
|
bcc +
|
||||||
pha
|
pha
|
||||||
lda #'$'
|
lda #'$'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
+ jsr conv.ubyte2hex
|
+ jsr conv.ubyte2hex
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
bcc +
|
bcc +
|
||||||
lda #'%'
|
lda #'%'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
+ ldy #8
|
+ ldy #8
|
||||||
- lda #'0'
|
- lda #'0'
|
||||||
asl P8ZP_SCRATCH_B1
|
asl P8ZP_SCRATCH_B1
|
||||||
bcc +
|
bcc +
|
||||||
lda #'1'
|
lda #'1'
|
||||||
+ jsr c64.CHROUT
|
+ jsr cbm.CHROUT
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
@ -359,7 +361,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
@ -371,7 +373,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -392,7 +394,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
|||||||
ldy #0
|
ldy #0
|
||||||
- lda conv.uword2decimal.decTenThousands,y
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
beq +
|
beq +
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
+ ldx P8ZP_SCRATCH_REG
|
+ ldx P8ZP_SCRATCH_REG
|
||||||
@ -415,14 +417,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
|||||||
bne -
|
bne -
|
||||||
|
|
||||||
_gotdigit
|
_gotdigit
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
lda conv.uword2decimal.decTenThousands,y
|
lda conv.uword2decimal.decTenThousands,y
|
||||||
bne _gotdigit
|
bne _gotdigit
|
||||||
rts
|
rts
|
||||||
_allzero
|
_allzero
|
||||||
lda #'0'
|
lda #'0'
|
||||||
jmp c64.CHROUT
|
jmp cbm.CHROUT
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,7 +435,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
|||||||
bpl +
|
bpl +
|
||||||
pha
|
pha
|
||||||
lda #'-'
|
lda #'-'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
tya
|
tya
|
||||||
eor #255
|
eor #255
|
||||||
tay
|
tay
|
||||||
@ -455,7 +457,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #0 ; char counter = 0
|
ldy #0 ; char counter = 0
|
||||||
- jsr c64.CHRIN
|
- jsr cbm.CHRIN
|
||||||
cmp #$0d ; return (ascii 13) pressed?
|
cmp #$0d ; return (ascii 13) pressed?
|
||||||
beq + ; yes, end.
|
beq + ; yes, end.
|
||||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||||
@ -586,7 +588,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
tax
|
tax
|
||||||
clc
|
clc
|
||||||
jsr c64.PLOT
|
jsr cbm.PLOT
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -595,7 +597,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|||||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||||
; -- returns the text screen width (number of columns)
|
; -- returns the text screen width (number of columns)
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
txa
|
txa
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -604,7 +606,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
|
|||||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||||
; -- returns the text screen height (number of rows)
|
; -- returns the text screen height (number of rows)
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
tya
|
tya
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
conv {
|
conv {
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; ----- number conversions to decimal strings ----
|
; ----- number conversions to decimal strings ----
|
||||||
|
|
||||||
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
|
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
|
||||||
|
@ -1,234 +0,0 @@
|
|||||||
; Cx16 specific disk drive I/O routines.
|
|
||||||
|
|
||||||
%import diskio
|
|
||||||
%import string
|
|
||||||
|
|
||||||
cx16diskio {
|
|
||||||
|
|
||||||
; Same as diskio.load() but with additional bank parameter to select the Ram bank to load into.
|
|
||||||
; Use kernal LOAD routine to load the given program file in memory.
|
|
||||||
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
|
|
||||||
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
|
|
||||||
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
|
||||||
; and the rest is loaded at the given location in memory.
|
|
||||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
|
||||||
; You can use the load_size() function to calcuate the size of the file that was loaded.
|
|
||||||
sub load(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
|
|
||||||
cx16.rambank(bank)
|
|
||||||
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
; Same as diskio.load_raw() but with additional bank parameter to select the Ram bank to load into.
|
|
||||||
; Use kernal LOAD routine to load the given file in memory.
|
|
||||||
; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
|
|
||||||
; The load address is mandatory. Returns the number of bytes loaded.
|
|
||||||
; If you load into regular system ram, use cx16.getrambank() for the bank argument,
|
|
||||||
; or alternatively make sure to reset the correct ram bank yourself after the load!
|
|
||||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
|
||||||
; You can use the load_size() function to calcuate the size of the file that was loaded.
|
|
||||||
sub load_raw(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
|
|
||||||
cx16.rambank(bank)
|
|
||||||
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
|
|
||||||
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
|
|
||||||
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
|
|
||||||
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub vload(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
|
|
||||||
; -- like the basic command VLOAD "filename",drivenumber,bank,address
|
|
||||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
|
||||||
; the file has to have the usual 2 byte header (which will be skipped)
|
|
||||||
%asm {{
|
|
||||||
clc
|
|
||||||
internal_vload:
|
|
||||||
phx
|
|
||||||
pha
|
|
||||||
tya
|
|
||||||
tax
|
|
||||||
bcc +
|
|
||||||
ldy #%00000010 ; headerless load mode
|
|
||||||
bne ++
|
|
||||||
+ ldy #0 ; normal load mode
|
|
||||||
+ lda #1
|
|
||||||
jsr c64.SETLFS
|
|
||||||
lda cx16.r0
|
|
||||||
ldy cx16.r0+1
|
|
||||||
jsr prog8_lib.strlen
|
|
||||||
tya
|
|
||||||
ldx cx16.r0
|
|
||||||
ldy cx16.r0+1
|
|
||||||
jsr c64.SETNAM
|
|
||||||
pla
|
|
||||||
clc
|
|
||||||
adc #2
|
|
||||||
ldx cx16.r1
|
|
||||||
ldy cx16.r1+1
|
|
||||||
stz P8ZP_SCRATCH_B1
|
|
||||||
jsr c64.LOAD
|
|
||||||
bcs +
|
|
||||||
inc P8ZP_SCRATCH_B1
|
|
||||||
+ jsr c64.CLRCHN
|
|
||||||
lda #1
|
|
||||||
jsr c64.CLOSE
|
|
||||||
plx
|
|
||||||
lda P8ZP_SCRATCH_B1
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub vload_raw(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
|
|
||||||
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
|
|
||||||
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
|
||||||
; the file is read fully including the first two bytes.
|
|
||||||
%asm {{
|
|
||||||
sec
|
|
||||||
jmp vload.internal_vload
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
; Replacement function that makes use of fast block read capability of the X16,
|
|
||||||
; and can wrap over multiple ram banks while reading.
|
|
||||||
; Use this in place of regular diskio.f_read() on X16.
|
|
||||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
|
||||||
; -- read from the currently open file, up to the given number of bytes.
|
|
||||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
|
||||||
if not diskio.iteration_in_progress or not num_bytes
|
|
||||||
return 0
|
|
||||||
|
|
||||||
diskio.list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
|
||||||
|
|
||||||
; commander X16 supports fast block-read via macptr() kernal call
|
|
||||||
uword size
|
|
||||||
while num_bytes {
|
|
||||||
size = 255
|
|
||||||
if num_bytes<size
|
|
||||||
size = num_bytes
|
|
||||||
size = cx16.macptr(lsb(size), bufferpointer, false)
|
|
||||||
if_cs
|
|
||||||
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
|
||||||
diskio.list_blocks += size
|
|
||||||
bufferpointer += size
|
|
||||||
if msb(bufferpointer) == $c0
|
|
||||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
|
||||||
num_bytes -= size
|
|
||||||
if c64.READST() & $40 {
|
|
||||||
diskio.f_close() ; end of file, close it
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diskio.list_blocks ; number of bytes read
|
|
||||||
|
|
||||||
byte_read_loop: ; fallback if macptr() isn't supported on the device
|
|
||||||
%asm {{
|
|
||||||
lda bufferpointer
|
|
||||||
sta m_in_buffer+1
|
|
||||||
lda bufferpointer+1
|
|
||||||
sta m_in_buffer+2
|
|
||||||
}}
|
|
||||||
while num_bytes {
|
|
||||||
if c64.READST() {
|
|
||||||
diskio.f_close()
|
|
||||||
if c64.READST() & $40 ; eof?
|
|
||||||
return diskio.list_blocks ; number of bytes read
|
|
||||||
return 0 ; error.
|
|
||||||
}
|
|
||||||
%asm {{
|
|
||||||
jsr c64.CHRIN
|
|
||||||
m_in_buffer sta $ffff
|
|
||||||
inc m_in_buffer+1
|
|
||||||
bne +
|
|
||||||
inc m_in_buffer+2
|
|
||||||
+
|
|
||||||
}}
|
|
||||||
diskio.list_blocks++
|
|
||||||
num_bytes--
|
|
||||||
}
|
|
||||||
return diskio.list_blocks ; number of bytes read
|
|
||||||
}
|
|
||||||
|
|
||||||
; replacement function that makes use of fast block read capability of the X16
|
|
||||||
; use this in place of regular diskio.f_read_all() on X16
|
|
||||||
sub f_read_all(uword bufferpointer) -> uword {
|
|
||||||
; -- read the full contents of the file, returns number of bytes read.
|
|
||||||
if not diskio.iteration_in_progress
|
|
||||||
return 0
|
|
||||||
|
|
||||||
uword total_read = 0
|
|
||||||
while not c64.READST() {
|
|
||||||
cx16.r0 = cx16diskio.f_read(bufferpointer, 256)
|
|
||||||
total_read += cx16.r0
|
|
||||||
bufferpointer += cx16.r0
|
|
||||||
}
|
|
||||||
return total_read
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub chdir(ubyte drivenumber, str path) {
|
|
||||||
; -- change current directory.
|
|
||||||
diskio.list_filename[0] = 'c'
|
|
||||||
diskio.list_filename[1] = 'd'
|
|
||||||
diskio.list_filename[2] = ':'
|
|
||||||
void string.copy(path, &diskio.list_filename+3)
|
|
||||||
diskio.send_command(drivenumber, diskio.list_filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub mkdir(ubyte drivenumber, str name) {
|
|
||||||
; -- make a new subdirectory.
|
|
||||||
diskio.list_filename[0] = 'm'
|
|
||||||
diskio.list_filename[1] = 'd'
|
|
||||||
diskio.list_filename[2] = ':'
|
|
||||||
void string.copy(name, &diskio.list_filename+3)
|
|
||||||
diskio.send_command(drivenumber, diskio.list_filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub rmdir(ubyte drivenumber, str name) {
|
|
||||||
; -- remove a subdirectory.
|
|
||||||
void string.find(name, '*')
|
|
||||||
if_cs
|
|
||||||
return ; refuse to act on a wildcard *
|
|
||||||
diskio.list_filename[0] = 'r'
|
|
||||||
diskio.list_filename[1] = 'd'
|
|
||||||
diskio.list_filename[2] = ':'
|
|
||||||
void string.copy(name, &diskio.list_filename+3)
|
|
||||||
diskio.send_command(drivenumber, diskio.list_filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub relabel(ubyte drivenumber, str name) {
|
|
||||||
; -- change the disk label.
|
|
||||||
diskio.list_filename[0] = 'r'
|
|
||||||
diskio.list_filename[1] = '-'
|
|
||||||
diskio.list_filename[2] = 'h'
|
|
||||||
diskio.list_filename[3] = ':'
|
|
||||||
void string.copy(name, &diskio.list_filename+4)
|
|
||||||
diskio.send_command(drivenumber, diskio.list_filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub f_seek(uword pos_hiword, uword pos_loword) {
|
|
||||||
; -- seek in the reading file opened with f_open, to the given 32-bits position
|
|
||||||
ubyte[6] command = ['p',0,0,0,0,0]
|
|
||||||
command[1] = 12 ; f_open uses channel 12
|
|
||||||
command[2] = lsb(pos_loword)
|
|
||||||
command[3] = msb(pos_loword)
|
|
||||||
command[4] = lsb(pos_hiword)
|
|
||||||
command[5] = msb(pos_hiword)
|
|
||||||
send_command:
|
|
||||||
c64.SETNAM(sizeof(command), &command)
|
|
||||||
c64.SETLFS(15, diskio.last_drivenumber, 15)
|
|
||||||
void c64.OPEN()
|
|
||||||
c64.CLOSE(15)
|
|
||||||
}
|
|
||||||
|
|
||||||
; TODO see if we can get this to work as well:
|
|
||||||
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
|
|
||||||
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
|
|
||||||
; cx16diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
|
|
||||||
; cx16diskio.f_seek.command[2] = lsb(pos_loword)
|
|
||||||
; cx16diskio.f_seek.command[3] = msb(pos_loword)
|
|
||||||
; cx16diskio.f_seek.command[4] = lsb(pos_hiword)
|
|
||||||
; cx16diskio.f_seek.command[5] = msb(pos_hiword)
|
|
||||||
; goto cx16diskio.f_seek.send_command
|
|
||||||
; }
|
|
||||||
}
|
|
797
compiler/res/prog8lib/cx16/diskio.p8
Normal file
797
compiler/res/prog8lib/cx16/diskio.p8
Normal file
@ -0,0 +1,797 @@
|
|||||||
|
; Commander X16 disk drive I/O routines.
|
||||||
|
; Largely compatible with the default C64 ones, but adds more stuff specific to the X16 as well.
|
||||||
|
|
||||||
|
%import textio
|
||||||
|
%import string
|
||||||
|
%import syslib
|
||||||
|
|
||||||
|
diskio {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
|
ubyte drivenumber = 8
|
||||||
|
|
||||||
|
sub set_drive(ubyte number) {
|
||||||
|
drivenumber = number
|
||||||
|
}
|
||||||
|
|
||||||
|
sub directory() -> bool {
|
||||||
|
; -- Prints the directory contents to the screen. Returns success.
|
||||||
|
|
||||||
|
cbm.SETNAM(1, "$")
|
||||||
|
cbm.SETLFS(12, drivenumber, 0)
|
||||||
|
ubyte status = 1
|
||||||
|
void cbm.OPEN() ; open 12,8,0,"$"
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
repeat 4 {
|
||||||
|
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
; while not stop key pressed / EOF encountered, read data.
|
||||||
|
status = cbm.READST()
|
||||||
|
if status!=0 {
|
||||||
|
status = 1
|
||||||
|
goto io_error
|
||||||
|
}
|
||||||
|
|
||||||
|
while status==0 {
|
||||||
|
ubyte low = cbm.CHRIN()
|
||||||
|
ubyte high = cbm.CHRIN()
|
||||||
|
txt.print_uw(mkword(high, low))
|
||||||
|
txt.spc()
|
||||||
|
ubyte @zp char
|
||||||
|
repeat {
|
||||||
|
char = cbm.CHRIN()
|
||||||
|
if char==0
|
||||||
|
break
|
||||||
|
txt.chrout(char)
|
||||||
|
}
|
||||||
|
txt.nl()
|
||||||
|
void cbm.CHRIN() ; skip 2 bytes
|
||||||
|
void cbm.CHRIN()
|
||||||
|
status = cbm.READST()
|
||||||
|
if cbm.STOP2()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
status = cbm.READST()
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
cbm.CLRCHN() ; restore default i/o devices
|
||||||
|
cbm.CLOSE(12)
|
||||||
|
|
||||||
|
if status and status & $40 == 0 { ; bit 6=end of file
|
||||||
|
txt.print("\ni/o error, status: ")
|
||||||
|
txt.print_ub(status)
|
||||||
|
txt.nl()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub diskname() -> uword {
|
||||||
|
; returns disk label name or 0 if error
|
||||||
|
cbm.SETNAM(3, "$")
|
||||||
|
cbm.SETLFS(12, diskio.drivenumber, 0)
|
||||||
|
ubyte status = 1
|
||||||
|
void cbm.OPEN() ; open 12,8,0,"$=c"
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
while cbm.CHRIN()!='"' {
|
||||||
|
; skip up to entry name
|
||||||
|
}
|
||||||
|
|
||||||
|
cx16.r0 = &diskio.list_filename
|
||||||
|
repeat {
|
||||||
|
@(cx16.r0) = cbm.CHRIN()
|
||||||
|
if @(cx16.r0)=='"' {
|
||||||
|
@(cx16.r0) = ' '
|
||||||
|
while @(cx16.r0)==' ' and cx16.r0>=&diskio.list_filename {
|
||||||
|
@(cx16.r0) = 0
|
||||||
|
cx16.r0--
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cx16.r0++
|
||||||
|
}
|
||||||
|
status = cbm.READST()
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(12)
|
||||||
|
if status and status & $40 == 0
|
||||||
|
return 0
|
||||||
|
return diskio.list_filename
|
||||||
|
}
|
||||||
|
|
||||||
|
; internal variables for the iterative file lister / loader
|
||||||
|
bool list_skip_disk_name
|
||||||
|
uword list_pattern
|
||||||
|
uword list_blocks
|
||||||
|
bool iteration_in_progress = false
|
||||||
|
str list_filetype = "???" ; prg, seq, dir
|
||||||
|
str list_filename = "?" * 50
|
||||||
|
|
||||||
|
; ----- get a list of files (uses iteration functions internally) -----
|
||||||
|
|
||||||
|
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||||
|
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||||
|
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
|
||||||
|
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||||
|
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
|
||||||
|
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||||
|
uword buffer_start = filenames_buffer
|
||||||
|
ubyte files_found = 0
|
||||||
|
if lf_start_list(pattern_ptr) {
|
||||||
|
while lf_next_entry() {
|
||||||
|
if list_filetype!="dir" {
|
||||||
|
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
|
||||||
|
files_found++
|
||||||
|
if filenames_buffer - buffer_start > filenames_buf_size-20 {
|
||||||
|
@(filenames_buffer)=0
|
||||||
|
lf_end_list()
|
||||||
|
sys.set_carry()
|
||||||
|
return files_found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lf_end_list()
|
||||||
|
}
|
||||||
|
@(filenames_buffer)=0
|
||||||
|
sys.clear_carry()
|
||||||
|
return files_found
|
||||||
|
}
|
||||||
|
|
||||||
|
; ----- iterative file lister functions (uses io channel 12) -----
|
||||||
|
|
||||||
|
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||||
|
; -- start an iterative file listing with optional pattern matching.
|
||||||
|
; note: only a single iteration loop can be active at a time!
|
||||||
|
lf_end_list()
|
||||||
|
list_pattern = pattern_ptr
|
||||||
|
list_skip_disk_name = true
|
||||||
|
iteration_in_progress = true
|
||||||
|
|
||||||
|
cbm.SETNAM(1, "$")
|
||||||
|
cbm.SETLFS(12, drivenumber, 0)
|
||||||
|
void cbm.OPEN() ; open 12,8,0,"$"
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
repeat 4 {
|
||||||
|
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
if cbm.READST()==0
|
||||||
|
return true
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
lf_end_list()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub lf_next_entry() -> bool {
|
||||||
|
; -- retrieve the next entry from an iterative file listing session.
|
||||||
|
; results will be found in list_blocks, list_filename, and list_filetype.
|
||||||
|
; if it returns false though, there are no more entries (or an error occurred).
|
||||||
|
|
||||||
|
if not iteration_in_progress
|
||||||
|
return false
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
void cbm.CHKIN(12) ; use #12 as input channel again
|
||||||
|
|
||||||
|
uword nameptr = &list_filename
|
||||||
|
ubyte blocks_lsb = cbm.CHRIN()
|
||||||
|
ubyte blocks_msb = cbm.CHRIN()
|
||||||
|
|
||||||
|
if cbm.READST()
|
||||||
|
goto close_end
|
||||||
|
|
||||||
|
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||||
|
|
||||||
|
; read until the filename starts after the first "
|
||||||
|
while cbm.CHRIN()!='\"' {
|
||||||
|
if cbm.READST()
|
||||||
|
goto close_end
|
||||||
|
}
|
||||||
|
|
||||||
|
; read the filename
|
||||||
|
repeat {
|
||||||
|
ubyte char = cbm.CHRIN()
|
||||||
|
if char==0
|
||||||
|
break
|
||||||
|
if char=='\"'
|
||||||
|
break
|
||||||
|
@(nameptr) = char
|
||||||
|
nameptr++
|
||||||
|
}
|
||||||
|
|
||||||
|
@(nameptr) = 0
|
||||||
|
|
||||||
|
do {
|
||||||
|
cx16.r15L = cbm.CHRIN()
|
||||||
|
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
|
||||||
|
list_filetype[0] = cx16.r15L
|
||||||
|
list_filetype[1] = cbm.CHRIN()
|
||||||
|
list_filetype[2] = cbm.CHRIN()
|
||||||
|
while cbm.CHRIN() {
|
||||||
|
; read the rest of the entry until the end
|
||||||
|
}
|
||||||
|
|
||||||
|
void cbm.CHRIN() ; skip 2 bytes
|
||||||
|
void cbm.CHRIN()
|
||||||
|
|
||||||
|
if not list_skip_disk_name {
|
||||||
|
if not list_pattern
|
||||||
|
return true
|
||||||
|
if string.pattern_match(list_filename, list_pattern)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
list_skip_disk_name = false
|
||||||
|
}
|
||||||
|
|
||||||
|
close_end:
|
||||||
|
lf_end_list()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub lf_end_list() {
|
||||||
|
; -- end an iterative file listing session (close channels).
|
||||||
|
if iteration_in_progress {
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(12)
|
||||||
|
iteration_in_progress = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ----- iterative file loader functions (uses io channel 12) -----
|
||||||
|
|
||||||
|
sub f_open(uword filenameptr) -> bool {
|
||||||
|
; -- open a file for iterative reading with f_read
|
||||||
|
; note: only a single iteration loop can be active at a time!
|
||||||
|
f_close()
|
||||||
|
|
||||||
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
|
cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
|
||||||
|
void cbm.OPEN() ; open 12,8,12,"filename"
|
||||||
|
if_cc {
|
||||||
|
if cbm.READST()==0 {
|
||||||
|
iteration_in_progress = true
|
||||||
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
|
if_cc {
|
||||||
|
void cbm.CHRIN() ; read first byte to test for file not found
|
||||||
|
if not cbm.READST() {
|
||||||
|
cbm.CLOSE(12) ; close file because we already consumed first byte
|
||||||
|
void cbm.OPEN() ; re-open the file
|
||||||
|
void cbm.CHKIN(12)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f_close()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
; optimized for Commander X16 to use MACPTR block read kernal call
|
||||||
|
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||||
|
; -- read from the currently open file, up to the given number of bytes.
|
||||||
|
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||||
|
if not iteration_in_progress or not num_bytes
|
||||||
|
return 0
|
||||||
|
|
||||||
|
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||||
|
|
||||||
|
; commander X16 supports fast block-read via macptr() kernal call
|
||||||
|
uword size
|
||||||
|
while num_bytes {
|
||||||
|
size = 255
|
||||||
|
if num_bytes<size
|
||||||
|
size = num_bytes
|
||||||
|
size = cx16.macptr(lsb(size), bufferpointer, false)
|
||||||
|
if_cs
|
||||||
|
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
||||||
|
list_blocks += size
|
||||||
|
bufferpointer += size
|
||||||
|
if msb(bufferpointer) == $c0
|
||||||
|
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||||
|
num_bytes -= size
|
||||||
|
if cbm.READST() & $40 {
|
||||||
|
f_close() ; end of file, close it
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list_blocks ; number of bytes read
|
||||||
|
|
||||||
|
byte_read_loop: ; fallback if macptr() isn't supported on the device
|
||||||
|
%asm {{
|
||||||
|
lda bufferpointer
|
||||||
|
sta m_in_buffer+1
|
||||||
|
lda bufferpointer+1
|
||||||
|
sta m_in_buffer+2
|
||||||
|
}}
|
||||||
|
while num_bytes {
|
||||||
|
if cbm.READST() {
|
||||||
|
f_close()
|
||||||
|
if cbm.READST() & $40 ; eof?
|
||||||
|
return list_blocks ; number of bytes read
|
||||||
|
return 0 ; error.
|
||||||
|
}
|
||||||
|
%asm {{
|
||||||
|
jsr cbm.CHRIN
|
||||||
|
m_in_buffer sta $ffff
|
||||||
|
inc m_in_buffer+1
|
||||||
|
bne +
|
||||||
|
inc m_in_buffer+2
|
||||||
|
+
|
||||||
|
}}
|
||||||
|
list_blocks++
|
||||||
|
num_bytes--
|
||||||
|
}
|
||||||
|
return list_blocks ; number of bytes read
|
||||||
|
}
|
||||||
|
|
||||||
|
; optimized for Commander X16 to use MACPTR block read kernal call
|
||||||
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
|
; -- read the full contents of the file, returns number of bytes read.
|
||||||
|
if not iteration_in_progress
|
||||||
|
return 0
|
||||||
|
|
||||||
|
uword total_read = 0
|
||||||
|
while not cbm.READST() {
|
||||||
|
cx16.r0 = f_read(bufferpointer, 256)
|
||||||
|
total_read += cx16.r0
|
||||||
|
bufferpointer += cx16.r0
|
||||||
|
}
|
||||||
|
return total_read
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
|
||||||
|
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||||
|
; Reads characters from the input file UNTIL a newline or return character (or EOF).
|
||||||
|
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||||
|
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
|
||||||
|
; I/O error status should be checked by the caller itself via READST() routine.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldx #12
|
||||||
|
jsr cbm.CHKIN ; use channel 12 again for input
|
||||||
|
ldy #0
|
||||||
|
_loop jsr cbm.CHRIN
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
|
beq _end
|
||||||
|
iny
|
||||||
|
cmp #$0a
|
||||||
|
beq _line_end
|
||||||
|
cmp #$0d
|
||||||
|
bne _loop
|
||||||
|
_line_end dey ; get rid of the trailing end-of-line char
|
||||||
|
lda #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
|
_end rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub f_close() {
|
||||||
|
; -- end an iterative file loading session (close channels).
|
||||||
|
if iteration_in_progress {
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(12)
|
||||||
|
iteration_in_progress = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ----- iterative file writing functions (uses io channel 13) -----
|
||||||
|
|
||||||
|
sub f_open_w(uword filenameptr) -> bool {
|
||||||
|
; -- open a file for iterative writing with f_write
|
||||||
|
f_close_w()
|
||||||
|
|
||||||
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
|
cbm.SETLFS(13, drivenumber, 1)
|
||||||
|
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||||
|
if_cc {
|
||||||
|
cbm.CHKOUT(13) ; use #13 as output channel
|
||||||
|
return not cbm.READST()
|
||||||
|
}
|
||||||
|
f_close_w()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||||
|
; -- write the given number of bytes to the currently open file
|
||||||
|
if num_bytes!=0 {
|
||||||
|
cbm.CHKOUT(13) ; use #13 as output channel again
|
||||||
|
repeat num_bytes {
|
||||||
|
cbm.CHROUT(@(bufferpointer))
|
||||||
|
bufferpointer++
|
||||||
|
}
|
||||||
|
return not cbm.READST()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub f_close_w() {
|
||||||
|
; -- end an iterative file writing session (close channels).
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(13)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ---- other functions ----
|
||||||
|
|
||||||
|
sub status() -> uword {
|
||||||
|
; -- retrieve the disk drive's current status message
|
||||||
|
uword messageptr = &list_filename
|
||||||
|
cbm.SETNAM(0, list_filename)
|
||||||
|
cbm.SETLFS(15, drivenumber, 15)
|
||||||
|
void cbm.OPEN() ; open 15,8,15
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void cbm.CHKIN(15) ; use #15 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
while not cbm.READST() {
|
||||||
|
cx16.r5L = cbm.CHRIN()
|
||||||
|
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||||
|
break
|
||||||
|
@(messageptr) = cx16.r5L
|
||||||
|
messageptr++
|
||||||
|
}
|
||||||
|
@(messageptr) = 0
|
||||||
|
|
||||||
|
done:
|
||||||
|
cbm.CLRCHN() ; restore default i/o devices
|
||||||
|
cbm.CLOSE(15)
|
||||||
|
return list_filename
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
list_filename = "?disk error"
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; saves a block of memory to disk, including the default 2 byte prg header.
|
||||||
|
sub save(uword filenameptr, uword address, uword size) -> bool {
|
||||||
|
return internal_save_routine(filenameptr, address, size, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
; like save() but omits the 2 byte prg header.
|
||||||
|
sub save_raw(uword filenameptr, uword address, uword size) -> bool {
|
||||||
|
return internal_save_routine(filenameptr, address, size, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub internal_save_routine(uword filenameptr, uword address, uword size, bool headerless) -> bool {
|
||||||
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
|
cbm.SETLFS(1, drivenumber, 0)
|
||||||
|
uword @shared end_address = address + size
|
||||||
|
cx16.r0L = 0
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
lda address
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda address+1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
ldx end_address
|
||||||
|
ldy end_address+1
|
||||||
|
lda headerless
|
||||||
|
beq +
|
||||||
|
lda #<P8ZP_SCRATCH_W1
|
||||||
|
jsr cx16.BSAVE
|
||||||
|
bra ++
|
||||||
|
+ lda #<P8ZP_SCRATCH_W1
|
||||||
|
jsr cbm.SAVE
|
||||||
|
+ php
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
plp
|
||||||
|
}}
|
||||||
|
|
||||||
|
if_cc
|
||||||
|
cx16.r0L = cbm.READST()==0
|
||||||
|
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(1)
|
||||||
|
|
||||||
|
return cx16.r0L
|
||||||
|
}
|
||||||
|
|
||||||
|
; Use kernal LOAD routine to load the given program file in memory.
|
||||||
|
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
|
||||||
|
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
|
||||||
|
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
||||||
|
; and the rest is loaded at the given location in memory.
|
||||||
|
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||||
|
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
|
||||||
|
; (which is possible on the Commander X16), the returned size is not correct,
|
||||||
|
; because it doesn't take the number of ram banks into account.
|
||||||
|
; You can use the load_size() function to calcuate the size in this case.
|
||||||
|
; NOTE: data is read into the current Ram bank if you're reading into banked ram.
|
||||||
|
; if you require loading into another ram bank, you have to set that
|
||||||
|
; yourself using cx16.rambank(bank) before calling load().
|
||||||
|
sub load(uword filenameptr, uword address_override) -> uword {
|
||||||
|
return internal_load_routine(filenameptr, address_override, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||||
|
; No program header is assumed in the file. Everything is loaded.
|
||||||
|
; See comments on load() for more details.
|
||||||
|
sub load_raw(uword filenameptr, uword address) -> uword {
|
||||||
|
return internal_load_routine(filenameptr, address, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||||
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
|
ubyte secondary = 1
|
||||||
|
cx16.r1 = 0
|
||||||
|
if address_override
|
||||||
|
secondary = 0
|
||||||
|
if headerless
|
||||||
|
secondary |= %00000010 ; activate cx16 kernal headerless load support
|
||||||
|
cbm.SETLFS(1, drivenumber, secondary)
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
lda #0
|
||||||
|
ldx address_override
|
||||||
|
ldy address_override+1
|
||||||
|
jsr cbm.LOAD
|
||||||
|
bcs +
|
||||||
|
stx cx16.r1
|
||||||
|
sty cx16.r1+1
|
||||||
|
+ ldx P8ZP_SCRATCH_REG
|
||||||
|
}}
|
||||||
|
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(1)
|
||||||
|
return cx16.r1
|
||||||
|
}
|
||||||
|
|
||||||
|
sub delete(uword filenameptr) {
|
||||||
|
; -- delete a file on the drive
|
||||||
|
list_filename[0] = 's'
|
||||||
|
list_filename[1] = ':'
|
||||||
|
ubyte flen = string.copy(filenameptr, &list_filename+2)
|
||||||
|
cbm.SETNAM(flen+2, list_filename)
|
||||||
|
cbm.SETLFS(1, drivenumber, 15)
|
||||||
|
void cbm.OPEN()
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub rename(uword oldfileptr, uword newfileptr) {
|
||||||
|
; -- rename a file on the drive
|
||||||
|
list_filename[0] = 'r'
|
||||||
|
list_filename[1] = ':'
|
||||||
|
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
|
||||||
|
list_filename[flen_new+2] = '='
|
||||||
|
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
|
||||||
|
cbm.SETNAM(3+flen_new+flen_old, list_filename)
|
||||||
|
cbm.SETLFS(1, drivenumber, 15)
|
||||||
|
void cbm.OPEN()
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub send_command(uword commandptr) {
|
||||||
|
; -- send a dos command to the drive
|
||||||
|
cbm.SETNAM(string.length(commandptr), commandptr)
|
||||||
|
cbm.SETLFS(15, drivenumber, 15)
|
||||||
|
void cbm.OPEN()
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(15)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; CommanderX16 extensions over the basic C64/C128 diskio routines:
|
||||||
|
|
||||||
|
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
|
||||||
|
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
|
||||||
|
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
|
||||||
|
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||||
|
; -- like the basic command VLOAD "filename",drivenumber,bank,address
|
||||||
|
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||||
|
; the file has to have the usual 2 byte header (which will be skipped)
|
||||||
|
%asm {{
|
||||||
|
clc
|
||||||
|
internal_vload:
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
ldx drivenumber
|
||||||
|
bcc +
|
||||||
|
ldy #%00000010 ; headerless load mode
|
||||||
|
bne ++
|
||||||
|
+ ldy #0 ; normal load mode
|
||||||
|
+ lda #1
|
||||||
|
jsr cbm.SETLFS
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jsr prog8_lib.strlen
|
||||||
|
tya
|
||||||
|
ldx cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jsr cbm.SETNAM
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc #2
|
||||||
|
ldx cx16.r1
|
||||||
|
ldy cx16.r1+1
|
||||||
|
stz P8ZP_SCRATCH_B1
|
||||||
|
jsr cbm.LOAD
|
||||||
|
bcs +
|
||||||
|
inc P8ZP_SCRATCH_B1
|
||||||
|
+ jsr cbm.CLRCHN
|
||||||
|
lda #1
|
||||||
|
jsr cbm.CLOSE
|
||||||
|
plx
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||||
|
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
|
||||||
|
; loads a file into Vera's video memory in the given bank:address, returns success in A
|
||||||
|
; the file is read fully including the first two bytes.
|
||||||
|
%asm {{
|
||||||
|
sec
|
||||||
|
jmp vload.internal_vload
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub chdir(str path) {
|
||||||
|
; -- change current directory.
|
||||||
|
list_filename[0] = 'c'
|
||||||
|
list_filename[1] = 'd'
|
||||||
|
list_filename[2] = ':'
|
||||||
|
void string.copy(path, &list_filename+3)
|
||||||
|
send_command(list_filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub mkdir(str name) {
|
||||||
|
; -- make a new subdirectory.
|
||||||
|
list_filename[0] = 'm'
|
||||||
|
list_filename[1] = 'd'
|
||||||
|
list_filename[2] = ':'
|
||||||
|
void string.copy(name, &list_filename+3)
|
||||||
|
send_command(list_filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub rmdir(str name) {
|
||||||
|
; -- remove a subdirectory.
|
||||||
|
void string.find(name, '*')
|
||||||
|
if_cs
|
||||||
|
return ; refuse to act on a wildcard *
|
||||||
|
list_filename[0] = 'r'
|
||||||
|
list_filename[1] = 'd'
|
||||||
|
list_filename[2] = ':'
|
||||||
|
void string.copy(name, &list_filename+3)
|
||||||
|
send_command(list_filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub curdir() -> uword {
|
||||||
|
; return current directory name or 0 if error
|
||||||
|
; special X16 dos command to only return the current path in the entry list (R42+)
|
||||||
|
const ubyte MAX_PATH_LEN=80
|
||||||
|
uword reversebuffer = memory("curdir_buffer", MAX_PATH_LEN, 0)
|
||||||
|
cx16.r12 = reversebuffer + MAX_PATH_LEN-1
|
||||||
|
@(cx16.r12)=0
|
||||||
|
cbm.SETNAM(3, "$=c")
|
||||||
|
cbm.SETLFS(12, diskio.drivenumber, 0)
|
||||||
|
void cbm.OPEN() ; open 12,8,0,"$=c"
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
repeat 6 {
|
||||||
|
void cbm.CHRIN()
|
||||||
|
}
|
||||||
|
while cbm.CHRIN() {
|
||||||
|
; skip first line (drive label)
|
||||||
|
}
|
||||||
|
while cbm.CHRIN()!='"' {
|
||||||
|
; skip to first name
|
||||||
|
}
|
||||||
|
ubyte status = cbm.READST()
|
||||||
|
cx16.r10 = &list_filename
|
||||||
|
while status==0 {
|
||||||
|
repeat {
|
||||||
|
@(cx16.r10) = cbm.CHRIN()
|
||||||
|
if @(cx16.r10)==0
|
||||||
|
break
|
||||||
|
cx16.r10++
|
||||||
|
}
|
||||||
|
while @(cx16.r10)!='"' and cx16.r10>=&list_filename {
|
||||||
|
@(cx16.r10)=0
|
||||||
|
cx16.r10--
|
||||||
|
}
|
||||||
|
@(cx16.r10)=0
|
||||||
|
prepend(list_filename)
|
||||||
|
cx16.r10 = &list_filename
|
||||||
|
while cbm.CHRIN()!='"' and status==0 {
|
||||||
|
status = cbm.READST()
|
||||||
|
; skipping up to next entry name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
cbm.CLRCHN()
|
||||||
|
cbm.CLOSE(12)
|
||||||
|
if status and status & $40 == 0
|
||||||
|
return 0
|
||||||
|
if @(cx16.r12)==0 {
|
||||||
|
cx16.r12--
|
||||||
|
@(cx16.r12)='/'
|
||||||
|
}
|
||||||
|
return cx16.r12
|
||||||
|
|
||||||
|
sub prepend(str dir) {
|
||||||
|
if dir[0]=='/' and dir[1]==0
|
||||||
|
return
|
||||||
|
cx16.r9L = string.length(dir)
|
||||||
|
cx16.r12 -= cx16.r9L
|
||||||
|
sys.memcopy(dir, cx16.r12, cx16.r9L)
|
||||||
|
cx16.r12--
|
||||||
|
@(cx16.r12)='/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub relabel(str name) {
|
||||||
|
; -- change the disk label.
|
||||||
|
list_filename[0] = 'r'
|
||||||
|
list_filename[1] = '-'
|
||||||
|
list_filename[2] = 'h'
|
||||||
|
list_filename[3] = ':'
|
||||||
|
void string.copy(name, &list_filename+4)
|
||||||
|
send_command(list_filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub f_seek(uword pos_hiword, uword pos_loword) {
|
||||||
|
; -- seek in the reading file opened with f_open, to the given 32-bits position
|
||||||
|
ubyte[6] command = ['p',0,0,0,0,0]
|
||||||
|
command[1] = 12 ; f_open uses channel 12
|
||||||
|
command[2] = lsb(pos_loword)
|
||||||
|
command[3] = msb(pos_loword)
|
||||||
|
command[4] = lsb(pos_hiword)
|
||||||
|
command[5] = msb(pos_hiword)
|
||||||
|
send_command:
|
||||||
|
cbm.SETNAM(sizeof(command), &command)
|
||||||
|
cbm.SETLFS(15, drivenumber, 15)
|
||||||
|
void cbm.OPEN()
|
||||||
|
cbm.CLOSE(15)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; NOTE: f_seek_w() doesn't work reliably right now. I only manage to corrupt the fat32 filesystem on the sdcard with it...
|
||||||
|
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
|
||||||
|
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
|
||||||
|
; diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
|
||||||
|
; diskio.f_seek.command[2] = lsb(pos_loword)
|
||||||
|
; diskio.f_seek.command[3] = msb(pos_loword)
|
||||||
|
; diskio.f_seek.command[4] = lsb(pos_hiword)
|
||||||
|
; diskio.f_seek.command[5] = msb(pos_hiword)
|
||||||
|
; goto diskio.f_seek.send_command
|
||||||
|
; }
|
||||||
|
|
||||||
|
}
|
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
floats {
|
floats {
|
||||||
; ---- this block contains C-64 compatible floating point related functions ----
|
; ---- this block contains C-64 compatible floating point related functions ----
|
||||||
; the addresses are from cx16 V39 emulator and roms! they won't work on older versions.
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
@ -40,8 +41,8 @@ romsub $fe18 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt valu
|
|||||||
romsub $fe1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
romsub $fe1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
||||||
romsub $fe1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
romsub $fe1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||||
romsub $fe21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
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 (remainder in 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 (remainder in fac2) mind the order of the operands
|
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 $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 FADDH first to round instead of trunc
|
||||||
romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
@ -76,7 +77,7 @@ romsub $fe7b = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
|||||||
romsub $fe7e = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
romsub $fe7e = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||||
romsub $fe81 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
romsub $fe81 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||||
romsub $fe84 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
romsub $fe84 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||||
romsub $fe87 = FLOAT() clobbers(A,X,Y) ; FAC = (u8).A
|
romsub $fe87 = FLOAT() clobbers(A,X,Y) ; FAC = (s8).A
|
||||||
romsub $fe8a = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
romsub $fe8a = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
||||||
romsub $fe8d = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
romsub $fe8d = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
||||||
romsub $fe90 = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
romsub $fe90 = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
@ -133,8 +134,8 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
asmsub FREADUY (ubyte value @Y) {
|
asmsub FREADUY (ubyte value @Y) {
|
||||||
; -- 8 bit unsigned Y -> float in fac1
|
; -- 8 bit unsigned Y -> float in fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
tya
|
lda #0
|
||||||
jmp FLOAT
|
jmp GIVAYF
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
gfx2 {
|
gfx2 {
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; read-only control variables:
|
; read-only control variables:
|
||||||
ubyte active_mode = 0
|
ubyte active_mode = 0
|
||||||
uword width = 0
|
uword width = 0
|
||||||
@ -84,7 +86,7 @@ gfx2 {
|
|||||||
else -> {
|
else -> {
|
||||||
; back to default text mode
|
; back to default text mode
|
||||||
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
|
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
|
||||||
c64.CINT()
|
cbm.CINT()
|
||||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
|
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
|
||||||
width = 0
|
width = 0
|
||||||
height = 0
|
height = 0
|
||||||
@ -155,6 +157,9 @@ gfx2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub horizontal_line(uword x, uword y, uword length, ubyte color) {
|
sub horizontal_line(uword x, uword y, uword length, ubyte color) {
|
||||||
|
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
|
||||||
|
ubyte[9] masked_starts = [ 0, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111]
|
||||||
|
|
||||||
if length==0
|
if length==0
|
||||||
return
|
return
|
||||||
when active_mode {
|
when active_mode {
|
||||||
@ -163,12 +168,13 @@ gfx2 {
|
|||||||
ubyte separate_pixels = (8-lsb(x)) & 7
|
ubyte separate_pixels = (8-lsb(x)) & 7
|
||||||
if separate_pixels as uword > length
|
if separate_pixels as uword > length
|
||||||
separate_pixels = lsb(length)
|
separate_pixels = lsb(length)
|
||||||
repeat separate_pixels {
|
if separate_pixels {
|
||||||
; TODO optimize this by writing a masked byte in 1 go
|
position(x,y)
|
||||||
plot(x, y, color)
|
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
|
||||||
x++
|
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_starts[separate_pixels]
|
||||||
|
length -= separate_pixels
|
||||||
|
x += separate_pixels
|
||||||
}
|
}
|
||||||
length -= separate_pixels
|
|
||||||
if length {
|
if length {
|
||||||
position(x, y)
|
position(x, y)
|
||||||
separate_pixels = lsb(length) & 7
|
separate_pixels = lsb(length) & 7
|
||||||
@ -205,11 +211,9 @@ _loop lda length
|
|||||||
bra _loop
|
bra _loop
|
||||||
_done
|
_done
|
||||||
}}
|
}}
|
||||||
repeat separate_pixels {
|
|
||||||
; TODO optimize this by writing a masked byte in 1 go
|
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
|
||||||
plot(x, y, color)
|
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_ends[separate_pixels]
|
||||||
x++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
|
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
|
||||||
}
|
}
|
||||||
@ -366,8 +370,14 @@ _done
|
|||||||
return
|
return
|
||||||
position2(x,y,true)
|
position2(x,y,true)
|
||||||
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||||
color &= 3
|
;; color &= 3
|
||||||
color <<= gfx2.plot.shift4c[lsb(x) & 3] ; TODO with lookup table
|
;; color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||||
|
cx16.r2L = lsb(x) & 3
|
||||||
|
when color & 3 {
|
||||||
|
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
|
||||||
|
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
|
||||||
|
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
|
||||||
|
}
|
||||||
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
|
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||||
repeat lheight {
|
repeat lheight {
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -547,10 +557,13 @@ _done
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plot(uword @zp x, uword y, ubyte color) {
|
sub plot(uword @zp x, uword @zp y, ubyte @zp color) {
|
||||||
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||||
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
|
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
|
||||||
ubyte[4] @shared shift4c = [6,4,2,0]
|
ubyte[4] @shared shift4c = [6,4,2,0]
|
||||||
|
ubyte[4] shiftedleft_4c_1 = [1<<6, 1<<4, 1<<2, 1<<0]
|
||||||
|
ubyte[4] shiftedleft_4c_2 = [2<<6, 2<<4, 2<<2, 2<<0]
|
||||||
|
ubyte[4] shiftedleft_4c_3 = [3<<6, 3<<4, 3<<2, 3<<0]
|
||||||
|
|
||||||
when active_mode {
|
when active_mode {
|
||||||
1 -> {
|
1 -> {
|
||||||
@ -643,8 +656,13 @@ _done
|
|||||||
; TODO also mostly usable for lores 4c?
|
; TODO also mostly usable for lores 4c?
|
||||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||||
cx16.r2L = lsb(x) & 3 ; xbits
|
cx16.r2L = lsb(x) & 3 ; xbits
|
||||||
color &= 3
|
; color &= 3
|
||||||
color <<= shift4c[cx16.r2L] ; TODO with lookup table
|
; color <<= shift4c[cx16.r2L]
|
||||||
|
when color & 3 {
|
||||||
|
1 -> color = shiftedleft_4c_1[cx16.r2L]
|
||||||
|
2 -> color = shiftedleft_4c_2[cx16.r2L]
|
||||||
|
3 -> color = shiftedleft_4c_3[cx16.r2L]
|
||||||
|
}
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
lda cx16.r1L
|
lda cx16.r1L
|
||||||
@ -743,13 +761,125 @@ _done
|
|||||||
sta cx16.r0L
|
sta cx16.r0L
|
||||||
}}
|
}}
|
||||||
cx16.r1L = lsb(x) & 3
|
cx16.r1L = lsb(x) & 3
|
||||||
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L] ; TODO with lookup table
|
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L]
|
||||||
return cx16.r0L & 3
|
return cx16.r0L & 3
|
||||||
}
|
}
|
||||||
else -> return 0
|
else -> return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub fill(word @zp x, word @zp y, ubyte new_color) {
|
||||||
|
; Non-recursive scanline flood fill.
|
||||||
|
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
|
||||||
|
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
|
||||||
|
const ubyte MAXDEPTH = 48
|
||||||
|
word[MAXDEPTH] @split @shared stack_xl
|
||||||
|
word[MAXDEPTH] @split @shared stack_xr
|
||||||
|
word[MAXDEPTH] @split @shared stack_y
|
||||||
|
byte[MAXDEPTH] @shared stack_dy
|
||||||
|
cx16.r12L = 0 ; stack pointer
|
||||||
|
word x1
|
||||||
|
word x2
|
||||||
|
byte dy
|
||||||
|
cx16.r10L = new_color
|
||||||
|
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
|
||||||
|
if cx16.r12L==MAXDEPTH
|
||||||
|
return
|
||||||
|
cx16.r0s = sy+sdy
|
||||||
|
if cx16.r0s>=0 and cx16.r0s<=height-1 {
|
||||||
|
;; stack_xl[cx16.r12L] = sxl
|
||||||
|
;; stack_xr[cx16.r12L] = sxr
|
||||||
|
;; stack_y[cx16.r12L] = sy
|
||||||
|
;; stack_dy[cx16.r12L] = sdy
|
||||||
|
;; cx16.r12L++
|
||||||
|
%asm {{
|
||||||
|
ldy cx16.r12L
|
||||||
|
lda sxl
|
||||||
|
sta stack_xl_lsb,y
|
||||||
|
lda sxl+1
|
||||||
|
sta stack_xl_msb,y
|
||||||
|
lda sxr
|
||||||
|
sta stack_xr_lsb,y
|
||||||
|
lda sxr+1
|
||||||
|
sta stack_xr_msb,y
|
||||||
|
lda sy
|
||||||
|
sta stack_y_lsb,y
|
||||||
|
lda sy+1
|
||||||
|
sta stack_y_msb,y
|
||||||
|
ldy cx16.r12L
|
||||||
|
lda sdy
|
||||||
|
sta stack_dy,y
|
||||||
|
inc cx16.r12L
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sub pop_stack() {
|
||||||
|
;; cx16.r12L--
|
||||||
|
;; x1 = stack_xl[cx16.r12L]
|
||||||
|
;; x2 = stack_xr[cx16.r12L]
|
||||||
|
;; y = stack_y[cx16.r12L]
|
||||||
|
;; dy = stack_dy[cx16.r12L]
|
||||||
|
%asm {{
|
||||||
|
dec cx16.r12L
|
||||||
|
ldy cx16.r12L
|
||||||
|
lda stack_xl_lsb,y
|
||||||
|
sta x1
|
||||||
|
lda stack_xl_msb,y
|
||||||
|
sta x1+1
|
||||||
|
lda stack_xr_lsb,y
|
||||||
|
sta x2
|
||||||
|
lda stack_xr_msb,y
|
||||||
|
sta x2+1
|
||||||
|
lda stack_y_lsb,y
|
||||||
|
sta y
|
||||||
|
lda stack_y_msb,y
|
||||||
|
sta y+1
|
||||||
|
ldy cx16.r12L
|
||||||
|
lda stack_dy,y
|
||||||
|
sta dy
|
||||||
|
}}
|
||||||
|
y+=dy
|
||||||
|
}
|
||||||
|
cx16.r11L = pget(x as uword, y as uword) ; old_color
|
||||||
|
if cx16.r11L == cx16.r10L
|
||||||
|
return
|
||||||
|
if x<0 or x > width-1 or y<0 or y > height-1
|
||||||
|
return
|
||||||
|
push_stack(x, x, y, 1)
|
||||||
|
push_stack(x, x, y + 1, -1)
|
||||||
|
word left = 0
|
||||||
|
while cx16.r12L {
|
||||||
|
pop_stack()
|
||||||
|
x = x1
|
||||||
|
while x >= 0 and pget(x as uword, y as uword) == cx16.r11L {
|
||||||
|
plot(x as uword, y as uword, cx16.r10L)
|
||||||
|
x--
|
||||||
|
}
|
||||||
|
if x>= x1
|
||||||
|
goto skip
|
||||||
|
|
||||||
|
left = x + 1
|
||||||
|
if left < x1
|
||||||
|
push_stack(left, x1 - 1, y, -dy)
|
||||||
|
x = x1 + 1
|
||||||
|
|
||||||
|
do {
|
||||||
|
while x <= width-1 and pget(x as uword, y as uword) == cx16.r11L {
|
||||||
|
plot(x as uword, y as uword, cx16.r10L)
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
push_stack(left, x - 1, y, dy)
|
||||||
|
if x > x2 + 1
|
||||||
|
push_stack(x2 + 1, x - 1, y, -dy)
|
||||||
|
skip:
|
||||||
|
x++
|
||||||
|
while x <= x2 and pget(x as uword, y as uword) != cx16.r11L
|
||||||
|
x++
|
||||||
|
left = x
|
||||||
|
} until x>x2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub position(uword @zp x, uword y) {
|
sub position(uword @zp x, uword y) {
|
||||||
ubyte bank
|
ubyte bank
|
||||||
when active_mode {
|
when active_mode {
|
||||||
@ -945,11 +1075,12 @@ _done
|
|||||||
cx16.r8 = y
|
cx16.r8 = y
|
||||||
while @(sctextptr) {
|
while @(sctextptr) {
|
||||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||||
|
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
|
||||||
repeat 8 {
|
repeat 8 {
|
||||||
; TODO rewrite this inner loop partly in assembly
|
; TODO rewrite this inner loop partly in assembly:
|
||||||
; requires expanding the charbits to 2-bits per pixel (based on color)
|
; requires expanding the charbits to 2-bits per pixel (based on color)
|
||||||
; also it's way more efficient to draw whole horizontal spans instead of per-character
|
; also it's way more efficient to draw whole horizontal spans instead of per-character
|
||||||
cx16.r9L = cx16.vpeek(charset_bank, chardataptr) ; get the 8 horizontal character bits
|
cx16.r9L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
|
||||||
cx16.r7 = x
|
cx16.r7 = x
|
||||||
repeat 8 {
|
repeat 8 {
|
||||||
cx16.r9L <<= 1
|
cx16.r9L <<= 1
|
||||||
@ -957,7 +1088,6 @@ _done
|
|||||||
plot(cx16.r7, cx16.r8, cx16.r11L)
|
plot(cx16.r7, cx16.r8, cx16.r11L)
|
||||||
cx16.r7++
|
cx16.r7++
|
||||||
}
|
}
|
||||||
chardataptr++
|
|
||||||
cx16.r8++
|
cx16.r8++
|
||||||
}
|
}
|
||||||
x+=8
|
x+=8
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const uword WIDTH = 320
|
const uword WIDTH = 320
|
||||||
const ubyte HEIGHT = 240
|
const ubyte HEIGHT = 240
|
||||||
|
|
||||||
@ -143,7 +145,7 @@ graphics {
|
|||||||
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr cx16.FB_cursor_position
|
jsr cx16.FB_cursor_position
|
||||||
lda #1
|
lda graphics.stroke_color
|
||||||
jsr cx16.FB_set_pixel
|
jsr cx16.FB_set_pixel
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
|
; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
|
||||||
|
|
||||||
palette {
|
palette {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
uword vera_palette_ptr
|
uword vera_palette_ptr
|
||||||
ubyte c
|
ubyte cc
|
||||||
|
|
||||||
sub set_color(ubyte index, uword color) {
|
sub set_color(ubyte index, uword color) {
|
||||||
vera_palette_ptr = $fa00+(index as uword * 2)
|
vera_palette_ptr = $fa00+(index as uword * 2)
|
||||||
@ -79,13 +80,13 @@ palette {
|
|||||||
sub set_grayscale() {
|
sub set_grayscale() {
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat 16 {
|
repeat 16 {
|
||||||
c=0
|
cc=0
|
||||||
repeat 16 {
|
repeat 16 {
|
||||||
cx16.vpoke(1, vera_palette_ptr, c)
|
cx16.vpoke(1, vera_palette_ptr, cc)
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, c)
|
cx16.vpoke(1, vera_palette_ptr, cc)
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
c += $11
|
cc += $11
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,11 +151,11 @@ palette {
|
|||||||
sub set_c64pepto() {
|
sub set_c64pepto() {
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat 16 {
|
repeat 16 {
|
||||||
for c in 0 to 15 {
|
for cc in 0 to 15 {
|
||||||
uword cc = C64_colorpalette_pepto[c]
|
uword ccp = C64_colorpalette_pepto[cc]
|
||||||
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
|
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
|
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,11 +164,11 @@ palette {
|
|||||||
sub set_c64light() {
|
sub set_c64light() {
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat 16 {
|
repeat 16 {
|
||||||
for c in 0 to 15 {
|
for cc in 0 to 15 {
|
||||||
uword cc = C64_colorpalette_light[c]
|
uword ccp = C64_colorpalette_light[cc]
|
||||||
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
|
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
|
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,11 +177,11 @@ palette {
|
|||||||
sub set_c64dark() {
|
sub set_c64dark() {
|
||||||
vera_palette_ptr = $fa00
|
vera_palette_ptr = $fa00
|
||||||
repeat 16 {
|
repeat 16 {
|
||||||
for c in 0 to 15 {
|
for cc in 0 to 15 {
|
||||||
uword cc = C64_colorpalette_dark[c]
|
uword ccp = C64_colorpalette_dark[cc]
|
||||||
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
|
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
|
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
|
||||||
vera_palette_ptr++
|
vera_palette_ptr++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
%import syslib
|
%import syslib
|
||||||
|
|
||||||
psg {
|
psg {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
|
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
|
||||||
; 00 frequency word LSB
|
; 00 frequency word LSB
|
||||||
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
|
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
|
||||||
@ -22,6 +24,7 @@ psg {
|
|||||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||||
envelope_states[voice_num] = 255
|
envelope_states[voice_num] = 255
|
||||||
|
sys.irqsafe_set_irqd()
|
||||||
cx16.r0 = $f9c2 + voice_num * 4
|
cx16.r0 = $f9c2 + voice_num * 4
|
||||||
cx16.VERA_CTRL = 0
|
cx16.VERA_CTRL = 0
|
||||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||||
@ -32,6 +35,7 @@ psg {
|
|||||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||||
envelope_volumes[voice_num] = mkword(volume, 0)
|
envelope_volumes[voice_num] = mkword(volume, 0)
|
||||||
envelope_maxvolumes[voice_num] = volume
|
envelope_maxvolumes[voice_num] = volume
|
||||||
|
sys.irqsafe_clear_irqd()
|
||||||
}
|
}
|
||||||
|
|
||||||
; sub freq_hz(ubyte voice_num, float hertz) {
|
; sub freq_hz(ubyte voice_num, float hertz) {
|
||||||
@ -44,48 +48,50 @@ psg {
|
|||||||
sub freq(ubyte voice_num, uword vera_freq) {
|
sub freq(ubyte voice_num, uword vera_freq) {
|
||||||
; -- Changes the frequency of the voice's sound.
|
; -- Changes the frequency of the voice's sound.
|
||||||
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
|
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
|
||||||
; (https://github.com/commanderx16/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
|
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
|
||||||
cx16.r0 = $f9c0 + voice_num * 4
|
; Write freq MSB first and then LSB to reduce the chance on clicks
|
||||||
|
sys.irqsafe_set_irqd()
|
||||||
|
cx16.r0 = $f9c1 + voice_num * 4
|
||||||
cx16.VERA_CTRL = 0
|
cx16.VERA_CTRL = 0
|
||||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||||
cx16.VERA_ADDR_M = msb(cx16.r0)
|
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||||
cx16.VERA_ADDR_H = 1
|
cx16.VERA_ADDR_H = 1
|
||||||
cx16.VERA_DATA0 = lsb(vera_freq)
|
|
||||||
cx16.VERA_ADDR_L++
|
|
||||||
cx16.VERA_DATA0 = msb(vera_freq)
|
cx16.VERA_DATA0 = msb(vera_freq)
|
||||||
|
cx16.VERA_ADDR_L--
|
||||||
|
cx16.VERA_DATA0 = lsb(vera_freq)
|
||||||
|
sys.irqsafe_clear_irqd()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub volume(ubyte voice_num, ubyte vol) {
|
sub volume(ubyte voice_num, ubyte vol) {
|
||||||
; -- Modifies the volume of this voice.
|
; -- Modifies the volume of this voice.
|
||||||
; voice_num = 0-15, vol = 0-63 where 0=silent, 63=loudest.
|
; voice_num = 0-15, vol = 0-63 where 0=silent, 63=loudest.
|
||||||
cx16.r0 = $f9c2 + voice_num * 4
|
|
||||||
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | vol)
|
|
||||||
envelope_volumes[voice_num] = mkword(vol, 0)
|
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||||
|
cx16.vpoke_mask(1, $f9c2 + voice_num * 4, %11000000, vol)
|
||||||
envelope_maxvolumes[voice_num] = vol
|
envelope_maxvolumes[voice_num] = vol
|
||||||
}
|
}
|
||||||
|
|
||||||
sub pulse_width(ubyte voice_num, ubyte pw) {
|
sub pulse_width(ubyte voice_num, ubyte pw) {
|
||||||
; -- Modifies the pulse width of this voice (when waveform=PULSE)
|
; -- Modifies the pulse width of this voice (when waveform=PULSE)
|
||||||
; voice_num = 0-15, pw = 0-63 where 0=narrow, 63=50%cycle so square wave.
|
; voice_num = 0-15, pw = 0-63 where 0=narrow, 63=50%cycle so square wave.
|
||||||
cx16.r0 = $f9c3 + voice_num * 4
|
cx16.vpoke_mask(1, $f9c3 + voice_num * 4, %11000000, pw)
|
||||||
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | pw)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
|
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
|
||||||
; -- Enables AttackSustainRelease volume envelope for a voice.
|
; -- Enables AttackSustainRelease volume envelope for a voice.
|
||||||
; Note: this requires setting up envelopes_irq() as well, read its description.
|
; Note: this requires setting up envelopes_irq() as well, read its description.
|
||||||
; voice_num = 0-15 maxvolume = 0-63
|
; voice_num = 0-15 maxvolume = 0-63
|
||||||
; attack, sustain, release = 0-255 that determine the speed of the A/D/R.
|
; attack, sustain, release = 0-255 that determine the speed of the A/D/R:
|
||||||
; TODO describe how the speeds are calculated. For now, experiment. Higher values means *slower* enveloping.
|
; attack time: MAXVOL/15/attack seconds. higher value = faster attack.
|
||||||
|
; sustain time: sustain/60 seconds higher sustain value = longer sustain (!).
|
||||||
|
; release time: MAXVOL/15/release seconds. higher vaule = faster release.
|
||||||
|
|
||||||
envelope_states[voice_num] = 255
|
envelope_states[voice_num] = 255
|
||||||
envelope_attacks[voice_num] = attack
|
envelope_attacks[voice_num] = attack
|
||||||
envelope_sustains[voice_num] = sustain
|
envelope_sustains[voice_num] = sustain
|
||||||
envelope_releases[voice_num] = release
|
envelope_releases[voice_num] = release
|
||||||
if attack
|
cx16.r0 = mkword(maxvolume, 0)
|
||||||
attack = 0
|
if cx16.r0<envelope_volumes[voice_num]
|
||||||
else
|
envelope_volumes[voice_num] = cx16.r0
|
||||||
attack = maxvolume ; max volume when no attack is set
|
|
||||||
envelope_volumes[voice_num] = mkword(attack, 0)
|
|
||||||
envelope_maxvolumes[voice_num] = maxvolume
|
envelope_maxvolumes[voice_num] = maxvolume
|
||||||
envelope_states[voice_num] = 0
|
envelope_states[voice_num] = 0
|
||||||
}
|
}
|
||||||
@ -94,7 +100,6 @@ psg {
|
|||||||
; -- Shut down all PSG voices.
|
; -- Shut down all PSG voices.
|
||||||
for cx16.r1L in 0 to 15 {
|
for cx16.r1L in 0 to 15 {
|
||||||
envelope_states[cx16.r1L] = 255
|
envelope_states[cx16.r1L] = 255
|
||||||
envelope_volumes[cx16.r1L] = 0
|
|
||||||
volume(cx16.r1L, 0)
|
volume(cx16.r1L, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,11 +109,11 @@ psg {
|
|||||||
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
|
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
|
||||||
; or just install this routine as the only irq handler if you don't have to do other things there.
|
; or just install this routine as the only irq handler if you don't have to do other things there.
|
||||||
; Example: cx16.set_irq(&psg.envelopes_irq, true)
|
; Example: cx16.set_irq(&psg.envelopes_irq, true)
|
||||||
|
; NOTE: this routine calls save/restore_vera_context() for you, don't nest this or call it yourself!
|
||||||
|
|
||||||
; cx16.r0 = the volume word (volume scaled by 256)
|
; cx16.r0 = the volume word (volume scaled by 256)
|
||||||
; cx16.r1L = the voice number
|
; cx16.r1L = the voice number
|
||||||
; cx16.r2L = attack value
|
; cx16.r2L = attack value
|
||||||
|
|
||||||
pushw(cx16.r0)
|
pushw(cx16.r0)
|
||||||
push(cx16.r1L)
|
push(cx16.r1L)
|
||||||
push(cx16.r2L)
|
push(cx16.r2L)
|
||||||
@ -148,7 +153,7 @@ psg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
; set new volumes of all 16 voices, using vera stride of 4
|
; set new volumes of all 16 voices, using vera stride of 4
|
||||||
cx16.push_vera_context()
|
cx16.save_vera_context()
|
||||||
cx16.VERA_CTRL = 0
|
cx16.VERA_CTRL = 0
|
||||||
cx16.VERA_ADDR_L = $c2
|
cx16.VERA_ADDR_L = $c2
|
||||||
cx16.VERA_ADDR_M = $f9
|
cx16.VERA_ADDR_M = $f9
|
||||||
@ -160,7 +165,7 @@ psg {
|
|||||||
for cx16.r1L in 0 to 15 {
|
for cx16.r1L in 0 to 15 {
|
||||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||||
}
|
}
|
||||||
cx16.pop_vera_context()
|
cx16.restore_vera_context()
|
||||||
popw(cx16.r9)
|
popw(cx16.r9)
|
||||||
pop(cx16.r2L)
|
pop(cx16.r2L)
|
||||||
pop(cx16.r1L)
|
pop(cx16.r1L)
|
||||||
@ -168,7 +173,7 @@ psg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ubyte[16] envelope_states
|
ubyte[16] envelope_states
|
||||||
uword[16] envelope_volumes ; scaled by 256
|
uword[16] @split envelope_volumes ; scaled by 256
|
||||||
ubyte[16] envelope_attacks
|
ubyte[16] envelope_attacks
|
||||||
ubyte[16] envelope_sustains
|
ubyte[16] envelope_sustains
|
||||||
ubyte[16] envelope_releases
|
ubyte[16] envelope_releases
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
; Prog8 definitions for the CommanderX16
|
; Prog8 definitions for the CommanderX16
|
||||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
|
|
||||||
c64 {
|
cbm {
|
||||||
|
|
||||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
; Commodore (CBM) common variables, vectors and kernal routines
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; STROUT --> use txt.print
|
; STROUT --> use txt.print
|
||||||
; CLEARSCR -> use txt.clear_screen
|
; CLEARSCR -> use txt.clear_screen
|
||||||
@ -13,12 +15,12 @@ romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT
|
|||||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
|
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
|
||||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||||
@ -30,23 +32,23 @@ romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial
|
|||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
|
||||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
|
||||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
|
||||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
|
||||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
; ---- utility
|
; ---- utility
|
||||||
@ -55,7 +57,7 @@ asmsub STOP2() -> ubyte @A {
|
|||||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.STOP
|
jsr cbm.STOP
|
||||||
beq +
|
beq +
|
||||||
plx
|
plx
|
||||||
lda #0
|
lda #0
|
||||||
@ -67,10 +69,14 @@ asmsub STOP2() -> ubyte @A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub RDTIM16() -> uword @AY {
|
asmsub RDTIM16() -> uword @AY {
|
||||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs.
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.RDTIM
|
php
|
||||||
|
sei
|
||||||
|
jsr cbm.RDTIM
|
||||||
|
plp
|
||||||
|
cli
|
||||||
pha
|
pha
|
||||||
txa
|
txa
|
||||||
tay
|
tay
|
||||||
@ -84,38 +90,40 @@ asmsub RDTIM16() -> uword @AY {
|
|||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
; irq, system and hardware vectors:
|
; irq, system and hardware vectors:
|
||||||
&uword IERROR = $0300
|
&uword IERROR = $0300
|
||||||
&uword IMAIN = $0302
|
&uword IMAIN = $0302
|
||||||
&uword ICRNCH = $0304
|
&uword ICRNCH = $0304
|
||||||
&uword IQPLOP = $0306
|
&uword IQPLOP = $0306
|
||||||
&uword IGONE = $0308
|
&uword IGONE = $0308
|
||||||
&uword IEVAL = $030a
|
&uword IEVAL = $030a
|
||||||
&ubyte SAREG = $030c ; register storage for A for SYS calls
|
&ubyte SAREG = $030c ; register storage for A for SYS calls
|
||||||
&ubyte SXREG = $030d ; register storage for X for SYS calls
|
&ubyte SXREG = $030d ; register storage for X for SYS calls
|
||||||
&ubyte SYREG = $030e ; register storage for Y for SYS calls
|
&ubyte SYREG = $030e ; register storage for Y for SYS calls
|
||||||
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
|
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
|
||||||
&uword USRADD = $0311 ; vector for the USR() basic command
|
&uword USRADD = $0311 ; vector for the USR() basic command
|
||||||
; $0313 is unused.
|
; $0313 is unused.
|
||||||
&uword CINV = $0314 ; IRQ vector (in ram)
|
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||||
&uword CBINV = $0316 ; BRK vector (in ram)
|
&uword CBINV = $0316 ; BRK vector (in ram)
|
||||||
&uword NMINV = $0318 ; NMI vector (in ram)
|
&uword NMINV = $0318 ; NMI vector (in ram)
|
||||||
&uword IOPEN = $031a
|
&uword IOPEN = $031a
|
||||||
&uword ICLOSE = $031c
|
&uword ICLOSE = $031c
|
||||||
&uword ICHKIN = $031e
|
&uword ICHKIN = $031e
|
||||||
&uword ICKOUT = $0320
|
&uword ICKOUT = $0320
|
||||||
&uword ICLRCH = $0322
|
&uword ICLRCH = $0322
|
||||||
&uword IBASIN = $0324
|
&uword IBASIN = $0324
|
||||||
&uword IBSOUT = $0326
|
&uword IBSOUT = $0326
|
||||||
&uword ISTOP = $0328
|
&uword ISTOP = $0328
|
||||||
&uword IGETIN = $032a
|
&uword IGETIN = $032a
|
||||||
&uword ICLALL = $032c
|
&uword ICLALL = $032c
|
||||||
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
|
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
|
||||||
&uword ILOAD = $0330
|
&uword ILOAD = $0330
|
||||||
&uword ISAVE = $0332
|
&uword ISAVE = $0332
|
||||||
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||||
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
|
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
|
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
|
||||||
|
|
||||||
|
|
||||||
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
|
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
|
||||||
@ -233,14 +241,18 @@ cx16 {
|
|||||||
&ubyte VERA_IEN = VERA_BASE + $0006
|
&ubyte VERA_IEN = VERA_BASE + $0006
|
||||||
&ubyte VERA_ISR = VERA_BASE + $0007
|
&ubyte VERA_ISR = VERA_BASE + $0007
|
||||||
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
||||||
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
|
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009 ; DCSEL= 0
|
||||||
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
|
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A ; DCSEL= 0
|
||||||
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
|
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B ; DCSEL= 0
|
||||||
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
|
&ubyte VERA_DC_BORDER = VERA_BASE + $000C ; DCSEL= 0
|
||||||
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
|
&ubyte VERA_DC_HSTART = VERA_BASE + $0009 ; DCSEL= 1
|
||||||
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
|
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A ; DCSEL= 1
|
||||||
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
|
&ubyte VERA_DC_VSTART = VERA_BASE + $000B ; DCSEL= 1
|
||||||
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
|
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C ; DCSEL= 1
|
||||||
|
&ubyte VERA_DC_VER0 = VERA_BASE + $0009 ; DCSEL=63
|
||||||
|
&ubyte VERA_DC_VER1 = VERA_BASE + $000A ; DCSEL=63
|
||||||
|
&ubyte VERA_DC_VER2 = VERA_BASE + $000B ; DCSEL=63
|
||||||
|
&ubyte VERA_DC_VER3 = VERA_BASE + $000C ; DCSEL=63
|
||||||
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
||||||
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
||||||
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
||||||
@ -266,45 +278,45 @@ cx16 {
|
|||||||
|
|
||||||
; I/O
|
; I/O
|
||||||
|
|
||||||
const uword via1 = $9f00 ;VIA 6522 #1
|
const uword VIA1_BASE = $9f00 ;VIA 6522 #1
|
||||||
&ubyte d1prb = via1+0
|
&ubyte via1prb = VIA1_BASE + 0
|
||||||
&ubyte d1pra = via1+1
|
&ubyte via1pra = VIA1_BASE + 1
|
||||||
&ubyte d1ddrb = via1+2
|
&ubyte via1ddrb = VIA1_BASE + 2
|
||||||
&ubyte d1ddra = via1+3
|
&ubyte via1ddra = VIA1_BASE + 3
|
||||||
&ubyte d1t1l = via1+4
|
&ubyte via1t1l = VIA1_BASE + 4
|
||||||
&ubyte d1t1h = via1+5
|
&ubyte via1t1h = VIA1_BASE + 5
|
||||||
&ubyte d1t1ll = via1+6
|
&ubyte via1t1ll = VIA1_BASE + 6
|
||||||
&ubyte d1t1lh = via1+7
|
&ubyte via1t1lh = VIA1_BASE + 7
|
||||||
&ubyte d1t2l = via1+8
|
&ubyte via1t2l = VIA1_BASE + 8
|
||||||
&ubyte d1t2h = via1+9
|
&ubyte via1t2h = VIA1_BASE + 9
|
||||||
&ubyte d1sr = via1+10
|
&ubyte via1sr = VIA1_BASE + 10
|
||||||
&ubyte d1acr = via1+11
|
&ubyte via1acr = VIA1_BASE + 11
|
||||||
&ubyte d1pcr = via1+12
|
&ubyte via1pcr = VIA1_BASE + 12
|
||||||
&ubyte d1ifr = via1+13
|
&ubyte via1ifr = VIA1_BASE + 13
|
||||||
&ubyte d1ier = via1+14
|
&ubyte via1ier = VIA1_BASE + 14
|
||||||
&ubyte d1ora = via1+15
|
&ubyte via1ora = VIA1_BASE + 15
|
||||||
|
|
||||||
const uword via2 = $9f10 ;VIA 6522 #2
|
const uword VIA2_BASE = $9f10 ;VIA 6522 #2
|
||||||
&ubyte d2prb = via2+0
|
&ubyte via2prb = VIA2_BASE + 0
|
||||||
&ubyte d2pra = via2+1
|
&ubyte via2pra = VIA2_BASE + 1
|
||||||
&ubyte d2ddrb = via2+2
|
&ubyte via2ddrb = VIA2_BASE + 2
|
||||||
&ubyte d2ddra = via2+3
|
&ubyte via2ddra = VIA2_BASE + 3
|
||||||
&ubyte d2t1l = via2+4
|
&ubyte via2t1l = VIA2_BASE + 4
|
||||||
&ubyte d2t1h = via2+5
|
&ubyte via2t1h = VIA2_BASE + 5
|
||||||
&ubyte d2t1ll = via2+6
|
&ubyte via2t1ll = VIA2_BASE + 6
|
||||||
&ubyte d2t1lh = via2+7
|
&ubyte via2t1lh = VIA2_BASE + 7
|
||||||
&ubyte d2t2l = via2+8
|
&ubyte via2t2l = VIA2_BASE + 8
|
||||||
&ubyte d2t2h = via2+9
|
&ubyte via2t2h = VIA2_BASE + 9
|
||||||
&ubyte d2sr = via2+10
|
&ubyte via2sr = VIA2_BASE + 10
|
||||||
&ubyte d2acr = via2+11
|
&ubyte via2acr = VIA2_BASE + 11
|
||||||
&ubyte d2pcr = via2+12
|
&ubyte via2pcr = VIA2_BASE + 12
|
||||||
&ubyte d2ifr = via2+13
|
&ubyte via2ifr = VIA2_BASE + 13
|
||||||
&ubyte d2ier = via2+14
|
&ubyte via2ier = VIA2_BASE + 14
|
||||||
&ubyte d2ora = via2+15
|
&ubyte via2ora = VIA2_BASE + 15
|
||||||
|
|
||||||
; YM-2151 sound chip
|
; YM-2151 sound chip
|
||||||
&ubyte YM_ADDRESS = $9f40
|
&ubyte YM_ADDRESS = $9f40
|
||||||
&ubyte YM_DATA = $9f41
|
&ubyte YM_DATA = $9f41
|
||||||
|
|
||||||
const uword extdev = $9f60
|
const uword extdev = $9f60
|
||||||
|
|
||||||
@ -316,7 +328,7 @@ cx16 {
|
|||||||
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
|
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
|
||||||
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
|
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
|
||||||
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
|
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
|
||||||
romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc
|
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()
|
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
|
||||||
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
|
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
|
||||||
romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
|
romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
|
||||||
@ -333,19 +345,19 @@ romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
|
|||||||
romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
||||||
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
|
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
|
||||||
romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y)
|
romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y)
|
||||||
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, ubyte fill @Pc) clobbers(A,X,Y)
|
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, bool fill @Pc) clobbers(A,X,Y)
|
||||||
romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y)
|
romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y)
|
||||||
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, ubyte fill @Pc) clobbers(A,X,Y)
|
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, bool fill @Pc) clobbers(A,X,Y)
|
||||||
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
|
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
|
||||||
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
|
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
|
||||||
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y)
|
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, bool is_control @Pc) clobbers(A,X,Y)
|
||||||
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
|
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
|
||||||
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
|
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
|
||||||
|
|
||||||
; framebuffer
|
; framebuffer
|
||||||
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
||||||
romsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A, uword @R0, uword @R1 ; width=r0, height=r1
|
romsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A, uword @R0, uword @R1 ; width=r0, height=r1
|
||||||
romsub $fefc = FB_set_palette(uword pointer @R0, ubyte index @A, ubyte bytecount @X) clobbers(A,X,Y)
|
romsub $fefc = FB_set_palette(uword pointer @R0, ubyte index @A, ubyte colorcount @X) clobbers(A,X,Y)
|
||||||
romsub $feff = FB_cursor_position(uword x @R0, uword y @R1) clobbers(A,X,Y)
|
romsub $feff = FB_cursor_position(uword x @R0, uword y @R1) clobbers(A,X,Y)
|
||||||
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the previous routine, but avoiding having to respecify both x and y every time
|
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the previous routine, but avoiding having to respecify both x and y every time
|
||||||
romsub $ff02 = FB_cursor_next_line(uword x @R0) clobbers(A,X,Y)
|
romsub $ff02 = FB_cursor_next_line(uword x @R0) clobbers(A,X,Y)
|
||||||
@ -360,16 +372,17 @@ romsub $ff1a = FB_filter_pixels(uword pointer @ R0, uword count @R1) clobbers(A
|
|||||||
romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y)
|
romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y)
|
||||||
|
|
||||||
; misc
|
; misc
|
||||||
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, ubyte @Pc
|
romsub $FEBA = BSAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; like cbm.SAVE, but omits the 2-byte prg header
|
||||||
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> ubyte @Pc
|
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc
|
||||||
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc
|
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> bool @Pc
|
||||||
|
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, bool apply_mask @Pc) clobbers(A,X,Y) -> bool @Pc
|
||||||
romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y)
|
romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y)
|
||||||
romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y)
|
romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y)
|
||||||
romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y)
|
romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y)
|
||||||
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
|
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
|
||||||
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
|
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
|
||||||
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
|
||||||
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y)
|
romsub $fede = console_put_char(ubyte char @A, bool wrapping @Pc) clobbers(A,X,Y)
|
||||||
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
|
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
|
||||||
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
|
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
|
||||||
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
|
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
|
||||||
@ -377,7 +390,7 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
|||||||
romsub $fecc = monitor() clobbers(A,X,Y)
|
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||||
|
|
||||||
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
|
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
|
||||||
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
romsub $ff47 = enter_basic(bool cold_or_warm @Pc) clobbers(A,X,Y)
|
||||||
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
|
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
|
||||||
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
|
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
|
||||||
|
|
||||||
@ -394,10 +407,37 @@ romsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
|||||||
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||||
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
|
||||||
|
|
||||||
|
; Audio (rom bank 10)
|
||||||
|
romsub $C04B = psg_init() clobbers(A,X,Y)
|
||||||
|
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
|
||||||
|
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
|
||||||
|
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize PSG and YM audio chips
|
||||||
|
; TODO: add more of the audio routines?
|
||||||
|
|
||||||
|
|
||||||
|
asmsub set_screen_mode(ubyte mode @A) clobbers(A,X,Y) -> bool @Pc {
|
||||||
|
; -- convenience wrapper for screen_mode() to just set a new mode (and return success)
|
||||||
|
%asm {{
|
||||||
|
clc
|
||||||
|
jmp screen_mode
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub get_screen_mode() -> byte @A, byte @X, byte @Y {
|
||||||
|
; -- convenience wrapper for screen_mode() to just get the current mode in A, and size in characters in X+Y
|
||||||
|
; this does need a piece of inlined asm to call it ans store the result values if you call this from prog8 code
|
||||||
|
; Note: you can also just do the SEC yourself and simply call screen_mode() directly,
|
||||||
|
; or use the existing SCREEN kernal routine for just getting the size in characters.
|
||||||
|
%asm {{
|
||||||
|
sec
|
||||||
|
jmp screen_mode
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub kbdbuf_clear() {
|
asmsub kbdbuf_clear() {
|
||||||
; -- convenience helper routine to clear the keyboard buffer
|
; -- convenience helper routine to clear the keyboard buffer
|
||||||
%asm {{
|
%asm {{
|
||||||
- jsr c64.GETIN
|
- jsr cbm.GETIN
|
||||||
bne -
|
bne -
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -470,7 +510,7 @@ asmsub numbanks() -> uword @AY {
|
|||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sec
|
sec
|
||||||
jsr c64.MEMTOP
|
jsr cbm.MEMTOP
|
||||||
ldy #0
|
ldy #0
|
||||||
cmp #0
|
cmp #0
|
||||||
bne +
|
bne +
|
||||||
@ -592,42 +632,111 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @Y) clobbers (A) {
|
||||||
sub FB_set_pixels_from_buf(uword buffer, uword count) {
|
; -- bitwise or a single byte to the value already in the VERA's video memory at that location
|
||||||
|
; after applying the and-mask. Note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
sty P8ZP_SCRATCH_B1
|
||||||
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
|
stz cx16.VERA_CTRL
|
||||||
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
|
and #1
|
||||||
; See https://github.com/commanderx16/x16-rom/issues/179
|
sta cx16.VERA_ADDR_H
|
||||||
phx
|
lda cx16.r0
|
||||||
lda buffer
|
sta cx16.VERA_ADDR_L
|
||||||
ldy buffer+1
|
lda cx16.r0+1
|
||||||
sta P8ZP_SCRATCH_W1
|
sta cx16.VERA_ADDR_M
|
||||||
sty P8ZP_SCRATCH_W1+1
|
txa
|
||||||
jsr _pixels
|
and cx16.VERA_DATA0
|
||||||
plx
|
ora P8ZP_SCRATCH_B1
|
||||||
rts
|
sta cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
_pixels lda count+1
|
}}
|
||||||
beq +
|
|
||||||
ldx #0
|
|
||||||
- jsr _loop
|
|
||||||
inc P8ZP_SCRATCH_W1+1
|
|
||||||
dec count+1
|
|
||||||
bne -
|
|
||||||
|
|
||||||
+ ldx count
|
|
||||||
_loop ldy #0
|
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
iny
|
|
||||||
dex
|
|
||||||
bne -
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
; ---- system stuff -----
|
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
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asmsub save_vera_context() clobbers(A) {
|
||||||
|
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||||
|
%asm {{
|
||||||
|
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||||
|
lda cx16.VERA_ADDR_L
|
||||||
|
sta _vera_storage
|
||||||
|
lda cx16.VERA_ADDR_M
|
||||||
|
sta _vera_storage+1
|
||||||
|
lda cx16.VERA_ADDR_H
|
||||||
|
sta _vera_storage+2
|
||||||
|
lda cx16.VERA_CTRL
|
||||||
|
sta _vera_storage+3
|
||||||
|
eor #1
|
||||||
|
sta _vera_storage+7
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
lda cx16.VERA_ADDR_L
|
||||||
|
sta _vera_storage+4
|
||||||
|
lda cx16.VERA_ADDR_M
|
||||||
|
sta _vera_storage+5
|
||||||
|
lda cx16.VERA_ADDR_H
|
||||||
|
sta _vera_storage+6
|
||||||
|
rts
|
||||||
|
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub restore_vera_context() clobbers(A) {
|
||||||
|
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
||||||
|
%asm {{
|
||||||
|
lda cx16.save_vera_context._vera_storage+7
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
lda cx16.save_vera_context._vera_storage+6
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda cx16.save_vera_context._vera_storage+5
|
||||||
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda cx16.save_vera_context._vera_storage+4
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
lda cx16.save_vera_context._vera_storage+3
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
lda cx16.save_vera_context._vera_storage+2
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda cx16.save_vera_context._vera_storage+1
|
||||||
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda cx16.save_vera_context._vera_storage+0
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sys {
|
||||||
|
; ------- lowlevel system routines --------
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
|
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
||||||
|
|
||||||
asmsub init_system() {
|
asmsub init_system() {
|
||||||
; Initializes the machine to a sane starting state.
|
; Initializes the machine to a sane starting state.
|
||||||
; Called automatically by the loader program logic.
|
; Called automatically by the loader program logic.
|
||||||
@ -638,28 +747,30 @@ asmsub init_system() {
|
|||||||
tay
|
tay
|
||||||
jsr cx16.mouse_config ; disable mouse
|
jsr cx16.mouse_config ; disable mouse
|
||||||
cld
|
cld
|
||||||
lda VERA_DC_VIDEO
|
lda cx16.VERA_DC_VIDEO
|
||||||
and #%00000111 ; retain chroma + output mode
|
and #%00000111 ; retain chroma + output mode
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
lda #$80
|
lda #$0a
|
||||||
sta VERA_CTRL ; reset vera
|
sta $01 ; rom bank 10 (audio)
|
||||||
|
jsr cx16.audio_init ; silence
|
||||||
stz $01 ; rom bank 0 (kernal)
|
stz $01 ; rom bank 0 (kernal)
|
||||||
jsr c64.IOINIT
|
jsr cbm.IOINIT
|
||||||
jsr c64.RESTOR
|
jsr cbm.RESTOR
|
||||||
jsr c64.CINT
|
jsr cbm.CINT
|
||||||
lda VERA_DC_VIDEO
|
lda cx16.VERA_DC_VIDEO
|
||||||
and #%11111000
|
and #%11111000
|
||||||
ora P8ZP_SCRATCH_REG
|
ora P8ZP_SCRATCH_REG
|
||||||
sta VERA_DC_VIDEO ; keep old output mode
|
sta cx16.VERA_DC_VIDEO ; restore old output mode
|
||||||
lda #$90 ; black
|
lda #$90 ; black
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
lda #1
|
lda #1
|
||||||
sta $00 ; select ram bank 1
|
jsr cbm.CHROUT ; swap fg/bg
|
||||||
jsr c64.CHROUT ; swap fg/bg
|
|
||||||
lda #$9e ; yellow
|
lda #$9e ; yellow
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
lda #147 ; clear screen
|
lda #147 ; clear screen
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
|
lda #PROG8_VARSHIGH_RAMBANK
|
||||||
|
sta $00 ; select ram bank
|
||||||
lda #0
|
lda #0
|
||||||
tax
|
tax
|
||||||
tay
|
tay
|
||||||
@ -677,6 +788,8 @@ asmsub init_system_phase2() {
|
|||||||
sta restore_irq._orig_irqvec
|
sta restore_irq._orig_irqvec
|
||||||
lda cx16.CINV+1
|
lda cx16.CINV+1
|
||||||
sta restore_irq._orig_irqvec+1
|
sta restore_irq._orig_irqvec+1
|
||||||
|
lda #PROG8_VARSHIGH_RAMBANK
|
||||||
|
sta $00 ; select ram bank
|
||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -694,12 +807,12 @@ asmsub cleanup_at_exit() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta _modified+1
|
sta _modified+1
|
||||||
sty _modified+2
|
sty _modified+2
|
||||||
lda #0
|
lda #0
|
||||||
adc #0
|
rol a
|
||||||
sta _use_kernal
|
sta _use_kernal
|
||||||
sei
|
sei
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
@ -729,8 +842,7 @@ _modified jsr $ffff ; modified
|
|||||||
_use_kernal .byte 0
|
_use_kernal .byte 0
|
||||||
|
|
||||||
_irq_handler_init
|
_irq_handler_init
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
; save all zp scratch registers as these might be clobbered by the irq routine
|
||||||
stx IRQ_X_REG
|
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
sta IRQ_SCRATCH_ZPB1
|
sta IRQ_SCRATCH_ZPB1
|
||||||
lda P8ZP_SCRATCH_REG
|
lda P8ZP_SCRATCH_REG
|
||||||
@ -743,18 +855,15 @@ _irq_handler_init
|
|||||||
sta IRQ_SCRATCH_ZPWORD2
|
sta IRQ_SCRATCH_ZPWORD2
|
||||||
lda P8ZP_SCRATCH_W2+1
|
lda P8ZP_SCRATCH_W2+1
|
||||||
sta IRQ_SCRATCH_ZPWORD2+1
|
sta IRQ_SCRATCH_ZPWORD2+1
|
||||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
|
||||||
dex
|
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
|
||||||
dex
|
; We assume IRQ handlers don't contain complex expressions taking up more than that.
|
||||||
dex
|
ldx #32
|
||||||
dex
|
|
||||||
dex
|
|
||||||
dex
|
|
||||||
cld
|
cld
|
||||||
rts
|
rts
|
||||||
|
|
||||||
_irq_handler_end
|
_irq_handler_end
|
||||||
; restore all zp scratch registers and the X register
|
; restore all zp scratch registers
|
||||||
lda IRQ_SCRATCH_ZPB1
|
lda IRQ_SCRATCH_ZPB1
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda IRQ_SCRATCH_ZPREG
|
lda IRQ_SCRATCH_ZPREG
|
||||||
@ -767,10 +876,8 @@ _irq_handler_end
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda IRQ_SCRATCH_ZPWORD2+1
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
ldx IRQ_X_REG
|
|
||||||
rts
|
rts
|
||||||
|
|
||||||
IRQ_X_REG .byte 0
|
|
||||||
IRQ_SCRATCH_ZPB1 .byte 0
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
IRQ_SCRATCH_ZPREG .byte 0
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
@ -779,57 +886,6 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub push_vera_context() clobbers(A) {
|
|
||||||
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
|
||||||
%asm {{
|
|
||||||
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
|
||||||
lda cx16.VERA_ADDR_L
|
|
||||||
sta _vera_storage
|
|
||||||
lda cx16.VERA_ADDR_M
|
|
||||||
sta _vera_storage+1
|
|
||||||
lda cx16.VERA_ADDR_H
|
|
||||||
sta _vera_storage+2
|
|
||||||
lda cx16.VERA_CTRL
|
|
||||||
sta _vera_storage+3
|
|
||||||
eor #1
|
|
||||||
sta cx16.VERA_CTRL
|
|
||||||
lda cx16.VERA_ADDR_L
|
|
||||||
sta _vera_storage+4
|
|
||||||
lda cx16.VERA_ADDR_M
|
|
||||||
sta _vera_storage+5
|
|
||||||
lda cx16.VERA_ADDR_H
|
|
||||||
sta _vera_storage+6
|
|
||||||
lda cx16.VERA_CTRL
|
|
||||||
sta _vera_storage+7
|
|
||||||
rts
|
|
||||||
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub pop_vera_context() clobbers(A) {
|
|
||||||
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
|
||||||
%asm {{
|
|
||||||
lda cx16.push_vera_context._vera_storage+7
|
|
||||||
sta cx16.VERA_CTRL
|
|
||||||
lda cx16.push_vera_context._vera_storage+6
|
|
||||||
sta cx16.VERA_ADDR_H
|
|
||||||
lda cx16.push_vera_context._vera_storage+5
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
lda cx16.push_vera_context._vera_storage+4
|
|
||||||
sta cx16.VERA_ADDR_L
|
|
||||||
lda cx16.push_vera_context._vera_storage+3
|
|
||||||
sta cx16.VERA_CTRL
|
|
||||||
lda cx16.push_vera_context._vera_storage+2
|
|
||||||
sta cx16.VERA_ADDR_H
|
|
||||||
lda cx16.push_vera_context._vera_storage+1
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
lda cx16.push_vera_context._vera_storage+0
|
|
||||||
sta cx16.VERA_ADDR_L
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
asmsub restore_irq() clobbers(A) {
|
asmsub restore_irq() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
@ -899,29 +955,33 @@ asmsub set_rasterline(uword line @AY) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sys {
|
|
||||||
; ------- lowlevel system routines --------
|
|
||||||
|
|
||||||
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
|
|
||||||
|
|
||||||
|
|
||||||
asmsub reset_system() {
|
asmsub reset_system() {
|
||||||
; Soft-reset the system back to initial power-on Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
|
; We do this via the SMC so that a true reset is performed that also resets the Vera fully.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
stz $01 ; bank the kernal in
|
ldx #$42
|
||||||
lda #$80
|
ldy #1
|
||||||
sta cx16.VERA_CTRL ; reset Vera (kernal doesn't do this?)
|
tya
|
||||||
jmp (cx16.RESET_VEC)
|
jsr cx16.i2c_write_byte
|
||||||
|
bra *
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub poweroff_system() {
|
||||||
|
; use the SMC to shutdown the computer
|
||||||
|
void cx16.i2c_write_byte($42, $01, $00)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub set_leds_brightness(ubyte activity, ubyte power) {
|
||||||
|
void cx16.i2c_write_byte($42, $04, power)
|
||||||
|
void cx16.i2c_write_byte($42, $05, activity)
|
||||||
|
}
|
||||||
|
|
||||||
asmsub wait(uword jiffies @AY) {
|
asmsub wait(uword jiffies @AY) {
|
||||||
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
; --- 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: 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 {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
@ -933,9 +993,13 @@ _loop lda P8ZP_SCRATCH_W1
|
|||||||
plx
|
plx
|
||||||
rts
|
rts
|
||||||
|
|
||||||
+ jsr c64.RDTIM
|
+ sei
|
||||||
|
jsr cbm.RDTIM
|
||||||
|
cli
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- jsr c64.RDTIM
|
- sei
|
||||||
|
jsr cbm.RDTIM
|
||||||
|
cli
|
||||||
cmp P8ZP_SCRATCH_B1
|
cmp P8ZP_SCRATCH_B1
|
||||||
beq -
|
beq -
|
||||||
|
|
||||||
@ -1075,10 +1139,23 @@ _longcopy
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_set_irqd() {
|
||||||
|
%asm {{
|
||||||
|
php
|
||||||
|
sei
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub irqsafe_clear_irqd() {
|
||||||
|
%asm {{
|
||||||
|
plp
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
inline asmsub exit(ubyte returnvalue @A) {
|
inline asmsub exit(ubyte returnvalue @A) {
|
||||||
; -- immediately exit the program with a return code in the A register
|
; -- immediately exit the program with a return code in the A register
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.CLRCHN ; reset i/o channels
|
jsr cbm.CLRCHN ; reset i/o channels
|
||||||
ldx prog8_lib.orig_stackpointer
|
ldx prog8_lib.orig_stackpointer
|
||||||
txs
|
txs
|
||||||
rts ; return to original caller
|
rts ; return to original caller
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
txt {
|
txt {
|
||||||
|
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
const ubyte DEFAULT_WIDTH = 80
|
const ubyte DEFAULT_WIDTH = 80
|
||||||
const ubyte DEFAULT_HEIGHT = 60
|
const ubyte DEFAULT_HEIGHT = 60
|
||||||
|
|
||||||
@ -33,10 +35,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
|
|||||||
; ---- set the cursor on the given column (starting with 0) on the current line
|
; ---- set the cursor on the given column (starting with 0) on the current line
|
||||||
%asm {{
|
%asm {{
|
||||||
sec
|
sec
|
||||||
jsr c64.PLOT
|
jsr cbm.PLOT
|
||||||
tay
|
tay
|
||||||
clc
|
clc
|
||||||
jmp c64.PLOT
|
jmp cbm.PLOT
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +48,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
|||||||
sty _ly+1
|
sty _ly+1
|
||||||
phx
|
phx
|
||||||
pha
|
pha
|
||||||
jsr c64.SCREEN ; get dimensions in X/Y
|
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||||
txa
|
txa
|
||||||
lsr a
|
lsr a
|
||||||
lsr a
|
lsr a
|
||||||
@ -94,7 +96,7 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
pha
|
pha
|
||||||
jsr c64.SCREEN ; get dimensions in X/Y
|
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||||
txa
|
txa
|
||||||
lsr a
|
lsr a
|
||||||
lsr a
|
lsr a
|
||||||
@ -125,7 +127,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sta _la+1
|
sta _la+1
|
||||||
jsr c64.SCREEN ; get dimensions in X/Y
|
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||||
txa
|
txa
|
||||||
lsr a
|
lsr a
|
||||||
lsr a
|
lsr a
|
||||||
@ -158,35 +160,35 @@ ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$
|
|||||||
|
|
||||||
sub color (ubyte txtcol) {
|
sub color (ubyte txtcol) {
|
||||||
txtcol &= 15
|
txtcol &= 15
|
||||||
c64.CHROUT(color_to_charcode[txtcol])
|
cbm.CHROUT(color_to_charcode[txtcol])
|
||||||
}
|
}
|
||||||
|
|
||||||
sub color2 (ubyte txtcol, ubyte bgcol) {
|
sub color2 (ubyte txtcol, ubyte bgcol) {
|
||||||
txtcol &= 15
|
txtcol &= 15
|
||||||
bgcol &= 15
|
bgcol &= 15
|
||||||
c64.CHROUT(color_to_charcode[bgcol])
|
cbm.CHROUT(color_to_charcode[bgcol])
|
||||||
c64.CHROUT(1) ; switch fg and bg colors
|
cbm.CHROUT(1) ; switch fg and bg colors
|
||||||
c64.CHROUT(color_to_charcode[txtcol])
|
cbm.CHROUT(color_to_charcode[txtcol])
|
||||||
}
|
}
|
||||||
|
|
||||||
sub lowercase() {
|
sub lowercase() {
|
||||||
c64.CHROUT($0e)
|
cbm.CHROUT($0e)
|
||||||
; this is not 100% compatible: cx16.screen_set_charset(3, 0) ; lowercase petscii charset
|
; this is not 100% compatible: cx16.screen_set_charset(3, 0) ; lowercase petscii charset
|
||||||
}
|
}
|
||||||
|
|
||||||
sub uppercase() {
|
sub uppercase() {
|
||||||
c64.CHROUT($8e)
|
cbm.CHROUT($8e)
|
||||||
; this is not 100% compatible: cx16.screen_set_charset(2, 0) ; uppercase petscii charset
|
; this is not 100% compatible: cx16.screen_set_charset(2, 0) ; uppercase petscii charset
|
||||||
}
|
}
|
||||||
|
|
||||||
sub iso() {
|
sub iso() {
|
||||||
c64.CHROUT($0f)
|
cbm.CHROUT($0f)
|
||||||
; This doesn't enable it completely: cx16.screen_set_charset(1, 0) ; iso charset
|
; This doesn't enable it completely: cx16.screen_set_charset(1, 0) ; iso charset
|
||||||
}
|
}
|
||||||
|
|
||||||
sub iso_off() {
|
sub iso_off() {
|
||||||
; -- you have to call this first when switching back from iso charset to regular charset.
|
; -- you have to call this first when switching back from iso charset to regular charset.
|
||||||
c64.CHROUT($8f)
|
cbm.CHROUT($8f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -195,7 +197,7 @@ asmsub scroll_left() clobbers(A, Y) {
|
|||||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
dex
|
dex
|
||||||
stx _lx+1
|
stx _lx+1
|
||||||
dey
|
dey
|
||||||
@ -241,7 +243,7 @@ asmsub scroll_right() clobbers(A) {
|
|||||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
dex
|
dex
|
||||||
stx _lx+1
|
stx _lx+1
|
||||||
txa
|
txa
|
||||||
@ -295,7 +297,7 @@ asmsub scroll_up() clobbers(A, Y) {
|
|||||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
stx _nextline+1
|
stx _nextline+1
|
||||||
dey
|
dey
|
||||||
sty P8ZP_SCRATCH_B1
|
sty P8ZP_SCRATCH_B1
|
||||||
@ -345,7 +347,7 @@ asmsub scroll_down() clobbers(A, Y) {
|
|||||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
stx _nextline+1
|
stx _nextline+1
|
||||||
dey
|
dey
|
||||||
sty P8ZP_SCRATCH_B1
|
sty P8ZP_SCRATCH_B1
|
||||||
@ -396,20 +398,20 @@ _nextline
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
|
||||||
|
|
||||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||||
; ---- print null terminated string from A/Y
|
; ---- print null terminated string from A/Y
|
||||||
; note: the compiler contains an optimization that will replace
|
; note: the compiler contains an optimization that will replace
|
||||||
; a call to this subroutine with a string argument of just one char,
|
; a call to this subroutine with a string argument of just one char,
|
||||||
; by just one call to c64.CHROUT of that single char.
|
; by just one call to cbm.CHROUT of that single char.
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
sty P8ZP_SCRATCH_REG
|
sty P8ZP_SCRATCH_REG
|
||||||
ldy #0
|
ldy #0
|
||||||
- lda (P8ZP_SCRATCH_B1),y
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
beq +
|
beq +
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
+ rts
|
+ rts
|
||||||
@ -423,11 +425,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
|||||||
jsr conv.ubyte2decimal
|
jsr conv.ubyte2decimal
|
||||||
pha
|
pha
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
txa
|
txa
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
plx
|
plx
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -443,16 +445,16 @@ _print_byte_digits
|
|||||||
cpy #'0'
|
cpy #'0'
|
||||||
beq +
|
beq +
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
bra _ones
|
bra _ones
|
||||||
+ pla
|
+ pla
|
||||||
cmp #'0'
|
cmp #'0'
|
||||||
beq _ones
|
beq _ones
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
_ones txa
|
_ones txa
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
plx
|
plx
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -466,45 +468,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
|||||||
cmp #0
|
cmp #0
|
||||||
bpl +
|
bpl +
|
||||||
lda #'-'
|
lda #'-'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
+ pla
|
+ pla
|
||||||
jsr conv.byte2decimal
|
jsr conv.byte2decimal
|
||||||
bra print_ub._print_byte_digits
|
bra print_ub._print_byte_digits
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
bcc +
|
bcc +
|
||||||
pha
|
pha
|
||||||
lda #'$'
|
lda #'$'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
pla
|
pla
|
||||||
+ jsr conv.ubyte2hex
|
+ jsr conv.ubyte2hex
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
plx
|
plx
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
bcc +
|
bcc +
|
||||||
lda #'%'
|
lda #'%'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
+ ldy #8
|
+ ldy #8
|
||||||
- lda #'0'
|
- lda #'0'
|
||||||
asl P8ZP_SCRATCH_B1
|
asl P8ZP_SCRATCH_B1
|
||||||
bcc +
|
bcc +
|
||||||
lda #'1'
|
lda #'1'
|
||||||
+ jsr c64.CHROUT
|
+ jsr cbm.CHROUT
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
plx
|
plx
|
||||||
@ -512,7 +514,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
@ -524,7 +526,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
|
||||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -545,7 +547,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
|||||||
ldy #0
|
ldy #0
|
||||||
- lda conv.uword2decimal.decTenThousands,y
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
beq +
|
beq +
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
bne -
|
bne -
|
||||||
+ plx
|
+ plx
|
||||||
@ -568,14 +570,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
|||||||
bne -
|
bne -
|
||||||
|
|
||||||
_gotdigit
|
_gotdigit
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
iny
|
iny
|
||||||
lda conv.uword2decimal.decTenThousands,y
|
lda conv.uword2decimal.decTenThousands,y
|
||||||
bne _gotdigit
|
bne _gotdigit
|
||||||
rts
|
rts
|
||||||
_allzero
|
_allzero
|
||||||
lda #'0'
|
lda #'0'
|
||||||
jmp c64.CHROUT
|
jmp cbm.CHROUT
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,7 +588,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
|||||||
bpl +
|
bpl +
|
||||||
pha
|
pha
|
||||||
lda #'-'
|
lda #'-'
|
||||||
jsr c64.CHROUT
|
jsr cbm.CHROUT
|
||||||
tya
|
tya
|
||||||
eor #255
|
eor #255
|
||||||
tay
|
tay
|
||||||
@ -607,7 +609,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #0 ; char counter = 0
|
ldy #0 ; char counter = 0
|
||||||
- jsr c64.CHRIN
|
- jsr cbm.CHRIN
|
||||||
cmp #$0d ; return (ascii 13) pressed?
|
cmp #$0d ; return (ascii 13) pressed?
|
||||||
beq + ; yes, end.
|
beq + ; yes, end.
|
||||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||||
@ -769,7 +771,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|||||||
phx
|
phx
|
||||||
tax
|
tax
|
||||||
clc
|
clc
|
||||||
jsr c64.PLOT
|
jsr cbm.PLOT
|
||||||
plx
|
plx
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -778,7 +780,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
|||||||
asmsub width() clobbers(X,Y) -> ubyte @A {
|
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||||
; -- returns the text screen width (number of columns)
|
; -- returns the text screen width (number of columns)
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
txa
|
txa
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -787,7 +789,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
|
|||||||
asmsub height() clobbers(X, Y) -> ubyte @A {
|
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||||
; -- returns the text screen height (number of rows)
|
; -- returns the text screen height (number of rows)
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.SCREEN
|
jsr cbm.SCREEN
|
||||||
tya
|
tya
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
cx16logo {
|
cx16logo {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
sub logo_at(ubyte column, ubyte row) {
|
sub logo_at(ubyte column, ubyte row) {
|
||||||
uword strptr
|
uword strptr
|
||||||
for strptr in logo_lines {
|
for strptr in logo_lines {
|
||||||
|
@ -1,59 +1,66 @@
|
|||||||
; C64 and Cx16 disk drive I/O routines.
|
; C64/C128 disk drive I/O routines.
|
||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
%import string
|
%import string
|
||||||
%import syslib
|
%import syslib
|
||||||
|
|
||||||
diskio {
|
diskio {
|
||||||
|
%option no_symbol_prefixing
|
||||||
|
|
||||||
sub directory(ubyte drivenumber) -> bool {
|
ubyte drivenumber = 8
|
||||||
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
|
|
||||||
|
|
||||||
c64.SETNAM(1, "$")
|
sub set_drive(ubyte number) {
|
||||||
c64.SETLFS(12, drivenumber, 0)
|
drivenumber = number
|
||||||
|
}
|
||||||
|
|
||||||
|
sub directory() -> bool {
|
||||||
|
; -- Prints the directory contents to the screen. Returns success.
|
||||||
|
|
||||||
|
cbm.SETNAM(1, "$")
|
||||||
|
cbm.SETLFS(12, drivenumber, 0)
|
||||||
ubyte status = 1
|
ubyte status = 1
|
||||||
void c64.OPEN() ; open 12,8,0,"$"
|
void cbm.OPEN() ; open 12,8,0,"$"
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
void c64.CHKIN(12) ; use #12 as input channel
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
repeat 4 {
|
repeat 4 {
|
||||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
; while not stop key pressed / EOF encountered, read data.
|
; while not stop key pressed / EOF encountered, read data.
|
||||||
status = c64.READST()
|
status = cbm.READST()
|
||||||
if status!=0 {
|
if status!=0 {
|
||||||
status = 1
|
status = 1
|
||||||
goto io_error
|
goto io_error
|
||||||
}
|
}
|
||||||
|
|
||||||
while status==0 {
|
while status==0 {
|
||||||
ubyte low = c64.CHRIN()
|
ubyte low = cbm.CHRIN()
|
||||||
ubyte high = c64.CHRIN()
|
ubyte high = cbm.CHRIN()
|
||||||
txt.print_uw(mkword(high, low))
|
txt.print_uw(mkword(high, low))
|
||||||
txt.spc()
|
txt.spc()
|
||||||
ubyte @zp char
|
ubyte @zp char
|
||||||
repeat {
|
repeat {
|
||||||
char = c64.CHRIN()
|
char = cbm.CHRIN()
|
||||||
if char==0
|
if char==0
|
||||||
break
|
break
|
||||||
txt.chrout(char)
|
txt.chrout(char)
|
||||||
}
|
}
|
||||||
txt.nl()
|
txt.nl()
|
||||||
void c64.CHRIN() ; skip 2 bytes
|
void cbm.CHRIN() ; skip 2 bytes
|
||||||
void c64.CHRIN()
|
void cbm.CHRIN()
|
||||||
status = c64.READST()
|
status = cbm.READST()
|
||||||
if c64.STOP2()
|
if cbm.STOP2()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
status = c64.READST()
|
status = cbm.READST()
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
cbm.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(12)
|
cbm.CLOSE(12)
|
||||||
|
|
||||||
if status and status & $40 == 0 { ; bit 6=end of file
|
if status and status & $40 == 0 { ; bit 6=end of file
|
||||||
txt.print("\ni/o error, status: ")
|
txt.print("\ni/o error, status: ")
|
||||||
@ -65,37 +72,43 @@ io_error:
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
sub diskname(ubyte drivenumber) -> uword {
|
sub diskname() -> uword {
|
||||||
; -- Returns pointer to disk name string or 0 if failure.
|
; -- Returns pointer to disk name string or 0 if failure.
|
||||||
|
|
||||||
c64.SETNAM(1, "$")
|
cbm.SETNAM(1, "$")
|
||||||
c64.SETLFS(12, drivenumber, 0)
|
cbm.SETLFS(12, drivenumber, 0)
|
||||||
ubyte okay = false
|
ubyte okay = false
|
||||||
void c64.OPEN() ; open 12,8,0,"$"
|
void cbm.OPEN() ; open 12,8,0,"$"
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
void c64.CHKIN(12) ; use #12 as input channel
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
repeat 6 {
|
while cbm.CHRIN()!='"' {
|
||||||
void c64.CHRIN() ; skip the 6 prologue bytes
|
; skip up to entry name
|
||||||
}
|
}
|
||||||
if c64.READST()!=0
|
if cbm.READST()!=0
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
cx16.r0 = &list_filename
|
cx16.r0 = &list_filename
|
||||||
repeat {
|
repeat {
|
||||||
@(cx16.r0) = c64.CHRIN()
|
@(cx16.r0) = cbm.CHRIN()
|
||||||
if @(cx16.r0)==0
|
if @(cx16.r0)=='"' {
|
||||||
|
@(cx16.r0) = ' '
|
||||||
|
while @(cx16.r0)==' ' and cx16.r0>=&diskio.list_filename {
|
||||||
|
@(cx16.r0) = 0
|
||||||
|
cx16.r0--
|
||||||
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
cx16.r0++
|
cx16.r0++
|
||||||
}
|
}
|
||||||
okay = true
|
okay = true
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
cbm.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(12)
|
cbm.CLOSE(12)
|
||||||
if okay
|
if okay
|
||||||
return &list_filename
|
return &list_filename
|
||||||
return 0
|
return 0
|
||||||
@ -106,13 +119,12 @@ io_error:
|
|||||||
uword list_pattern
|
uword list_pattern
|
||||||
uword list_blocks
|
uword list_blocks
|
||||||
bool iteration_in_progress = false
|
bool iteration_in_progress = false
|
||||||
ubyte last_drivenumber = 8 ; which drive was last used for a f_open operation?
|
|
||||||
str list_filetype = "???" ; prg, seq, dir
|
str list_filetype = "???" ; prg, seq, dir
|
||||||
str list_filename = "?" * 50
|
str list_filename = "?" * 50
|
||||||
|
|
||||||
; ----- get a list of files (uses iteration functions internally) -----
|
; ----- get a list of files (uses iteration functions internally) -----
|
||||||
|
|
||||||
sub list_filenames(ubyte drivenumber, uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||||
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
|
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
|
||||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||||
@ -120,10 +132,10 @@ io_error:
|
|||||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||||
uword buffer_start = filenames_buffer
|
uword buffer_start = filenames_buffer
|
||||||
ubyte files_found = 0
|
ubyte files_found = 0
|
||||||
if lf_start_list(drivenumber, pattern_ptr) {
|
if lf_start_list(pattern_ptr) {
|
||||||
while lf_next_entry() {
|
while lf_next_entry() {
|
||||||
if list_filetype!="dir" {
|
if list_filetype!="dir" {
|
||||||
filenames_buffer += string.copy(diskio.list_filename, filenames_buffer) + 1
|
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
|
||||||
files_found++
|
files_found++
|
||||||
if filenames_buffer - buffer_start > filenames_buf_size-20 {
|
if filenames_buffer - buffer_start > filenames_buf_size-20 {
|
||||||
@(filenames_buffer)=0
|
@(filenames_buffer)=0
|
||||||
@ -142,7 +154,7 @@ io_error:
|
|||||||
|
|
||||||
; ----- iterative file lister functions (uses io channel 12) -----
|
; ----- iterative file lister functions (uses io channel 12) -----
|
||||||
|
|
||||||
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> bool {
|
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||||
; -- start an iterative file listing with optional pattern matching.
|
; -- start an iterative file listing with optional pattern matching.
|
||||||
; note: only a single iteration loop can be active at a time!
|
; note: only a single iteration loop can be active at a time!
|
||||||
lf_end_list()
|
lf_end_list()
|
||||||
@ -150,20 +162,20 @@ io_error:
|
|||||||
list_skip_disk_name = true
|
list_skip_disk_name = true
|
||||||
iteration_in_progress = true
|
iteration_in_progress = true
|
||||||
|
|
||||||
c64.SETNAM(1, "$")
|
cbm.SETNAM(1, "$")
|
||||||
c64.SETLFS(12, drivenumber, 0)
|
cbm.SETLFS(12, drivenumber, 0)
|
||||||
void c64.OPEN() ; open 12,8,0,"$"
|
void cbm.OPEN() ; open 12,8,0,"$"
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
void c64.CHKIN(12) ; use #12 as input channel
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
repeat 4 {
|
repeat 4 {
|
||||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
void cbm.CHRIN() ; skip the 4 prologue bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
if c64.READST()==0
|
if cbm.READST()==0
|
||||||
return true
|
return true
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
@ -180,26 +192,26 @@ io_error:
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
repeat {
|
repeat {
|
||||||
void c64.CHKIN(12) ; use #12 as input channel again
|
void cbm.CHKIN(12) ; use #12 as input channel again
|
||||||
|
|
||||||
uword nameptr = &list_filename
|
uword nameptr = &list_filename
|
||||||
ubyte blocks_lsb = c64.CHRIN()
|
ubyte blocks_lsb = cbm.CHRIN()
|
||||||
ubyte blocks_msb = c64.CHRIN()
|
ubyte blocks_msb = cbm.CHRIN()
|
||||||
|
|
||||||
if c64.READST()
|
if cbm.READST()
|
||||||
goto close_end
|
goto close_end
|
||||||
|
|
||||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||||
|
|
||||||
; read until the filename starts after the first "
|
; read until the filename starts after the first "
|
||||||
while c64.CHRIN()!='\"' {
|
while cbm.CHRIN()!='\"' {
|
||||||
if c64.READST()
|
if cbm.READST()
|
||||||
goto close_end
|
goto close_end
|
||||||
}
|
}
|
||||||
|
|
||||||
; read the filename
|
; read the filename
|
||||||
repeat {
|
repeat {
|
||||||
ubyte char = c64.CHRIN()
|
ubyte char = cbm.CHRIN()
|
||||||
if char==0
|
if char==0
|
||||||
break
|
break
|
||||||
if char=='\"'
|
if char=='\"'
|
||||||
@ -211,17 +223,17 @@ io_error:
|
|||||||
@(nameptr) = 0
|
@(nameptr) = 0
|
||||||
|
|
||||||
do {
|
do {
|
||||||
cx16.r15L = c64.CHRIN()
|
cx16.r15L = cbm.CHRIN()
|
||||||
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
|
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
|
||||||
list_filetype[0] = cx16.r15L
|
list_filetype[0] = cx16.r15L
|
||||||
list_filetype[1] = c64.CHRIN()
|
list_filetype[1] = cbm.CHRIN()
|
||||||
list_filetype[2] = c64.CHRIN()
|
list_filetype[2] = cbm.CHRIN()
|
||||||
while c64.CHRIN() {
|
while cbm.CHRIN() {
|
||||||
; read the rest of the entry until the end
|
; read the rest of the entry until the end
|
||||||
}
|
}
|
||||||
|
|
||||||
void c64.CHRIN() ; skip 2 bytes
|
void cbm.CHRIN() ; skip 2 bytes
|
||||||
void c64.CHRIN()
|
void cbm.CHRIN()
|
||||||
|
|
||||||
if not list_skip_disk_name {
|
if not list_skip_disk_name {
|
||||||
if not list_pattern
|
if not list_pattern
|
||||||
@ -240,8 +252,8 @@ close_end:
|
|||||||
sub lf_end_list() {
|
sub lf_end_list() {
|
||||||
; -- end an iterative file listing session (close channels).
|
; -- end an iterative file listing session (close channels).
|
||||||
if iteration_in_progress {
|
if iteration_in_progress {
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(12)
|
cbm.CLOSE(12)
|
||||||
iteration_in_progress = false
|
iteration_in_progress = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,25 +261,24 @@ close_end:
|
|||||||
|
|
||||||
; ----- iterative file loader functions (uses io channel 12) -----
|
; ----- iterative file loader functions (uses io channel 12) -----
|
||||||
|
|
||||||
sub f_open(ubyte drivenumber, uword filenameptr) -> bool {
|
sub f_open(uword filenameptr) -> bool {
|
||||||
; -- open a file for iterative reading with f_read
|
; -- open a file for iterative reading with f_read
|
||||||
; note: only a single iteration loop can be active at a time!
|
; note: only a single iteration loop can be active at a time!
|
||||||
f_close()
|
f_close()
|
||||||
|
|
||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
c64.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
|
cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
|
||||||
last_drivenumber = drivenumber
|
void cbm.OPEN() ; open 12,8,12,"filename"
|
||||||
void c64.OPEN() ; open 12,8,12,"filename"
|
|
||||||
if_cc {
|
if_cc {
|
||||||
if c64.READST()==0 {
|
if cbm.READST()==0 {
|
||||||
iteration_in_progress = true
|
iteration_in_progress = true
|
||||||
void c64.CHKIN(12) ; use #12 as input channel
|
void cbm.CHKIN(12) ; use #12 as input channel
|
||||||
if_cc {
|
if_cc {
|
||||||
void c64.CHRIN() ; read first byte to test for file not found
|
void cbm.CHRIN() ; read first byte to test for file not found
|
||||||
if not c64.READST() {
|
if not cbm.READST() {
|
||||||
c64.CLOSE(12) ; close file because we already consumed first byte
|
cbm.CLOSE(12) ; close file because we already consumed first byte
|
||||||
void c64.OPEN() ; re-open the file
|
void cbm.OPEN() ; re-open the file
|
||||||
void c64.CHKIN(12)
|
void cbm.CHKIN(12)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,9 +291,6 @@ close_end:
|
|||||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||||
; -- read from the currently open file, up to the given number of bytes.
|
; -- read from the currently open file, up to the given number of bytes.
|
||||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||||
; NOTE: on systems with banked ram (such as Commander X16) this routine DOES NOT
|
|
||||||
; automatically load into subsequent banks if it reaches a bank boundary!
|
|
||||||
; Consider using cx16diskio.f_read() on X16.
|
|
||||||
if not iteration_in_progress or not num_bytes
|
if not iteration_in_progress or not num_bytes
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -295,14 +303,14 @@ close_end:
|
|||||||
sta m_in_buffer+2
|
sta m_in_buffer+2
|
||||||
}}
|
}}
|
||||||
while num_bytes {
|
while num_bytes {
|
||||||
if c64.READST() {
|
if cbm.READST() {
|
||||||
f_close()
|
f_close()
|
||||||
if c64.READST() & $40 ; eof?
|
if cbm.READST() & $40 ; eof?
|
||||||
return list_blocks ; number of bytes read
|
return list_blocks ; number of bytes read
|
||||||
return 0 ; error.
|
return 0 ; error.
|
||||||
}
|
}
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr c64.CHRIN
|
jsr cbm.CHRIN
|
||||||
m_in_buffer sta $ffff
|
m_in_buffer sta $ffff
|
||||||
inc m_in_buffer+1
|
inc m_in_buffer+1
|
||||||
bne +
|
bne +
|
||||||
@ -317,12 +325,11 @@ m_in_buffer sta $ffff
|
|||||||
|
|
||||||
sub f_read_all(uword bufferpointer) -> uword {
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
; -- read the full contents of the file, returns number of bytes read.
|
; -- read the full contents of the file, returns number of bytes read.
|
||||||
; Note: Consider using cx16diskio.f_read_all() on X16!
|
|
||||||
if not iteration_in_progress
|
if not iteration_in_progress
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
uword total_read = 0
|
uword total_read = 0
|
||||||
while not c64.READST() {
|
while not cbm.READST() {
|
||||||
cx16.r0 = f_read(bufferpointer, 256)
|
cx16.r0 = f_read(bufferpointer, 256)
|
||||||
total_read += cx16.r0
|
total_read += cx16.r0
|
||||||
bufferpointer += cx16.r0
|
bufferpointer += cx16.r0
|
||||||
@ -340,9 +347,9 @@ m_in_buffer sta $ffff
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldx #12
|
ldx #12
|
||||||
jsr c64.CHKIN ; use channel 12 again for input
|
jsr cbm.CHKIN ; use channel 12 again for input
|
||||||
ldy #0
|
ldy #0
|
||||||
_loop jsr c64.CHRIN
|
_loop jsr cbm.CHRIN
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
beq _end
|
beq _end
|
||||||
iny
|
iny
|
||||||
@ -357,12 +364,11 @@ _end rts
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub f_close() {
|
sub f_close() {
|
||||||
; -- end an iterative file loading session (close channels).
|
; -- end an iterative file loading session (close channels).
|
||||||
if iteration_in_progress {
|
if iteration_in_progress {
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(12)
|
cbm.CLOSE(12)
|
||||||
iteration_in_progress = false
|
iteration_in_progress = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,16 +376,16 @@ _end rts
|
|||||||
|
|
||||||
; ----- iterative file writing functions (uses io channel 13) -----
|
; ----- iterative file writing functions (uses io channel 13) -----
|
||||||
|
|
||||||
sub f_open_w(ubyte drivenumber, uword filenameptr) -> bool {
|
sub f_open_w(uword filenameptr) -> bool {
|
||||||
; -- open a file for iterative writing with f_write
|
; -- open a file for iterative writing with f_write
|
||||||
f_close_w()
|
f_close_w()
|
||||||
|
|
||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
c64.SETLFS(13, drivenumber, 1)
|
cbm.SETLFS(13, drivenumber, 1)
|
||||||
void c64.OPEN() ; open 13,8,1,"filename"
|
void cbm.OPEN() ; open 13,8,1,"filename"
|
||||||
if_cc {
|
if_cc {
|
||||||
c64.CHKOUT(13) ; use #13 as output channel
|
cbm.CHKOUT(13) ; use #13 as output channel
|
||||||
return not c64.READST()
|
return not cbm.READST()
|
||||||
}
|
}
|
||||||
f_close_w()
|
f_close_w()
|
||||||
return false
|
return false
|
||||||
@ -388,39 +394,39 @@ _end rts
|
|||||||
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||||
; -- write the given number of bytes to the currently open file
|
; -- write the given number of bytes to the currently open file
|
||||||
if num_bytes!=0 {
|
if num_bytes!=0 {
|
||||||
c64.CHKOUT(13) ; use #13 as output channel again
|
cbm.CHKOUT(13) ; use #13 as output channel again
|
||||||
repeat num_bytes {
|
repeat num_bytes {
|
||||||
c64.CHROUT(@(bufferpointer))
|
cbm.CHROUT(@(bufferpointer))
|
||||||
bufferpointer++
|
bufferpointer++
|
||||||
}
|
}
|
||||||
return not c64.READST()
|
return not cbm.READST()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_close_w() {
|
sub f_close_w() {
|
||||||
; -- end an iterative file writing session (close channels).
|
; -- end an iterative file writing session (close channels).
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(13)
|
cbm.CLOSE(13)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
; ---- other functions ----
|
; ---- other functions ----
|
||||||
|
|
||||||
sub status(ubyte drivenumber) -> uword {
|
sub status() -> uword {
|
||||||
; -- retrieve the disk drive's current status message
|
; -- retrieve the disk drive's current status message
|
||||||
uword messageptr = &list_filename
|
uword messageptr = &list_filename
|
||||||
c64.SETNAM(0, list_filename)
|
cbm.SETNAM(0, list_filename)
|
||||||
c64.SETLFS(15, drivenumber, 15)
|
cbm.SETLFS(15, drivenumber, 15)
|
||||||
void c64.OPEN() ; open 15,8,15
|
void cbm.OPEN() ; open 15,8,15
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
void c64.CHKIN(15) ; use #15 as input channel
|
void cbm.CHKIN(15) ; use #15 as input channel
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
while not c64.READST() {
|
while not cbm.READST() {
|
||||||
cx16.r5L = c64.CHRIN()
|
cx16.r5L = cbm.CHRIN()
|
||||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||||
break
|
break
|
||||||
@(messageptr) = cx16.r5L
|
@(messageptr) = cx16.r5L
|
||||||
@ -429,8 +435,8 @@ _end rts
|
|||||||
@(messageptr) = 0
|
@(messageptr) = 0
|
||||||
|
|
||||||
done:
|
done:
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
cbm.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(15)
|
cbm.CLOSE(15)
|
||||||
return list_filename
|
return list_filename
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
@ -438,9 +444,9 @@ io_error:
|
|||||||
goto done
|
goto done
|
||||||
}
|
}
|
||||||
|
|
||||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> bool {
|
sub save(uword filenameptr, uword address, uword size) -> bool {
|
||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
cbm.SETLFS(1, drivenumber, 0)
|
||||||
uword @shared end_address = address + size
|
uword @shared end_address = address + size
|
||||||
cx16.r0L = 0
|
cx16.r0L = 0
|
||||||
|
|
||||||
@ -453,17 +459,17 @@ io_error:
|
|||||||
lda #<P8ZP_SCRATCH_W1
|
lda #<P8ZP_SCRATCH_W1
|
||||||
ldx end_address
|
ldx end_address
|
||||||
ldy end_address+1
|
ldy end_address+1
|
||||||
jsr c64.SAVE
|
jsr cbm.SAVE
|
||||||
php
|
php
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
plp
|
plp
|
||||||
}}
|
}}
|
||||||
|
|
||||||
if_cc
|
if_cc
|
||||||
cx16.r0L = c64.READST()==0
|
cx16.r0L = cbm.READST()==0
|
||||||
|
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(1)
|
cbm.CLOSE(1)
|
||||||
|
|
||||||
return cx16.r0L
|
return cx16.r0L
|
||||||
}
|
}
|
||||||
@ -474,99 +480,77 @@ io_error:
|
|||||||
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
; If you specify a custom address_override, the first 2 bytes in the file are ignored
|
||||||
; and the rest is loaded at the given location in memory.
|
; and the rest is loaded at the given location in memory.
|
||||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
||||||
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
|
sub load(uword filenameptr, uword address_override) -> uword {
|
||||||
; (which is possible on the Commander X16), the returned size is not correct,
|
cbm.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
; because it doesn't take the number of ram banks into account.
|
|
||||||
; Also consider using cx16diskio.load() instead on the Commander X16.
|
|
||||||
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
|
|
||||||
return internal_load_routine(drivenumber, filenameptr, address_override, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
; Use kernal LOAD routine to load the given file in memory.
|
|
||||||
; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
|
|
||||||
; This is different from Basic's LOAD instruction which always skips the first two bytes.
|
|
||||||
; The load address is mandatory.
|
|
||||||
; Returns the end load address+1 if successful or 0 if a load error occurred.
|
|
||||||
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
|
|
||||||
; (which is possible on the Commander X16), the returned size is not correct,
|
|
||||||
; because it doesn't take the number of ram banks into account.
|
|
||||||
; Also consider using cx16diskio.load_raw() instead on the Commander X16.
|
|
||||||
sub load_raw(ubyte drivenumber, uword filenameptr, uword address) -> uword {
|
|
||||||
if sys.target==16 ; are we on commander X16?
|
|
||||||
return internal_load_routine(drivenumber, filenameptr, address, true)
|
|
||||||
; fallback to reading the 2 header bytes separately
|
|
||||||
if not f_open(drivenumber, filenameptr)
|
|
||||||
return 0
|
|
||||||
cx16.r1 = f_read(address, 2)
|
|
||||||
f_close()
|
|
||||||
if cx16.r1!=2
|
|
||||||
return 0
|
|
||||||
address += 2
|
|
||||||
return load(drivenumber, filenameptr, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
; Internal routine, only to be used on Commander X16 platform if headerless=true,
|
|
||||||
; because this routine uses kernal support for that to load headerless files.
|
|
||||||
; On C64 it will always be called with headerless=false.
|
|
||||||
sub internal_load_routine(ubyte drivenumber, uword filenameptr, uword address_override, bool headerless) -> uword {
|
|
||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
|
||||||
ubyte secondary = 1
|
ubyte secondary = 1
|
||||||
cx16.r1 = 0
|
cx16.r1 = 0
|
||||||
if address_override
|
if address_override
|
||||||
secondary = 0
|
secondary = 0
|
||||||
if headerless
|
cbm.SETLFS(1, drivenumber, secondary)
|
||||||
secondary |= %00000010 ; activate cx16 kernal headerless load support
|
|
||||||
c64.SETLFS(1, drivenumber, secondary)
|
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #0
|
lda #0
|
||||||
ldx address_override
|
ldx address_override
|
||||||
ldy address_override+1
|
ldy address_override+1
|
||||||
jsr c64.LOAD
|
jsr cbm.LOAD
|
||||||
bcs +
|
bcs +
|
||||||
stx cx16.r1
|
stx cx16.r1
|
||||||
sty cx16.r1+1
|
sty cx16.r1+1
|
||||||
+ ldx P8ZP_SCRATCH_REG
|
+ ldx P8ZP_SCRATCH_REG
|
||||||
}}
|
}}
|
||||||
|
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(1)
|
cbm.CLOSE(1)
|
||||||
return cx16.r1
|
return cx16.r1
|
||||||
}
|
}
|
||||||
|
|
||||||
sub delete(ubyte drivenumber, uword filenameptr) {
|
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
|
||||||
|
; No program header is assumed in the file. Everything is loaded.
|
||||||
|
; See comments on load() for more details.
|
||||||
|
sub load_raw(uword filenameptr, uword address) -> uword {
|
||||||
|
; read the 2 header bytes separately to skip them
|
||||||
|
if not f_open(filenameptr)
|
||||||
|
return 0
|
||||||
|
cx16.r1 = f_read(address, 2)
|
||||||
|
f_close()
|
||||||
|
if cx16.r1!=2
|
||||||
|
return 0
|
||||||
|
address += 2
|
||||||
|
return load(filenameptr, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub delete(uword filenameptr) {
|
||||||
; -- delete a file on the drive
|
; -- delete a file on the drive
|
||||||
list_filename[0] = 's'
|
list_filename[0] = 's'
|
||||||
list_filename[1] = ':'
|
list_filename[1] = ':'
|
||||||
ubyte flen = string.copy(filenameptr, &list_filename+2)
|
ubyte flen = string.copy(filenameptr, &list_filename+2)
|
||||||
c64.SETNAM(flen+2, list_filename)
|
cbm.SETNAM(flen+2, list_filename)
|
||||||
c64.SETLFS(1, drivenumber, 15)
|
cbm.SETLFS(1, drivenumber, 15)
|
||||||
void c64.OPEN()
|
void cbm.OPEN()
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(1)
|
cbm.CLOSE(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
sub rename(uword oldfileptr, uword newfileptr) {
|
||||||
; -- rename a file on the drive
|
; -- rename a file on the drive
|
||||||
list_filename[0] = 'r'
|
list_filename[0] = 'r'
|
||||||
list_filename[1] = ':'
|
list_filename[1] = ':'
|
||||||
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
|
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
|
||||||
list_filename[flen_new+2] = '='
|
list_filename[flen_new+2] = '='
|
||||||
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
|
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
|
||||||
c64.SETNAM(3+flen_new+flen_old, list_filename)
|
cbm.SETNAM(3+flen_new+flen_old, list_filename)
|
||||||
c64.SETLFS(1, drivenumber, 15)
|
cbm.SETLFS(1, drivenumber, 15)
|
||||||
void c64.OPEN()
|
void cbm.OPEN()
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(1)
|
cbm.CLOSE(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
sub send_command(ubyte drivenumber, uword commandptr) {
|
sub send_command(uword commandptr) {
|
||||||
; -- send a dos command to the drive
|
; -- send a dos command to the drive
|
||||||
c64.SETNAM(string.length(commandptr), commandptr)
|
cbm.SETNAM(string.length(commandptr), commandptr)
|
||||||
c64.SETLFS(15, drivenumber, 15)
|
cbm.SETLFS(15, drivenumber, 15)
|
||||||
void c64.OPEN()
|
void cbm.OPEN()
|
||||||
c64.CLRCHN()
|
cbm.CLRCHN()
|
||||||
c64.CLOSE(15)
|
cbm.CLOSE(15)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user