Compare commits

...

162 Commits
v8.11 ... v9.0

Author SHA1 Message Date
7ee162d98b preparing version 9.0 2023-06-05 19:47:00 +02:00
380f557c45 vm: implement split incr/decr 2023-06-03 22:22:13 +02:00
1bdae53f4e fix unit tests 2023-06-03 21:39:34 +02:00
9314c346da -target option is now required; c64 no longer the default 2023-06-03 19:14:45 +02:00
bfaad1388c IR: handle split arrays without new custom opcodes 2023-06-03 01:51:02 +02:00
0b580ad05d v9 upgrading doc 2023-06-01 20:23:04 +02:00
bb35a80177 %option splitarrays now also at module level 2023-05-31 21:50:41 +02:00
24fc95ac81 fix atari target syslib 2023-05-31 20:58:00 +02:00
8f864417c4 added %option splitarrays (block level) 2023-05-31 18:49:21 +02:00
bb9d29b061 fix an array literal assignment type error for word arrays 2023-05-30 22:46:37 +02:00
b9d8ec1463 add -splitarrays command line option 2023-05-30 19:08:34 +02:00
1842a7660d fix compiler crash on missing arguments for clamp,min,max 2023-05-30 18:13:58 +02:00
5caa2f5536 attempt to get newer 64tass from debian testing repo 2023-05-29 23:46:47 +02:00
d6078be8b7 attempt to get newer 64tass from debian testing repo 2023-05-29 23:44:10 +02:00
cf60723f14 attempt to get newer 64tass from debian testing repo 2023-05-29 23:43:08 +02:00
f7ff0a2b1d attempt to get newer 64tass from debian testing repo 2023-05-29 23:39:00 +02:00
cc49664b2f attempt to get newer 64tass from debian testing repo 2023-05-29 23:34:34 +02:00
99fe74f026 attempt to get newer 64tass from debian testing repo 2023-05-29 23:31:23 +02:00
b021869eeb attempt to get newer 64tass from debian testing repo 2023-05-29 23:24:48 +02:00
b8806d163b attempt to get newer 64tass from debian testing repo 2023-05-29 23:22:05 +02:00
1116aae1de attempt to get newer 64tass from debian testing repo 2023-05-29 23:18:15 +02:00
5e5f60253b attempt to get newer 64tass from debian testing repo 2023-05-29 23:14:22 +02:00
bbc02752c9 use split word arrays in various examples, fix codegen issue, docs 2023-05-29 15:34:33 +02:00
9896bc110e fix some split array issues in 6502 codegen 2023-05-28 22:49:33 +02:00
ca60f8ecdd Merge branch 'master' into split-arrays 2023-05-28 22:35:16 +02:00
544acd1e35 Merge branch 'v8_maintenance' 2023-05-28 22:30:52 +02:00
6e07602d77 fix psg initial envelope maxvol setting 2023-05-28 22:30:34 +02:00
82898f7bba fix some split array issues in 6502 codegen 2023-05-28 22:24:56 +02:00
d61283a8bc Merge branch 'master' into split-arrays 2023-05-28 14:25:37 +02:00
1ee3f826cc fix sqrt() regression 2023-05-28 14:23:47 +02:00
4a00a5ba9e use split word arrays in various examples 2023-05-28 13:51:58 +02:00
39eda67867 Merge branch 'master' into split-arrays
# Conflicts:
#	examples/test.p8
2023-05-28 13:28:43 +02:00
a99d38fdaa Merge branch 'v8_maintenance'
# Conflicts:
#	examples/test.p8
2023-05-28 13:26:05 +02:00
0eb2d437e2 fix compiler error and codegen fault on signed value bitwise operation 2023-05-28 13:13:11 +02:00
3ac9036c79 more split array stuff for 6502 2023-05-27 22:44:45 +02:00
c94e292176 more split array stuff 2023-05-27 12:47:11 +02:00
91d87c2d9b Merge branch 'master' into split-arrays 2023-05-26 20:22:30 +02:00
ff472f69c0 update gradle wrapper to 8.1.1 2023-05-26 20:21:34 +02:00
e18119e24c Merge branch 'master' into split-arrays 2023-05-26 19:25:57 +02:00
4a592dc64c kotlin 1.8.21 2023-05-26 19:20:56 +02:00
d9e13201dd fix kotlin version IDE warning 2023-05-26 19:14:19 +02:00
5c75b19c5d fix kotlin version IDE warning 2023-05-26 19:13:21 +02:00
52a77db60f adding split array type 2023-05-26 19:11:07 +02:00
0513c250fb Merge branch 'v8_maintenance' 2023-05-23 20:42:51 +02:00
48864ad6cf add a unit test that checks for 64tass availability 2023-05-23 20:42:36 +02:00
cdbccad21e optimized gfx2 plot and horizontal_line a bit more 2023-05-23 20:29:17 +02:00
e15bc68c9b added gfx2.fill() flood fill routine 2023-05-23 00:50:10 +02:00
8bffd7672d added sys.irqsafe_set_irqd()/irqsafe_clear_irqd() 2023-05-22 21:13:20 +02:00
61df5b3060 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/prog8lib/cx16/syslib.p8
2023-05-22 20:43:05 +02:00
b5255444cd irq-safe irqd handling for RDTIM16 2023-05-22 20:36:33 +02:00
0c94e377fc Merge branch 'v8_maintenance' 2023-05-21 16:09:31 +02:00
8e5c67b4b2 ir: don't refuse complicated array lookup expressions 2023-05-21 16:07:19 +02:00
b24f2f1756 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/prog8lib/cx16/syslib.p8
#	examples/test.p8
2023-05-21 15:05:17 +02:00
c69c17de42 cx16 avoid ram bank issue with RDTIM in sys.wait() and c64.RDTIM16() 2023-05-21 15:03:33 +02:00
061617122a Merge branch 'v8_maintenance'
# Conflicts:
#	examples/test.p8
2023-05-20 18:07:57 +02:00
125ce3240f expr operands assignment refactor 2023-05-20 18:04:46 +02:00
7215efe167 fix expr eval error in certain situations
such as pokew() with 2 complex operands
2023-05-20 17:42:35 +02:00
06d1570142 cx16: added diskio.save_raw() headerless save routine 2023-05-20 00:00:50 +02:00
093c370faa todo 2023-05-19 01:26:15 +02:00
aec9574737 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/version.txt
#	docs/source/todo.rst
#	examples/test.p8
2023-05-18 22:47:06 +02:00
7ceb76cff5 fix compiler crash on certain operands type mismatch 2023-05-18 22:46:00 +02:00
300e2fe9f8 IR: wrong attempt at optimizing register usage by reusing registers inside different code chunks 2023-05-18 21:57:21 +02:00
91e1643627 update 3rd party libraries 2023-05-18 11:47:30 +02:00
91421b0c62 IR handy sequence shortcut functions 2023-05-18 11:32:20 +02:00
40f611664f upgr 2023-05-18 00:04:31 +02:00
dcba4f4098 fix resultregister crash 2023-05-18 00:00:37 +02:00
c098ad2b3b fix vm minf/maxf 2023-05-17 23:18:14 +02:00
b43223cb7a added clamp() builtin function and floats.clampf() 2023-05-17 23:12:58 +02:00
e243531dab upgrading 2023-05-17 00:49:47 +02:00
1af38e62bc removed floats.fabs() and floats.sqrt()/fsqrt() 2023-05-17 00:46:15 +02:00
f37f062cdc fix for loop pre-check 2023-05-17 00:33:55 +02:00
7e734214dc v8_maintenance branch made 2023-05-15 23:01:43 +02:00
05d152746f Merge branch 'master' into version_9 2023-05-15 22:43:03 +02:00
dea7f37553 vm: fix % result when dividing by 0 2023-05-15 20:33:20 +02:00
415c599310 update cx16 keyhandler example to r43 keyboard changes 2023-05-14 23:38:16 +02:00
70cd4fedbe Revert "update cx16 keyhandler example to r43 keyboard changes"
This reverts commit 1e6d7673bc.
2023-05-14 23:29:04 +02:00
1e6d7673bc update cx16 keyhandler example to r43 keyboard changes 2023-05-14 23:11:24 +02:00
b4963b725b Merge branch 'master' into version_9
# Conflicts:
#	compiler/res/version.txt
2023-05-14 22:19:23 +02:00
0371ffa4ce 'amiga' example using iso font 2023-05-14 21:55:35 +02:00
6a664a7e15 Merge branch 'master' into version_9 2023-05-14 21:03:08 +02:00
88ce9300bc fix parse cpureg in IR regspec 2023-05-14 21:02:40 +02:00
85cf0e311c Merge branch 'master' into version_9
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	docs/source/todo.rst
#	intermediate/src/prog8/intermediate/IRInstructions.kt
2023-05-14 20:47:09 +02:00
0e3d75cfeb move irType() to intermediate module 2023-05-14 20:44:32 +02:00
630c8a5faa IR: fix romsub encoding 2023-05-14 18:08:06 +02:00
905921a684 IR: new (sys)call instructions that encapsulate the full subroutine call
to fix the bugs resulting from nesting subroutine calls (as param to another call etc)
2023-05-14 15:20:25 +02:00
1e469b3b0f Merge branch 'master' into version_9
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2023-05-09 22:45:21 +02:00
bff3c4f95c IR now converts IRInlineAsmChunk (of type IR) into regular code chunks directly.
.p8ir files usually won't contain <INLINEASM> nodes any longer
2023-05-09 21:04:31 +02:00
bd2bcb6994 Merge branch 'master' into version_9
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt
#	compiler/res/prog8lib/c128/syslib.p8
#	compiler/res/prog8lib/c64/syslib.p8
#	compiler/res/prog8lib/cx16/syslib.p8
#	docs/source/todo.rst
#	examples/test.p8
#	intermediate/src/prog8/intermediate/IRInstructions.kt
2023-05-08 23:17:52 +02:00
4c8898a639 fix typecheck crash on certain byte to word assignments 2023-05-08 23:02:48 +02:00
97df33ab1a IR: fix byte to word assignment not doing value extension 2023-05-08 22:47:00 +02:00
ef46fb2685 refactor 2023-05-08 21:51:55 +02:00
d5d6dd3614 optimize typecast expr 2023-05-08 03:30:14 +02:00
6c233c6a0a optimize add/sub expr 2023-05-08 02:41:34 +02:00
6db715d879 optimize multiplication expr 2023-05-08 02:10:54 +02:00
ab02e8a546 optimize more carry flag assembly 2023-05-07 23:55:34 +02:00
8cbfe64f19 optimize some carry flag assembly 2023-05-07 23:27:49 +02:00
fd1e9971e4 asmsub Pc params and returnvalue must be boolean 2023-05-07 22:59:30 +02:00
68336a76c5 optimized word comparison expressions 2023-05-07 20:40:48 +02:00
393e914a86 optimized word equality comparison expressions 2023-05-07 18:55:17 +02:00
ffb54110e9 optimized byte comparison expressions 2023-05-07 15:15:58 +02:00
533d825f1a optimized ubyte comparison expressions 2023-05-07 14:47:31 +02:00
c65279b672 optimized logical expressions more 2023-05-07 13:29:45 +02:00
f9926beeef fix cx16.psg irq issue 2023-05-04 00:16:24 +02:00
add8a777d8 IR: binarydata fixes 2023-05-03 22:31:04 +02:00
21bc505d85 for loops no longer execute when from var already reached beyond the end 2023-05-03 00:43:03 +02:00
3fc49c001e IR: fix for-loop codegen when step<0 2023-05-02 23:12:11 +02:00
3d69a95c49 IR: fix for-loop codegen when step<0 2023-05-02 23:09:42 +02:00
d81fdf6d6b for loops... 2023-05-02 22:55:58 +02:00
87d3109ffb diskio f_seek_w() abandoned due to unreliability 2023-05-02 19:33:49 +02:00
180dbbb521 cleaning up the diskio modules
for cx16: removed cx16diskio (merged everything into its regular diskio module)
for cx16: the load() and load_raw() routines that took an extra ram bank parameter are gone. You have to cx16.rambank() yourself before calling load().
2023-05-02 03:31:11 +02:00
24aac7cee5 cleaning up the diskio modules 2023-05-02 02:15:22 +02:00
53e18a5387 Api change: drivenumber parameter removed from all routines in diskio and cx16diskio modules 2023-05-02 01:48:56 +02:00
92062d056d divmod() now works on multiple data types including float.
divmodw() has been removed
2023-05-02 01:19:53 +02:00
06368ab0a1 sqrt() now works on multiple data types including float.
no need to use floats.sqrtf() anymore
2023-05-02 01:19:53 +02:00
38efe25c68 abs() now works on multiple data types including float.
no need to use floats.fabs() anymore
2023-05-02 01:19:53 +02:00
319079de7a sqrt 2023-05-02 01:19:53 +02:00
025bf900a5 min max docs, added floats.minf() and maxf() 2023-05-02 01:19:53 +02:00
2885f4f7b1 fix 2023-05-02 01:19:53 +02:00
c07eda15b1 adding min() and max() 2023-05-02 01:19:53 +02:00
4274296cf3 api change: new 'cbm' module that now contains the common CBM kernal variables and routines. 2023-05-02 01:19:53 +02:00
76a203d4df api change: rename builtin func sqrt16 to sqrtw 2023-05-02 01:19:53 +02:00
24f37e2062 fix 2023-05-02 01:19:36 +02:00
f465b2e2a0 some improvements to IR peephole optimizer 2023-05-02 00:29:04 +02:00
ce00e49a89 version 8.12 2023-04-30 14:04:54 +02:00
d494f9d66b fix 2023-04-29 18:04:08 +02:00
c35a183a64 extra fix 2023-04-29 17:24:01 +02:00
9cdd5fe7f2 fix byte to word sign extension error in certain cases 2023-04-29 17:14:50 +02:00
c21428215e fix possible mkword() error 2023-04-29 14:39:14 +02:00
64d5af46f5 fix IDEA kotlin version 2023-04-29 14:23:40 +02:00
25846ea18a fix zsound stream example (missing sound file) 2023-04-29 13:02:24 +02:00
798383596d fix %option merge possible error 2023-04-29 00:01:59 +02:00
9ca71bc937 fix %option merge not choosing correct block to merge into 2023-04-28 23:52:02 +02:00
5407429ec0 improve error message 2023-04-28 23:32:19 +02:00
ee5c94f6db c128: fix key status zp location symbols 2023-04-28 20:43:26 +02:00
91045afbee document limited fp support 2023-04-28 18:18:41 +02:00
3f64782023 c128: remove floats module 2023-04-28 17:48:54 +02:00
f8d35f9502 c128: no FP support 2023-04-28 17:43:42 +02:00
ea78d3ec9a c128: better ZP definition 2023-04-28 17:08:56 +02:00
e056a28316 c128: fix memory bank resetting 2023-04-28 04:02:07 +02:00
0bea721c2e docs 2023-04-27 01:26:25 +02:00
e1b89494d0 tiny psg improvement to avoid clicks more on changing freq or envelope, added cx16.vpoke_mask() 2023-04-26 22:45:32 +02:00
cd8e7f3912 psg comment 2023-04-24 01:23:03 +02:00
50604c25c2 remove obsolete comments, updated links and docs. 2023-04-23 15:13:53 +02:00
aa6b2357d8 fix void warnings 2023-04-18 23:47:31 +02:00
5b2d29bef6 improved and added a few system routines for the cx16 2023-04-18 23:20:28 +02:00
a296d26328 api change: renamed cx16.push/pop_vera_context() to save/restore_vera_context()
this better reflects its capability because it doesn't use a stack, only a single buffer
2023-04-17 23:37:15 +02:00
d01a26ec61 fix occasional crash when indexing an undefined array variable 2023-04-16 05:23:06 +02:00
efd7d6f0c0 tweak IR call args setting now via special SETPARAM instruction 2023-04-14 02:10:39 +02:00
b55be093be tweak IR 2023-04-11 22:48:20 +02:00
7c1d5cadd7 fix sort and reverse on strings on 6502 codegen 2023-04-10 19:33:24 +02:00
dd1592b03b ir syscalls args via stack instead of fixed r65500+ 2023-04-10 18:02:37 +02:00
9b37ac483f vm fix str to word conversion
ir SYSCALL puts result(s) on value stack,  instead of on hardcoded r0, r1
2023-04-10 16:26:42 +02:00
090820958e ir divmod returns its results on valuestack, to keep consistency with the rule that only 1 register can be a returnvalue 2023-04-10 15:26:30 +02:00
ac21e1be5c vm syscall instruction no longer fixed to r0 2023-04-10 13:44:05 +02:00
5196443b26 fix 2023-04-10 12:16:52 +02:00
c8531cbeb1 remove unused variables from IR output 2023-04-09 23:09:30 +02:00
c560abedba fix compiler crash on rol/ror array value 2023-04-09 22:29:11 +02:00
9b952fbc44 tweaking IR instruction set branch instructions 2023-04-09 22:17:19 +02:00
ccdf05e922 tweaking IR instruction formats 2023-04-09 16:12:16 +02:00
c3d74f2ae9 fix golden ram area for x16, remove romsub restriction
note: romsubs still won't work in the VM but at least they compile again
2023-04-08 00:40:52 +02:00
f47498888c optimize imports 2023-04-07 22:34:23 +02:00
5665a7f0cb also track ir reg types 2023-04-07 22:24:17 +02:00
221 changed files with 8059 additions and 5691 deletions

View File

@ -12,8 +12,13 @@ jobs:
- name: Checkout code
uses: actions/checkout@v2
- name: Install 64tass
run: sudo apt-get update -y && sudo apt-get install -y 64tass
- name: build and install recent 64tass
run: |
sudo apt-get install -y make build-essential
git clone --depth=1 https://github.com/irmen/64tass
cd 64tass
make -j4
sudo make install
- name: Set up JDK 11
uses: actions/setup-java@v2

2
.idea/kotlinc.xml generated
View File

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

View File

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

View File

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

View File

@ -1,55 +1,42 @@
<component name="libraryTable">
<library name="io.kotest.runner.junit5.jvm" type="repository">
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.5.5" />
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.6.2" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.5.5/kotest-runner-junit5-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.5.5/kotest-framework-api-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.4/kotlinx-coroutines-test-jvm-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.6.2/kotest-runner-junit5-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.6.2/kotest-framework-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.5.5/kotest-framework-engine-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.154/classgraph-4.8.154.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.7.0/kotlinx-coroutines-test-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.6.2/kotest-framework-engine-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.157/classgraph-4.8.157.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.4/kotlinx-coroutines-debug-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.5.5/kotest-framework-discovery-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.5.5/kotest-extensions-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-jvm/1.13.1/mockk-jvm-1.13.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.13.1/mockk-dsl-jvm-1.13.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.13.1/mockk-agent-jvm-1.13.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.2/objenesis-3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api-jvm/1.13.1/mockk-agent-api-jvm-1.13.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-core-jvm/1.13.1/mockk-core-jvm-1.13.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.13.2/junit-4.13.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter/5.8.2/junit-jupiter-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.8.2/junit-jupiter-params-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.8.2/junit-jupiter-engine-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.6.4/kotlinx-coroutines-core-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.5.5/kotest-framework-concurrency-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.6.2/kotest-framework-discovery-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.6.2/kotest-extensions-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.6.2/kotest-framework-concurrency-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -1,13 +1,13 @@
<component name="libraryTable">
<library name="michael.bull.kotlin.result.jvm" type="repository">
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.17/kotlin-result-jvm-1.1.17.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -76,7 +76,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
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.
Look in the [syntax-files](https://github.com/irmen/prog8/tree/master/syntax-files) directory in the github repository to find them.

View File

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

View File

@ -235,7 +235,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
class StRomSub(name: String,
val address: UInt,
val address: UInt?, // null in case of asmsub, specified in case of romsub
val parameters: List<StRomSubParameter>,
val returns: List<StRomSubParameter>,
astNode: PtNode) :

View File

@ -40,14 +40,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
val stNode = when(node) {
is PtAsmSub -> {
if(node.address==null) {
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
StSub(node.name, params, node.returns.singleOrNull()?.second, node)
} else {
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
StRomSub(node.name, node.address, parameters, returns, node)
}
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
StRomSub(node.name, node.address, parameters, returns, node)
}
is PtBlock -> {
StNode(node.name, StNodeType.BLOCK, node)

View File

@ -63,7 +63,7 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub?
}

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@ class CompilationOptions(val output: OutputType,
var experimentalCodegen: Boolean = false,
var varsHigh: Boolean = false,
var useNewExprCode: Boolean = false,
var splitWordArrays: Boolean = false,
var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()

View File

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

View File

@ -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) {
private var nextLocation: UInt = region.first

View File

@ -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?
init {
if (options.floats) {
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
@ -39,9 +43,9 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
}
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()
}

View File

@ -6,15 +6,22 @@ import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
// reference: "Mapping the C128" zero page chapter.
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte
override val SCRATCH_REG = 0x9cu // temp storage for a register, must be B1+1
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
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_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
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(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
@ -24,23 +31,42 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
when (options.zeropage) {
ZeropageType.FULL -> {
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09?
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
free.addAll(0x0au..0xffu)
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.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 -> {
free.clear() // don't use zeropage at all
}
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()
}

View File

@ -65,9 +65,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
}
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()

View File

@ -64,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0x0600u until 0x0800u)
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
}
}

View File

@ -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}")
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()

View File

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

View File

@ -362,13 +362,15 @@ class AsmGen6502Internal (
internal fun loadScaledArrayIndexIntoRegister(
expr: PtArrayIndexer,
elementDt: DataType,
register: CpuRegister,
addOneExtra: Boolean = false
register: CpuRegister
) {
val reg = register.toString().lowercase()
val indexnum = expr.index.asConstInteger()
if (indexnum != null) {
val indexValue = indexnum * options.compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0
val indexValue = if(expr.splitWords)
indexnum
else
indexnum * options.compTarget.memorySize(elementDt)
out(" ld$reg #$indexValue")
return
}
@ -377,77 +379,42 @@ class AsmGen6502Internal (
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}")
val indexName = asmVariableName(indexVar)
if (addOneExtra) {
// add 1 to the result
when (elementDt) {
in ByteDatatypes -> {
out(" ldy $indexName | iny")
when (register) {
CpuRegister.A -> out(" tya")
CpuRegister.X -> out(" tyx")
CpuRegister.Y -> {
}
}
}
in WordDatatypes -> {
out(" lda $indexName | sec | rol a")
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out(
"""
lda $indexName
asl a
asl a
sec
adc $indexName"""
)
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
if(expr.splitWords) {
when (register) {
CpuRegister.A -> out(" lda $indexName")
CpuRegister.X -> out(" ldx $indexName")
CpuRegister.Y -> out(" ldy $indexName")
}
} else {
when (elementDt) {
in ByteDatatypes -> out(" ld$reg $indexName")
in WordDatatypes -> {
out(" lda $indexName | asl a")
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
return
}
when (elementDt) {
in ByteDatatypes -> out(" ld$reg $indexName")
in WordDatatypes -> {
out(" lda $indexName | asl a")
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out(
"""
lda $indexName
asl a
asl a
clc
adc $indexName"""
)
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
}
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out("""
lda $indexName
asl a
asl a
clc
adc $indexName"""
)
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
}
}
@ -483,7 +450,7 @@ class AsmGen6502Internal (
when(reg) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister())
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype in SignedDatatypes)
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY,
@ -1639,14 +1606,7 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
@ -1680,14 +1640,7 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
@ -1723,15 +1676,7 @@ $repeatLabel lda $counterVar
if(wordJumpForSimpleRightOperands(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
return out(" jsr prog8_lib.reg_less_uw | beq $jumpIfFalseLabel")
}
@ -1769,15 +1714,7 @@ $repeatLabel lda $counterVar
if(wordJumpForSimpleRightOperands(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
}
@ -1816,14 +1753,7 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
@ -1859,14 +1789,7 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.BYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
out(" pla")
}
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
@ -1908,15 +1831,7 @@ $repeatLabel lda $counterVar
if(wordJumpForSimpleRightOperands(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
return code("P8ZP_SCRATCH_W2+1", "P8ZP_SCRATCH_W2")
}
@ -1959,15 +1874,7 @@ $repeatLabel lda $counterVar
if(wordJumpForSimpleLeftOperand(left, right, ::code))
return
if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, right)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
}
@ -2007,14 +1914,7 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
@ -2050,14 +1950,7 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.BYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
out(" pla")
}
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
@ -2101,15 +1994,7 @@ $repeatLabel lda $counterVar
if(wordJumpForSimpleRightOperands(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
}
@ -2156,15 +2041,7 @@ $repeatLabel lda $counterVar
return code(asmVariableName(left))
}
if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, right)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
}
@ -2200,14 +2077,7 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
out(" pla")
}
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
@ -2240,15 +2110,31 @@ $repeatLabel lda $counterVar
if(byteJumpForSimpleRightOperand(left, right, ::code))
return
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
return code("P8ZP_SCRATCH_B1")
}
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.BYTE, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
out(" pla")
}
return code("P8ZP_SCRATCH_B1")
}
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
if(left.isSimple()) {
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
}
private fun translateUwordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
@ -2282,15 +2168,7 @@ $repeatLabel lda $counterVar
if(wordJumpForSimpleRightOperands(left, right, ::code))
return
if(right.isSimple()) {
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(right, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, right)
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
}
@ -2328,15 +2206,7 @@ $repeatLabel lda $counterVar
if(wordJumpForSimpleRightOperands(left, right, ::code))
return
if(left.isSimple()) {
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.WORD, left)
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
}

View File

@ -1,5 +1,6 @@
package prog8.codegen.cpu6502
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
@ -30,12 +31,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultToStack, resultRegister)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultToStack, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultToStack, resultRegister)
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
"divmod" -> funcDivmod(fcall)
"divmodw" -> funcDivmodW(fcall)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
"divmod__uword" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall)
"ror" -> funcRor(fcall)
@ -90,13 +94,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
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)
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y)
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false)
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false)
}
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
assignAsmGen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
@ -113,8 +116,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", DataType.UWORD)
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, false)
asmgen.assignWordOperandsToAYAndVar(fcall.args[0], fcall.args[1], "P8ZP_SCRATCH_W2")
asmgen.out(" jsr prog8_lib.strcmp_mem")
if(resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
@ -206,27 +208,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
} else {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else {
asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
}
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
asmgen.out(" cmp P8ZP_SCRATCH_B1")
}
}
else -> {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
} else {
asmgen.pushCpuStack(DataType.UBYTE, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
}
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
asmgen.out(" cmp P8ZP_SCRATCH_B1")
}
}
} else
@ -252,25 +240,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
+""")
}
else -> {
if(arg1.isSimple()) {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bne +
cmp P8ZP_SCRATCH_W1
+""")
} else {
asmgen.pushCpuStack(DataType.UWORD, arg1)
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bne +
cmp P8ZP_SCRATCH_W1
+""")
}
asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bne +
cmp P8ZP_SCRATCH_W1
+""")
}
}
} else
@ -297,13 +272,34 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assign, fcall.definingISub())
}
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
when(fcall.args[0].type) {
DataType.UBYTE -> {
if(resultToStack)
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_stack")
else {
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
}
}
DataType.UWORD -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
}
}
DataType.FLOAT -> {
if(resultToStack)
throw AssemblyError("no support for sqrt float onto stack")
else {
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
}
else -> throw AssemblyError("weird dt")
}
}
@ -337,6 +333,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda #$numElements
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 -> {
asmgen.out("""
lda #<$varName
@ -346,6 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda #$numElements
jsr floats.func_reverse_f""")
}
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw AssemblyError("weird type")
}
}
@ -381,7 +388,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda #$numElements""")
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")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw AssemblyError("weird type")
}
} else
@ -416,6 +434,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("ror2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw")
}
@ -475,6 +495,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("ror split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw")
}
@ -517,6 +539,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("rol2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw")
}
@ -576,6 +600,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.splitWords)
TODO("rol split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw")
}
@ -591,14 +617,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
if(indexer.splitWords)
TODO("rol/ror split words access ${indexer.position}")
if(arrayvar.type==DataType.UWORD) {
if(dt!='b')
throw AssemblyError("non-array var indexing requires bytes dt")
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
} else {
val p = arrayvar.parent
val addressOf = PtAddressOf(arrayvar.position)
addressOf.add(arrayvar)
addressOf.parent = arrayvar.parent.parent
addressOf.parent = p
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)
@ -625,7 +654,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
}
}
@ -637,6 +666,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt")
}
} else {
@ -644,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_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
}
}
@ -655,21 +686,32 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val dt = fcall.args.single().type
if(resultToStack) {
when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
DataType.UWORD -> {}
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("no support for abs onto stack for this dt")
}
} else {
when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY")
DataType.UWORD -> {}
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
DataType.BYTE -> {
asmgen.out(" jsr prog8_lib.abs_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false)
}
DataType.WORD -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
}
DataType.FLOAT -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
DataType.UBYTE -> {
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
}
DataType.UWORD -> {
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
}
else -> throw AssemblyError("weird type")
}
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
}
}
@ -731,8 +773,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
// fall through method:
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
asmgen.out(" jsr prog8_lib.func_pokew")
}
@ -804,11 +845,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?) {
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.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
asmgen.out(" sta P8ESTACK_LO,x | pla | sta P8ESTACK_HI,x | dex")
} else {
val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])

View File

@ -237,6 +237,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
}
in SplitWordArrayTypes -> {
throw AssemblyError("can't push address of split-word array ${expr.position}")
}
in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
}
@ -761,6 +764,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
return
}
if(arrayExpr.splitWords)
TODO("split words expression ${arrayExpr.position}")
val constIndexNum = arrayExpr.index.asConstInteger()
if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)

View File

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

View File

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

View File

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

View File

@ -104,15 +104,15 @@ internal class ProgramAndVarsGen(
asmgen.out("+\t.word 0")
asmgen.out("prog8_entrypoint\t; assembly code starts here")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
}
CbmPrgLauncherType.NONE -> {
asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
}
}
}
@ -120,8 +120,8 @@ internal class ProgramAndVarsGen(
asmgen.out("; ---- atari xex program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
}
}
@ -140,20 +140,19 @@ internal class ProgramAndVarsGen(
if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr main.start")
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
asmgen.out(" jmp sys.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr main.start | lda #31 | sta $01")
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
asmgen.out(" jmp sys.cleanup_at_exit")
else
asmgen.out(" rts")
}
"c128" -> {
asmgen.out(" jsr main.start")
// TODO c128: how to bank basic+kernal back in?
asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00")
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
asmgen.out(" jmp sys.cleanup_at_exit")
else
asmgen.out(" rts")
}
@ -544,6 +543,11 @@ internal class ProgramAndVarsGen(
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
in SplitWordArrayTypes -> {
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
in ArrayDatatypes -> {
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes")
@ -628,6 +632,18 @@ internal class ProgramAndVarsGen(
asmgen.out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_UW_SPLIT -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_W_SPLIT -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_F -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map {
@ -687,7 +703,7 @@ internal class ProgramAndVarsGen(
val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_UW -> array.map {
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
}
@ -719,11 +735,11 @@ internal class ProgramAndVarsGen(
else
"-$$hexnum"
}
DataType.ARRAY_UW -> array.map {
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0')
}
DataType.ARRAY_W -> array.map {
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0)

View File

@ -115,7 +115,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
left is PtIdentifier && left.name==scopedName
}
TargetStorageKind.ARRAY -> {
left is PtArrayIndexer && left isSameAs array!!
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
}
TargetStorageKind.MEMORY -> {
left isSameAs memory!!

View File

@ -195,7 +195,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
val indexVar = target.array.index as? PtIdentifier
when {
indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
val targetVarName = if(target.array.splitWords)
"${target.asmVarname} + ${indexNum.number.toInt()}"
else
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
when (target.datatype) {
in ByteDatatypes -> {
when(value.kind) {
@ -282,8 +285,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1")
if(target.array.splitWords) {
asmgen.out(" lda ${target.array.variable.name}_lsb,y | sta P8ZP_SCRATCH_W1")
asmgen.out(" lda ${target.array.variable.name}_msb,y | sta P8ZP_SCRATCH_W1+1")
} else {
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1")
}
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_word_litval_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt())
@ -303,8 +311,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird source type ${value.kind}")
}
asmgen.restoreRegisterLocal(CpuRegister.Y)
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y")
if(target.array.splitWords) {
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name}_lsb,y")
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}_msb,y")
} else {
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y")
}
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
@ -772,13 +785,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
bcc +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy $otherName
rol a
eor #1
sta $name""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -798,13 +810,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $otherName
cmp $name
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $otherName
cpy $name
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -823,13 +833,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
lda #0
ldy $name
cpy $otherName
beq +
bcs ++
+ lda #0
beq ++
+ lda #1
rol a
+ sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -849,13 +857,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy $otherName
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -963,13 +969,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
bcc +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy #$value
rol a
eor #1
sta $name""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -989,13 +994,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda #$value
cmp $name
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy #$value
cpy $name
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -1014,13 +1017,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
lda #0
ldy $name
cpy #$value
beq +
bcs ++
+ lda #0
beq ++
+ lda #1
rol a
+ sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -1040,13 +1041,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy #$value
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""

View File

@ -1,5 +1,6 @@
package prog8tests.codegencpu6502
import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
@ -102,5 +103,14 @@ class TestCodegen: FunSpec({
result.name shouldBe "test"
Files.deleteIfExists(Path("${result.name}.asm"))
}
test("64tass assembler available? - if this fails you need to install 64tass in the path") {
val command = mutableListOf("64tass", "--version")
shouldNotThrowAny {
val proc = ProcessBuilder(command).inheritIO().start()
val result = proc.waitFor()
result.shouldBe(0)
}
}
})

View File

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

View File

@ -2,7 +2,10 @@ package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.*
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter

View File

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

View File

@ -1,5 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
@ -46,7 +47,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
assignment: PtAugmentedAssign
): IRCodeChunks {
val value = assignment.value
val vmDt = codeGen.irType(value.type)
val vmDt = irType(value.type)
return when(assignment.operator) {
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
@ -72,7 +73,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
val value = assignment.value
val targetDt = codeGen.irType(assignment.target.type)
val targetDt = irType(assignment.target.type)
return when (assignment.operator) {
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
@ -138,16 +139,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
"+" -> { }
"-" -> {
code += if(address!=null)
IRInstruction(Opcode.NEGM, vmDt, value = address)
IRInstruction(Opcode.NEGM, vmDt, address = address)
else
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
}
"~" -> {
val regMask = codeGen.registers.nextFree()
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)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = address)
else
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
}
@ -161,7 +162,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory
val targetArray = assignment.target.array
val vmDt = codeGen.irType(assignment.value.type)
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
var valueRegister = -1
@ -169,30 +171,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val zero = codeGen.isZero(assignment.value)
if(!zero) {
// calculate the assignment value
if (vmDt == IRDataType.FLOAT) {
if (valueDt == IRDataType.FLOAT) {
val tr = expressionEval.translateExpression(assignment.value)
valueFpRegister = tr.resultFpReg
addToResult(result, tr, -1, valueFpRegister)
} else {
val extendByteToWord = if(targetDt != valueDt) {
// usually an error EXCEPT when a byte is assigned to a word.
if(targetDt==IRDataType.WORD && valueDt==IRDataType.BYTE)
true
else
throw AssemblyError("assignment value and target dt mismatch")
} else false
if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
} else {
val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) {
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1 = valueRegister), null)
}
}
}
}
if(targetIdent!=null) {
val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
} else {
if (vmDt == IRDataType.FLOAT) {
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
}
if (targetDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
else
IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
}
result += IRCodeChunk(null, null).also { it += instruction }
return result
@ -214,70 +228,106 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) {
// there's no STOREZIX instruction
valueRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, value=0)
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
}
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
result += code
return result
}
val fixedIndex = constIntValue(targetArray.index)
val iterable = codeGen.symbolTable.flat.getValue(targetArray.variable.name) as StStaticVariable
val arrayLength = iterable.length!!
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
}
} else {
if(vmDt== IRDataType.FLOAT) {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
}
} else {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
}
}
}
return result
}
else if(targetMemory!=null) {
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
if(zero) {
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(targetMemory.address as PtNumber).number.toInt()) }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
}
} else {
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, value=(targetMemory.address as PtNumber).number.toInt()) }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
}
}
@ -288,10 +338,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also return\ed.
// returns the code to load the Index into the register, which is also returned.
val result = mutableListOf<IRCodeChunkBase>()
if(itemsize==1) {
if(itemsize==1 || array.splitWords) {
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
@ -300,7 +350,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(codeGen.options.useNewExprCode) {
val tr = expressionEval.translateExpression(array.index)
result += tr.chunks
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, value = itemsize), null)
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, immediate = itemsize), null)
return Pair(result, tr.resultReg)
} else {
val mult: PtExpression

View File

@ -4,6 +4,8 @@ import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.SignedDatatypes
import prog8.code.core.SplitWordArrayTypes
import prog8.intermediate.*
@ -13,12 +15,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return when(call.name) {
"any" -> funcAny(call)
"all" -> funcAll(call)
"abs" -> funcAbs(call)
"abs__byte", "abs__word", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call)
"sqrt16" -> funcSqrt16(call)
"divmod" -> funcDivmod(call, IRDataType.BYTE)
"divmodw" -> funcDivmod(call, IRDataType.WORD)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"pop" -> funcPop(call)
"popw" -> funcPopw(call)
"push" -> funcPush(call)
@ -37,6 +39,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"pokew" -> funcPokeW(call)
"pokemon" -> ExpressionCodeResult.EMPTY // easter egg function
"mkword" -> funcMkword(call)
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
"sort" -> funcSort(call)
"reverse" -> funcReverse(call)
"rol" -> funcRolRor(Opcode.ROXL, call)
@ -52,20 +57,28 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
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, value=divident.number.toInt()), null)
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: division in r0, remainder in r1
result += assignRegisterTo(call.args[2], 0)
result += assignRegisterTo(call.args[3], 1)
// 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)
}
@ -73,12 +86,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
val left = exprGen.translateExpression(call.args[0])
val right = exprGen.translateExpression(call.args[1])
val targetReg = codeGen.registers.nextFree()
addToResult(result, left, 65500, -1)
addToResult(result, right, 65501, -1)
addInstr(result, IRInstruction(Opcode.SYSCALL, value=IMSyscall.COMPARE_STRINGS.number), null)
addInstr(result, IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=targetReg, reg2=0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, targetReg, -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 {
@ -87,7 +98,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val dt = codeGen.irType(call.args[0].type)
val dt = irType(call.args[0].type)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMP, dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
}
@ -108,14 +119,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
// SysCall call convention: return value in register r0
if(tr.resultReg!=0)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
@ -133,14 +140,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
// SysCall call convention: return value in register r0
if(tr.resultReg!=0)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
@ -153,19 +156,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
when (sourceDt) {
DataType.UBYTE -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, immediate = 0x80)
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=tr.resultReg)
}
@ -177,20 +174,25 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, immediate = 0x8000)
it += IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
}
result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
}
else -> throw AssemblyError("weird type")
DataType.FLOAT -> {
val resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
else -> throw AssemblyError("weird dt")
}
}
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val vmDt = codeGen.irType(call.type)
val vmDt = irType(call.type)
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
@ -200,15 +202,37 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, vmDt, resultReg, -1)
}
private fun funcSqrt16(call: PtBuiltinFunctionCall): ExpressionCodeResult {
private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
val dt = call.args[0].type
when(dt) {
DataType.UBYTE -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
DataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
DataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg)
val resultFpReg = codeGen.registers.nextFreeFloat()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
}
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
else -> throw AssemblyError("invalid dt for sqrt")
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -257,15 +281,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw IllegalArgumentException("weird type to reverse")
}
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
@ -280,15 +304,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_W -> IMSyscall.SORT_WORD
DataType.STR -> IMSyscall.SORT_UBYTE
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw IllegalArgumentException("weird type to sort")
}
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
@ -304,13 +328,87 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, IRDataType.WORD, lsbTr.resultReg, -1)
}
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val type = irType(call.type)
val valueTr = exprGen.translateExpression(call.args[0])
val minimumTr = exprGen.translateExpression(call.args[1])
val maximumTr = exprGen.translateExpression(call.args[2])
result += valueTr.chunks
result += minimumTr.chunks
result += maximumTr.chunks
if(type==IRDataType.FLOAT) {
result += codeGen.makeSyscall(
IMSyscall.CLAMP_FLOAT, listOf(
valueTr.dt to valueTr.resultFpReg,
minimumTr.dt to minimumTr.resultFpReg,
maximumTr.dt to maximumTr.resultFpReg,
), type to valueTr.resultFpReg
)
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
} else {
val syscall = when(call.type) {
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE
DataType.BYTE -> IMSyscall.CLAMP_BYTE
DataType.UWORD -> IMSyscall.CLAMP_UWORD
DataType.WORD -> IMSyscall.CLAMP_WORD
else -> throw AssemblyError("invalid dt")
}
result += codeGen.makeSyscall(syscall, listOf(
valueTr.dt to valueTr.resultReg,
minimumTr.dt to minimumTr.resultReg,
maximumTr.dt to maximumTr.resultReg,
), type to valueTr.resultReg
)
return ExpressionCodeResult(result, type, valueTr.resultReg, -1)
}
}
private fun funcMin(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val type = irType(call.type)
val result = mutableListOf<IRCodeChunkBase>()
val leftTr = exprGen.translateExpression(call.args[0])
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
// right <= left, take right
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
}
result += IRCodeChunk(after, null)
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
}
private fun funcMax(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val type = irType(call.type)
val result = mutableListOf<IRCodeChunkBase>()
val leftTr = exprGen.translateExpression(call.args[0])
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
// right >= left, take right
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
}
result += IRCodeChunk(after, null)
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
}
private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, address = address)
}
} else {
val tr = exprGen.translateExpression(call.args[0])
@ -325,7 +423,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[1])
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, value = address)
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, address = address)
}
} else {
val addressTr = exprGen.translateExpression(call.args[0])
@ -346,7 +444,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address)
}
} else {
val tr = exprGen.translateExpression(call.args[0])
@ -361,7 +459,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[1])
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, value = address)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address)
}
} else {
val addressTr = exprGen.translateExpression(call.args[0])
@ -382,7 +480,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val resultRegister = codeGen.registers.nextFree()
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, address = address)
}
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
@ -402,7 +500,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val resultRegister = codeGen.registers.nextFree()
val address = (call.args[0] as PtNumber).number.toInt()
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 {
@ -442,7 +540,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
val vmDt = codeGen.irType(call.args[0].type)
val vmDt = irType(call.args[0].type)
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)

View File

@ -28,24 +28,24 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
return when (expr) {
is PtMachineRegister -> {
ExpressionCodeResult(emptyList(), codeGen.irType(expr.type), expr.register, -1)
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
}
is PtNumber -> {
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null)
if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat()
code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, immediateFp = expr.number.toFloat())
ExpressionCodeResult(code, vmDt,-1, resultFpRegister)
}
else {
val resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, value = expr.number.toInt())
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = expr.number.toInt())
ExpressionCodeResult(code, vmDt, resultRegister, -1)
}
}
is PtIdentifier -> {
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) {
if(vmDt==IRDataType.FLOAT) {
@ -66,7 +66,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
is PtAddressOf -> {
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
val symbol = expr.identifier.name
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
val code = IRCodeChunk(null, null)
@ -79,7 +79,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(expr.address is PtNumber) {
val address = (expr.address as PtNumber).number.toInt()
val resultRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, value = address), null)
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, address = address), null)
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
val tr = translateExpression(expr.address)
@ -105,48 +105,35 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
var tr = translateExpression(check.element)
addToResult(result, tr, tr.resultReg, -1)
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable
when(iterable.dt) {
DataType.STR -> {
tr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1)
tr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.STRING_CONTAINS.number)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
tr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1)
tr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=SyscallRegisterBase+2, value = iterable.length!!)
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.BYTEARRAY_CONTAINS.number)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
}
// SysCall call convention: return value in register r0
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
tr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1)
tr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=SyscallRegisterBase+2, value = iterable.length!!)
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.WORDARRAY_CONTAINS.number)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}")
@ -155,7 +142,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val vmDt = codeGen.irType(arrayIx.type)
val vmDt = irType(arrayIx.type)
val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name
@ -173,6 +160,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
var resultRegister = -1
if(arrayIx.splitWords) {
require(vmDt==IRDataType.WORD)
val iterable = codeGen.symbolTable.flat.getValue(arrayIx.variable.name) as StStaticVariable
val arrayLength = iterable.length!!
resultRegister = codeGen.registers.nextFree()
if(arrayIx.index is PtNumber) {
val memOffset = (arrayIx.index as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
val tmpRegMsb = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb+$memOffset")
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb+$memOffset")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb)
}
} else {
val tr = translateExpression(arrayIx.index)
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
val tmpRegMsb = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb)
}
}
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
}
var resultFpRegister = -1
if(arrayIx.index is PtNumber) {
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()
@ -205,7 +219,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(expr.value)
addToResult(result, tr, tr.resultReg, tr.resultFpReg)
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
when(expr.operator) {
"+" -> { }
"-" -> {
@ -216,7 +230,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
"~" -> {
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, value = mask), null)
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, immediate = mask), null)
}
else -> throw AssemblyError("weird prefix operator")
}
@ -321,12 +335,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else -> throw AssemblyError("weird cast type")
}
return ExpressionCodeResult(result, codeGen.irType(cast.type), actualResultReg2, actualResultFpReg2)
return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)
}
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
require(!codeGen.options.useNewExprCode)
val vmDt = codeGen.irType(binExpr.left.type)
val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
return when(binExpr.operator) {
"+" -> operatorPlus(binExpr, vmDt)
@ -353,87 +367,82 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
is StSub -> {
val result = mutableListOf<IRCodeChunkBase>()
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.irType(parameter.type)
val symbol = "${fcall.name}.${parameter.name}"
if(codeGen.isZero(arg)) {
addInstr(result, IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol), null)
} else {
if (paramDt == IRDataType.FLOAT) {
val tr = translateExpression(arg)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol), null)
} else {
val tr = translateExpression(arg)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1 = tr.resultReg, labelSymbol = symbol), null)
}
}
val paramDt = irType(parameter.type)
val tr = translateExpression(arg)
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
result += tr.chunks
}
return if(fcall.void) {
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name), null)
// return value
val returnRegSpec = if(fcall.void) null else {
val returnIrType = irType(callTarget.returnType!!)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), null)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null)
}
// create the call
val call = IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
addInstr(result, call, null)
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} else {
var resultReg = -1
var resultFpReg = -1
if(fcall.type==DataType.FLOAT) {
resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.CALLRVAL, IRDataType.FLOAT, fpReg1=resultFpReg, labelSymbol=fcall.name), null)
} else {
resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.CALLRVAL, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null)
}
ExpressionCodeResult(result, codeGen.irType(fcall.type), resultReg, resultFpReg)
}
else if(fcall.type==DataType.FLOAT)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
}
is StRomSub -> {
val result = mutableListOf<IRCodeChunkBase>()
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.irType(parameter.type)
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
if(codeGen.isZero(arg)) {
addInstr(result, IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr), null)
val paramDt = irType(parameter.type)
val tr = translateExpression(arg)
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, parameter.register)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
result += tr.chunks
}
// return value
val returnRegSpec = if(fcall.void) null else {
if(callTarget.returns.isEmpty())
null
else if(callTarget.returns.size==1) {
val returns = callTarget.returns[0]
val returnIrType = irType(returns.type)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
} else {
if (paramDt == IRDataType.FLOAT)
throw AssemblyError("doesn't support float register argument in asm romsub")
val tr = translateExpression(arg)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.STORECPU, paramDt, reg1 = tr.resultReg, labelSymbol = paramRegStr), null)
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
val returns = callTarget.returns.first { it.register.registerOrPair!=null }
val returnIrType = irType(returns.type)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
}
}
// just a regular call without using Vm register call convention: the value is returned in CPU registers!
addInstr(result, IRInstruction(Opcode.CALL, value=callTarget.address.toInt()), null)
val resultReg = codeGen.registers.nextFree()
if(!fcall.void) {
when(callTarget.returns.size) {
0 -> throw AssemblyError("expect a return value")
1 -> {
if(fcall.type==DataType.FLOAT)
throw AssemblyError("doesn't support float register result in asm romsub")
val returns = callTarget.returns.single()
val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
}
else -> {
val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null }
if(returnRegister!=null) {
// we skip the other values returned in the status flags.
val regStr = returnRegister.register.registerOrPair.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
} else {
val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null }
if(firstReturnRegister!=null) {
// we just take the first register return value and ignore the rest.
val regStr = firstReturnRegister.register.registerOrPair.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
} else {
throw AssemblyError("invalid number of return values from call")
}
}
}
}
}
return ExpressionCodeResult(result, if(fcall.void) IRDataType.BYTE else codeGen.irType(fcall.type), resultReg, -1)
// create the call
val call =
if(callTarget.address==null)
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
else
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
addInstr(result, call, null)
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
}
else -> throw AssemblyError("invalid node type")
}
@ -454,7 +463,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val resultRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
val zeroRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, value=0), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, immediate = 0), null)
val ins = if (signed) {
if (greaterEquals) Opcode.SGES else Opcode.SGTS
} else {
@ -464,21 +473,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, SyscallRegisterBase, -1)
val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, SyscallRegisterBase+1, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number)
// SysCall call convention: return value in register r0
val zeroReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = zeroReg, value = 0)
it += if (greaterEquals)
IRInstruction(Opcode.SGES, IRDataType.BYTE, reg1 = leftTr.resultReg, reg2 = zeroReg)
else
IRInstruction(Opcode.SGTS, IRDataType.BYTE, reg1 = leftTr.resultReg, reg2 = zeroReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
@ -510,7 +505,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val resultRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
val zeroRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, value=0), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, immediate = 0), null)
val ins = if (signed) {
if (lessEquals) Opcode.SLES else Opcode.SLTS
} else {
@ -520,23 +515,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, SyscallRegisterBase, -1)
val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, SyscallRegisterBase+1, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number)
// SysCall call convention: return value in register r0
val zeroReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = zeroReg, value = 0)
it += if (lessEquals)
IRInstruction(Opcode.SLES, IRDataType.BYTE, reg1 = 0, reg2 = zeroReg)
else
IRInstruction(Opcode.SLTS, IRDataType.BYTE, reg1 = 0, reg2 = zeroReg)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
@ -563,32 +542,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val resultRegister = codeGen.registers.nextFree()
val valueReg = codeGen.registers.nextFree()
val label = codeGen.createLabelName()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, value=1), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 1), null)
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
if (notEquals) {
addInstr(result, IRInstruction(Opcode.BNZ, IRDataType.BYTE, reg1=valueReg, labelSymbol = label), null)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=valueReg, immediate = 0, labelSymbol = label), null)
} else {
addInstr(result, IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=valueReg, labelSymbol = label), null)
addInstr(result, IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=valueReg, immediate = 0, labelSymbol = label), null)
}
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, value=0), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0), null)
result += IRCodeChunk(label, null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, SyscallRegisterBase, -1)
val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, SyscallRegisterBase+1, -1)
val resultRegister = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number)
// SysCall call convention: return value in register r0
if (!notEquals)
it += IRInstruction(Opcode.INV, vmDt, reg1 = 0)
it += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=0)
it += IRInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value = 1)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
return if(constValue(binExpr.right)==0.0) {
val tr = translateExpression(binExpr.left)
@ -651,7 +617,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
@ -668,7 +634,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.AND, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
addInstr(result, IRInstruction(Opcode.AND, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
@ -685,7 +651,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.OR, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
addInstr(result, IRInstruction(Opcode.OR, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
@ -703,7 +669,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.MOD, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
addInstr(result, IRInstruction(Opcode.MOD, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
@ -749,9 +715,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
addInstr(result, if (signed)
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, value=(binExpr.right as PtNumber).number.toInt())
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
else
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, value=(binExpr.right as PtNumber).number.toInt())
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
, null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
} else {
@ -832,7 +798,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.SUB, vmDt, fpReg1 = tr.resultFpReg, fpValue = (binExpr.right as PtNumber).number.toFloat()), null)
addInstr(result, IRInstruction(Opcode.SUB, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number.toFloat()), null)
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else {
val leftTr = translateExpression(binExpr.left)
@ -852,9 +818,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
else {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left,)
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.SUB, vmDt, reg1 = tr.resultReg, value = (binExpr.right as PtNumber).number.toInt()), null)
addInstr(result, IRInstruction(Opcode.SUB, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
@ -887,7 +853,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.ADD, vmDt, fpReg1 = tr.resultFpReg, fpValue = (binExpr.right as PtNumber).number.toFloat()), null)
addInstr(result, IRInstruction(Opcode.ADD, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number.toFloat()), null)
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else {
val leftTr = translateExpression(binExpr.left)
@ -915,7 +881,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.ADD, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
addInstr(result, IRInstruction(Opcode.ADD, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
@ -935,7 +901,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, value=knownAddress)
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
@ -947,7 +913,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, value = knownAddress)
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
@ -966,13 +932,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, -1, tr.resultFpReg)
val ins = if(signed) {
if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
else
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
else
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
}
@ -987,13 +953,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, tr.resultReg, -1)
val ins = if(signed) {
if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, value = knownAddress)
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, value = knownAddress)
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
}
@ -1014,7 +980,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
else
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
, null)
@ -1027,7 +993,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, value = knownAddress)
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
@ -1041,7 +1007,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
IRInstruction(Opcode.DECM, vmDt, address = knownAddress)
else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null)
@ -1050,7 +1016,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, value=knownAddress)
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, address = knownAddress)
else
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null)
@ -1058,7 +1024,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
IRInstruction(Opcode.DECM, vmDt, address = knownAddress)
else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null)
@ -1067,7 +1033,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, value = knownAddress)
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
@ -1081,7 +1047,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
IRInstruction(Opcode.INCM, vmDt, address = knownAddress)
else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null)
@ -1090,7 +1056,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, value = knownAddress)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = knownAddress)
else
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null)
@ -1098,7 +1064,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
IRInstruction(Opcode.INCM, vmDt, address = knownAddress)
else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null)
@ -1107,7 +1073,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, value=knownAddress)
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
@ -1121,7 +1087,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(codeGen.isOne(operand)) {
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, value=knownAddress)
IRInstruction(opc, vmDt, address = knownAddress)
else
IRInstruction(opc, vmDt, labelSymbol = symbol)
addInstr(result, ins, null)
@ -1130,7 +1096,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, tr.resultReg, -1)
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, reg1 = tr.resultReg, value=knownAddress)
IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = knownAddress)
else
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
addInstr(result, ins, null)
@ -1142,7 +1108,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isOne(operand)){
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLM, vmDt, value=knownAddress)
IRInstruction(Opcode.LSLM, vmDt, address = knownAddress)
else
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
, null)
@ -1150,7 +1116,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, value=knownAddress)
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
@ -1163,7 +1129,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, value = knownAddress)
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, address = knownAddress)
else
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null)
@ -1178,15 +1144,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) {
// @(address) = @(address) %= operand
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, value = knownAddress)
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, value = number)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, value = knownAddress)
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, address = knownAddress)
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, immediate = number)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, address = knownAddress)
}
} else {
// symbol = symbol %= operand
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, labelSymbol = symbol)
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, value = number)
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, immediate = number)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, labelSymbol = symbol)
}
}
@ -1196,9 +1162,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) {
// @(address) = @(address) %= operand
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, value = knownAddress)
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, address = knownAddress)
it += IRInstruction(Opcode.MODR, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, value = knownAddress)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, address = knownAddress)
}
} else {
// symbol = symbol %= operand
@ -1279,16 +1245,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, value = knownAddress)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, value=operand.number.toInt())
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, value = knownAddress)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, value=operand.number.toInt())
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol)
}
@ -1299,9 +1265,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, value = knownAddress)
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, value = knownAddress)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
}
} else {
// in-place modify a symbol (variable)
@ -1330,21 +1296,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, value = knownAddress)
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, fpValue = operand.number.toFloat())
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, value=knownAddress)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, fpValue = operand.number.toFloat())
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
@ -1356,19 +1322,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, value = knownAddress)
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, value=knownAddress)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)

View File

@ -1,12 +1,44 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize(optimizationsEnabled: Boolean, errors: IErrorReporter) {
if(!optimizationsEnabled)
return optimizeOnlyJoinChunks()
peepholeOptimize()
val remover = IRUnusedCodeRemover(irprog, errors)
var totalRemovals = 0
do {
val numRemoved = remover.optimize()
totalRemovals += numRemoved
} while(numRemoved>0 && errors.noErrors())
errors.report()
if(totalRemovals>0) {
irprog.linkChunks() // re-link again.
}
}
private fun optimizeOnlyJoinChunks() {
irprog.foreachSub { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
}
irprog.linkChunks() // re-link
}
private fun peepholeOptimize() {
irprog.foreachSub { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
// TODO also do register optimization step here?
sub.chunks.withIndex().forEach { (index, chunk1) ->
// we don't optimize Inline Asm chunks here.
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
@ -28,7 +60,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
removeEmptyChunks(sub)
}
irprog.linkChunks() // re-link
irprog.linkChunks() // re-link
}
private fun removeEmptyChunks(sub: IRSubroutine) {
@ -38,7 +70,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
/*
Empty Code chunk with label ->
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.
Empty Code chunk without label ->
should not have been generated! ERROR.
@ -63,7 +95,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
if (chunk.label == nextchunk.label)
removeChunks += index
else {
// TODO: consolidate labels on same chunk
// TODO: merge labels on same chunk
}
}
}
@ -85,7 +117,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(sub.chunks.isEmpty())
return
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null)
return false
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
@ -102,12 +134,39 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[0]
for(ix in 1 until sub.chunks.size) {
val lastChunk = chunks.last()
if(mayJoin(lastChunk, sub.chunks[ix])) {
lastChunk.instructions += sub.chunks[ix].instructions
lastChunk.next = sub.chunks[ix].next
val candidate = sub.chunks[ix]
when(candidate) {
is IRCodeChunk -> {
if(mayJoinCodeChunks(lastChunk, candidate)) {
lastChunk.instructions += candidate.instructions
lastChunk.next = candidate.next
}
else
chunks += candidate
}
is IRInlineAsmChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
else
chunks += candidate
}
}
is IRInlineBinaryChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
else
chunks += candidate
}
}
}
else
chunks += sub.chunks[ix]
}
sub.chunks.clear()
sub.chunks += chunks
@ -176,7 +235,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
// remove useless RETURN
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNREG)) {
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) {
val previous = chunk.instructions[idx-1]
if(previous.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx)
@ -194,14 +253,16 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
// replace call + return --> jump
if(idx>0 && ins.opcode==Opcode.RETURN) {
val previous = chunk.instructions[idx-1]
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLRVAL) {
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, value=previous.value, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
chunk.instructions.removeAt(idx)
changed = true
}
}
// This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
// If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
// if(idx>0 && ins.opcode==Opcode.RETURN) {
// val previous = chunk.instructions[idx-1]
// if(previous.opcode==Opcode.CALL) {
// chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
// chunk.instructions.removeAt(idx)
// changed = true
// }
// }
}
return changed
}
@ -212,47 +273,47 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
indexedInstructions.reversed().forEach { (idx, ins) ->
when (ins.opcode) {
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
if (ins.value == 1) {
if (ins.immediate == 1) {
chunk.instructions.removeAt(idx)
changed = true
}
}
Opcode.ADD, Opcode.SUB -> {
if (ins.value == 1) {
if (ins.immediate == 1) {
chunk.instructions[idx] = IRInstruction(
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
ins.type,
ins.reg1
)
changed = true
} else if (ins.value == 0) {
} else if (ins.immediate == 0) {
chunk.instructions.removeAt(idx)
changed = true
}
}
Opcode.AND -> {
if (ins.value == 0) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
if (ins.immediate == 0) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
changed = true
} else if (ins.value == 255 && ins.type == IRDataType.BYTE) {
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
chunk.instructions.removeAt(idx)
changed = true
} else if (ins.value == 65535 && ins.type == IRDataType.WORD) {
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
chunk.instructions.removeAt(idx)
changed = true
}
}
Opcode.OR -> {
if (ins.value == 0) {
if (ins.immediate == 0) {
chunk.instructions.removeAt(idx)
changed = true
} else if ((ins.value == 255 && ins.type == IRDataType.BYTE) || (ins.value == 65535 && ins.type == IRDataType.WORD)) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
} 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, immediate = ins.immediate)
changed = true
}
}
Opcode.XOR -> {
if (ins.value == 0) {
if (ins.immediate == 0) {
chunk.instructions.removeAt(idx)
changed = true
}

View File

@ -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)
}
*/
}

View File

@ -5,35 +5,18 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
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 {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
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++
}
}
}
var numRemoved = removeUnusedSubroutines() + removeUnusedAsmSubroutines()
// remove empty blocks
irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) {
irprog.blocks.remove(block)
irprog.st.removeTree(block.label)
numRemoved++
}
}
@ -41,8 +24,102 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
return numRemoved
}
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 {
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())
fun grow() {
@ -73,29 +150,27 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
sub.chunks.forEach { chunk ->
chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach {
if(it.branchTarget==null) {
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
} else {
linkedChunks += it.branchTarget!!
}
irprog.foreachCodeChunk { chunk ->
chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach {
if(it.branchTarget==null) {
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
} else {
linkedChunks += it.branchTarget!!
}
if (chunk.label == "main.start")
linkedChunks += chunk
}
if (chunk.label == "main.start")
linkedChunks += chunk
}
return removeUnlinkedChunks(linkedChunks)
}
private fun removeUnlinkedChunks(
linkedChunks: MutableSet<IRCodeChunkBase>
linkedChunks: Set<IRCodeChunkBase>
): Int {
var numRemoved = 0
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
irprog.foreachSub { sub ->
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
if (chunk !in linkedChunks) {
if (chunk === sub.chunks[0]) {

View File

@ -1,11 +1,7 @@
package prog8.codegen.intermediate
import prog8.code.core.AssemblyError
import prog8.intermediate.SyscallRegisterBase
internal class RegisterPool {
// reserve 0,1,2 for return values of subroutine calls and syscalls
// TODO set this back to 0 once 'resultRegister' has been removed everywhere and SYSCALL/DIVMOD fixed?
// reserve 0,1,2 for return values of subroutine calls and syscalls in IR assembly code
private var firstFree: Int=3
private var firstFreeFloat: Int=3
@ -15,16 +11,12 @@ internal class RegisterPool {
fun nextFree(): Int {
val result = firstFree
firstFree++
if(firstFree >= SyscallRegisterBase)
throw AssemblyError("out of virtual registers (int)")
return result
}
fun nextFreeFloat(): Int {
val result = firstFreeFloat
firstFreeFloat++
if(firstFreeFloat >= SyscallRegisterBase)
throw AssemblyError("out of virtual registers (fp)")
return result
}
}

View File

@ -2,7 +2,10 @@ package prog8.codegen.vm
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.*
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram

View File

@ -8,7 +8,7 @@ import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start")
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
chunks.forEach { sub += it }
block += sub
@ -36,17 +36,17 @@ class TestIRPeepholeOpt: FunSpec({
return makeIRProgram(listOf(chunk))
}
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.allSubs().flatMap { it.chunks }.toList()
test("remove nops") {
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=42),
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, immediate=42),
IRInstruction(Opcode.NOP),
IRInstruction(Opcode.NOP)
))
irProg.chunks().single().instructions.size shouldBe 3
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 1
}
@ -65,7 +65,7 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().size shouldBe 4
irProg.chunks()[0].label shouldBe "main.start"
irProg.chunks()[1].label shouldBe "label"
@ -92,7 +92,7 @@ class TestIRPeepholeOpt: FunSpec({
))
irProg.chunks().single().instructions.size shouldBe 6
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.CLC
@ -107,7 +107,7 @@ class TestIRPeepholeOpt: FunSpec({
))
irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.LOADR
@ -117,31 +117,31 @@ class TestIRPeepholeOpt: FunSpec({
test("remove useless div/mul, add/sub") {
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 0)
))
irProg.chunks().single().instructions.size shouldBe 10
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4
}
test("replace add/sub 1 by inc/dec") {
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 1)
))
irProg.chunks().single().instructions.size shouldBe 2
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.INC
@ -150,40 +150,40 @@ class TestIRPeepholeOpt: FunSpec({
test("remove useless and/or/xor") {
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 255),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 65535),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 200),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 60000),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 255),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 65535),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 200),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 60000),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 1)
))
irProg.chunks().single().instructions.size shouldBe 8
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4
}
test("replace and/or/xor by constant number") {
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 0),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255),
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 0),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 255),
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535)
))
irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 4
instr[0].opcode shouldBe Opcode.LOAD
instr[1].opcode shouldBe Opcode.LOAD
instr[2].opcode shouldBe Opcode.LOAD
instr[3].opcode shouldBe Opcode.LOAD
instr[0].value shouldBe 0
instr[1].value shouldBe 0
instr[2].value shouldBe 255
instr[3].value shouldBe 65535
instr[0].immediate shouldBe 0
instr[1].immediate shouldBe 0
instr[2].immediate shouldBe 255
instr[3].immediate shouldBe 65535
}
})

View File

@ -1,5 +1,6 @@
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
@ -7,6 +8,7 @@ import prog8.code.target.VMTarget
import prog8.codegen.vm.VmAssemblyProgram
import prog8.codegen.vm.VmCodeGen
import prog8.intermediate.IRSubroutine
import prog8.intermediate.Opcode
class TestVmCodeGen: FunSpec({
@ -98,7 +100,7 @@ class TestVmCodeGen: FunSpec({
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
irChunks.size shouldBe 1
}
test("float comparison expressions against zero") {
@ -267,7 +269,7 @@ class TestVmCodeGen: FunSpec({
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
irChunks.size shouldBe 1
}
test("integer comparison expressions against zero") {
@ -436,7 +438,36 @@ class TestVmCodeGen: FunSpec({
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
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, 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
}
})

View File

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

View File

@ -170,7 +170,7 @@ class StatementOptimizer(private val program: Program,
if(iterationCount!=null) {
val loopName = forLoop.loopVar.nameInSource
if(!forLoop.iterable.referencesIdentifier(loopName) && !forLoop.body.referencesIdentifier(loopName)) {
errors.warn("for loop can be replaced with repeat loop, possibly also remove the loop variable", forLoop.position)
errors.warn("for loop can be replaced with repeat loop", forLoop.position)
val repeat = RepeatLoop(NumericLiteral.optimalNumeric(iterationCount, forLoop.position), forLoop.body, forLoop.position)
return listOf(IAstModification.ReplaceNode(forLoop, repeat, parent))
}

View File

@ -157,6 +157,12 @@ class UnusedCodeRemover(private val program: Program,
return noModifications
}
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(assignment.target isSameAs assignment.value)
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
return noModifications
}
private fun deduplicateAssignments(statements: List<Statement>, scope: IStatementContainer): List<IAstModification> {
// removes 'duplicate' assignments that assign the same target directly after another, unless it is a function call
val linesToRemove = mutableListOf<Assignment>()

View File

@ -38,10 +38,10 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
testImplementation project(':intermediate')
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
}
configurations.all {

View File

@ -7,31 +7,6 @@ atari {
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
; ---- kernal routines ----
; TODO
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; TODO
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc
clv
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the Atari
}}
}
}
@ -40,6 +15,26 @@ sys {
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; TODO
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc
clv
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the Atari
}}
}
asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt.
@ -188,6 +183,19 @@ _longcopy
}}
}
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
; TODO

View File

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

View File

@ -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_W1,
; 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

View File

@ -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
romsub $af27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 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 TODO so.... not on c128 !?
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 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 = (s8).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 {{
lda #0
jmp GIVAYF
}}
}
&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"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
; }
}

View File

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

View File

@ -5,7 +5,6 @@
floats {
; ---- 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.
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586

View File

@ -84,7 +84,7 @@ gfx2 {
else -> {
; back to default text mode
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
c64.CINT()
cbm.CINT()
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
width = 0
height = 0
@ -155,6 +155,9 @@ gfx2 {
}
sub horizontal_line(uword x, uword y, uword length, ubyte color) {
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
ubyte[9] masked_starts = [ 0, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111]
if length==0
return
when active_mode {
@ -163,12 +166,13 @@ gfx2 {
ubyte separate_pixels = (8-lsb(x)) & 7
if separate_pixels as uword > length
separate_pixels = lsb(length)
repeat separate_pixels {
; TODO optimize this by writing a masked byte in 1 go
plot(x, y, color)
x++
if separate_pixels {
position(x,y)
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_starts[separate_pixels]
length -= separate_pixels
x += separate_pixels
}
length -= separate_pixels
if length {
position(x, y)
separate_pixels = lsb(length) & 7
@ -205,11 +209,9 @@ _loop lda length
bra _loop
_done
}}
repeat separate_pixels {
; TODO optimize this by writing a masked byte in 1 go
plot(x, y, color)
x++
}
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_ends[separate_pixels]
}
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
}
@ -366,8 +368,14 @@ _done
return
position2(x,y,true)
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3] ; TODO with lookup table
;; color &= 3
;; color <<= gfx2.plot.shift4c[lsb(x) & 3]
cx16.r2L = lsb(x) & 3
when color & 3 {
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
}
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
repeat lheight {
%asm {{
@ -547,10 +555,13 @@ _done
}
}
sub plot(uword @zp x, uword y, ubyte color) {
sub plot(uword @zp x, uword @zp y, ubyte @zp color) {
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
ubyte[4] @shared shift4c = [6,4,2,0]
ubyte[4] shiftedleft_4c_1 = [1<<6, 1<<4, 1<<2, 1<<0]
ubyte[4] shiftedleft_4c_2 = [2<<6, 2<<4, 2<<2, 2<<0]
ubyte[4] shiftedleft_4c_3 = [3<<6, 3<<4, 3<<2, 3<<0]
when active_mode {
1 -> {
@ -643,8 +654,13 @@ _done
; TODO also mostly usable for lores 4c?
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = lsb(x) & 3 ; xbits
color &= 3
color <<= shift4c[cx16.r2L] ; TODO with lookup table
; color &= 3
; color <<= shift4c[cx16.r2L]
when color & 3 {
1 -> color = shiftedleft_4c_1[cx16.r2L]
2 -> color = shiftedleft_4c_2[cx16.r2L]
3 -> color = shiftedleft_4c_3[cx16.r2L]
}
%asm {{
stz cx16.VERA_CTRL
lda cx16.r1L
@ -743,13 +759,125 @@ _done
sta cx16.r0L
}}
cx16.r1L = lsb(x) & 3
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L] ; TODO with lookup table
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L]
return cx16.r0L & 3
}
else -> return 0
}
}
sub fill(word @zp x, word @zp y, ubyte new_color) {
; Non-recursive scanline flood fill.
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
const ubyte MAXDEPTH = 48
word[MAXDEPTH] @split @shared stack_xl
word[MAXDEPTH] @split @shared stack_xr
word[MAXDEPTH] @split @shared stack_y
byte[MAXDEPTH] @shared stack_dy
cx16.r12L = 0 ; stack pointer
word x1
word x2
byte dy
cx16.r10L = new_color
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
if cx16.r12L==MAXDEPTH
return
cx16.r0s = sy+sdy
if cx16.r0s>=0 and cx16.r0s<=height-1 {
;; stack_xl[cx16.r12L] = sxl
;; stack_xr[cx16.r12L] = sxr
;; stack_y[cx16.r12L] = sy
;; stack_dy[cx16.r12L] = sdy
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda sxl
sta stack_xl_lsb,y
lda sxl+1
sta stack_xl_msb,y
lda sxr
sta stack_xr_lsb,y
lda sxr+1
sta stack_xr_msb,y
lda sy
sta stack_y_lsb,y
lda sy+1
sta stack_y_msb,y
ldy cx16.r12L
lda sdy
sta stack_dy,y
inc cx16.r12L
}}
}
}
sub pop_stack() {
;; cx16.r12L--
;; x1 = stack_xl[cx16.r12L]
;; x2 = stack_xr[cx16.r12L]
;; y = stack_y[cx16.r12L]
;; dy = stack_dy[cx16.r12L]
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda stack_xl_lsb,y
sta x1
lda stack_xl_msb,y
sta x1+1
lda stack_xr_lsb,y
sta x2
lda stack_xr_msb,y
sta x2+1
lda stack_y_lsb,y
sta y
lda stack_y_msb,y
sta y+1
ldy cx16.r12L
lda stack_dy,y
sta dy
}}
y+=dy
}
cx16.r11L = pget(x as uword, y as uword) ; old_color
if cx16.r11L == cx16.r10L
return
if x<0 or x > width-1 or y<0 or y > height-1
return
push_stack(x, x, y, 1)
push_stack(x, x, y + 1, -1)
word left = 0
while cx16.r12L {
pop_stack()
x = x1
while x >= 0 and pget(x as uword, y as uword) == cx16.r11L {
plot(x as uword, y as uword, cx16.r10L)
x--
}
if x>= x1
goto skip
left = x + 1
if left < x1
push_stack(left, x1 - 1, y, -dy)
x = x1 + 1
do {
while x <= width-1 and pget(x as uword, y as uword) == cx16.r11L {
plot(x as uword, y as uword, cx16.r10L)
x++
}
push_stack(left, x - 1, y, dy)
if x > x2 + 1
push_stack(x2 + 1, x - 1, y, -dy)
skip:
x++
while x <= x2 and pget(x as uword, y as uword) != cx16.r11L
x++
left = x
} until x>x2
}
}
sub position(uword @zp x, uword y) {
ubyte bank
when active_mode {

View File

@ -22,6 +22,7 @@ psg {
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
envelope_states[voice_num] = 255
sys.irqsafe_set_irqd()
cx16.r0 = $f9c2 + voice_num * 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -32,6 +33,7 @@ psg {
cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume
sys.irqsafe_clear_irqd()
}
; sub freq_hz(ubyte voice_num, float hertz) {
@ -44,48 +46,50 @@ psg {
sub freq(ubyte voice_num, uword vera_freq) {
; -- 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.
; (https://github.com/commanderx16/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
cx16.r0 = $f9c0 + voice_num * 4
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
; Write freq MSB first and then LSB to reduce the chance on clicks
sys.irqsafe_set_irqd()
cx16.r0 = $f9c1 + voice_num * 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0)
cx16.VERA_ADDR_M = msb(cx16.r0)
cx16.VERA_ADDR_H = 1
cx16.VERA_DATA0 = lsb(vera_freq)
cx16.VERA_ADDR_L++
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) {
; -- Modifies the volume of this voice.
; 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)
cx16.vpoke_mask(1, $f9c2 + voice_num * 4, %11000000, vol)
envelope_maxvolumes[voice_num] = vol
}
sub pulse_width(ubyte voice_num, ubyte pw) {
; -- 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.
cx16.r0 = $f9c3 + voice_num * 4
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | pw)
cx16.vpoke_mask(1, $f9c3 + voice_num * 4, %11000000, pw)
}
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
; -- Enables AttackSustainRelease volume envelope for a voice.
; Note: this requires setting up envelopes_irq() as well, read its description.
; voice_num = 0-15 maxvolume = 0-63
; 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, sustain, release = 0-255 that determine the speed of the A/D/R:
; 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_attacks[voice_num] = attack
envelope_sustains[voice_num] = sustain
envelope_releases[voice_num] = release
if attack
attack = 0
else
attack = maxvolume ; max volume when no attack is set
envelope_volumes[voice_num] = mkword(attack, 0)
cx16.r0 = mkword(maxvolume, 0)
if cx16.r0<envelope_volumes[voice_num]
envelope_volumes[voice_num] = cx16.r0
envelope_maxvolumes[voice_num] = maxvolume
envelope_states[voice_num] = 0
}
@ -94,7 +98,6 @@ psg {
; -- Shut down all PSG voices.
for cx16.r1L in 0 to 15 {
envelope_states[cx16.r1L] = 255
envelope_volumes[cx16.r1L] = 0
volume(cx16.r1L, 0)
}
}
@ -104,11 +107,11 @@ psg {
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
; or just install this routine as the only irq handler if you don't have to do other things there.
; Example: cx16.set_irq(&psg.envelopes_irq, true)
; NOTE: this routine calls save/restore_vera_context() for you, don't nest this or call it yourself!
; cx16.r0 = the volume word (volume scaled by 256)
; cx16.r1L = the voice number
; cx16.r2L = attack value
pushw(cx16.r0)
push(cx16.r1L)
push(cx16.r2L)
@ -148,7 +151,7 @@ psg {
}
; 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_ADDR_L = $c2
cx16.VERA_ADDR_M = $f9
@ -160,7 +163,7 @@ psg {
for cx16.r1L in 0 to 15 {
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
}
cx16.pop_vera_context()
cx16.restore_vera_context()
popw(cx16.r9)
pop(cx16.r2L)
pop(cx16.r1L)
@ -168,7 +171,7 @@ psg {
}
ubyte[16] envelope_states
uword[16] envelope_volumes ; scaled by 256
uword[16] @split envelope_volumes ; scaled by 256
ubyte[16] envelope_attacks
ubyte[16] envelope_sustains
ubyte[16] envelope_releases

View File

@ -1,9 +1,9 @@
; Prog8 definitions for the CommanderX16
; Including memory registers, I/O registers, Basic and Kernal subroutines.
c64 {
cbm {
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
; Commodore (CBM) common variables, vectors and kernal routines
; STROUT --> use txt.print
; CLEARSCR -> use txt.clear_screen
@ -13,12 +13,12 @@ romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
@ -30,23 +30,23 @@ romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
; ---- utility
@ -55,7 +55,7 @@ asmsub STOP2() -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{
phx
jsr c64.STOP
jsr cbm.STOP
beq +
plx
lda #0
@ -67,10 +67,14 @@ asmsub STOP2() -> ubyte @A {
}
asmsub RDTIM16() -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs.
%asm {{
phx
jsr c64.RDTIM
php
sei
jsr cbm.RDTIM
plp
cli
pha
txa
tay
@ -85,37 +89,37 @@ asmsub RDTIM16() -> uword @AY {
cx16 {
; irq, system and hardware vectors:
&uword IERROR = $0300
&uword IMAIN = $0302
&uword ICRNCH = $0304
&uword IQPLOP = $0306
&uword IGONE = $0308
&uword IEVAL = $030a
&ubyte SAREG = $030c ; register storage for A for SYS calls
&ubyte SXREG = $030d ; register storage for X for SYS calls
&ubyte SYREG = $030e ; register storage for Y for SYS calls
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
&uword USRADD = $0311 ; vector for the USR() basic command
&uword IERROR = $0300
&uword IMAIN = $0302
&uword ICRNCH = $0304
&uword IQPLOP = $0306
&uword IGONE = $0308
&uword IEVAL = $030a
&ubyte SAREG = $030c ; register storage for A for SYS calls
&ubyte SXREG = $030d ; register storage for X for SYS calls
&ubyte SYREG = $030e ; register storage for Y for SYS calls
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
&uword USRADD = $0311 ; vector for the USR() basic command
; $0313 is unused.
&uword CINV = $0314 ; IRQ vector (in ram)
&uword CBINV = $0316 ; BRK vector (in ram)
&uword NMINV = $0318 ; NMI vector (in ram)
&uword IOPEN = $031a
&uword ICLOSE = $031c
&uword ICHKIN = $031e
&uword ICKOUT = $0320
&uword ICLRCH = $0322
&uword IBASIN = $0324
&uword IBSOUT = $0326
&uword ISTOP = $0328
&uword IGETIN = $032a
&uword ICLALL = $032c
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
&uword ILOAD = $0330
&uword ISAVE = $0332
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
&uword CINV = $0314 ; IRQ vector (in ram)
&uword CBINV = $0316 ; BRK vector (in ram)
&uword NMINV = $0318 ; NMI vector (in ram)
&uword IOPEN = $031a
&uword ICLOSE = $031c
&uword ICHKIN = $031e
&uword ICKOUT = $0320
&uword ICLRCH = $0322
&uword IBASIN = $0324
&uword IBSOUT = $0326
&uword ISTOP = $0328
&uword IGETIN = $032a
&uword ICLALL = $032c
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
&uword ILOAD = $0330
&uword ISAVE = $0332
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
@ -271,44 +275,44 @@ cx16 {
; I/O
const uword VIA1_BASE = $9f00 ;VIA 6522 #1
&ubyte via1prb = VIA1_BASE + 0
&ubyte via1pra = VIA1_BASE + 1
&ubyte via1ddrb = VIA1_BASE + 2
&ubyte via1ddra = VIA1_BASE + 3
&ubyte via1t1l = VIA1_BASE + 4
&ubyte via1t1h = VIA1_BASE + 5
&ubyte via1t1ll = VIA1_BASE + 6
&ubyte via1t1lh = VIA1_BASE + 7
&ubyte via1t2l = VIA1_BASE + 8
&ubyte via1t2h = VIA1_BASE + 9
&ubyte via1sr = VIA1_BASE + 10
&ubyte via1acr = VIA1_BASE + 11
&ubyte via1pcr = VIA1_BASE + 12
&ubyte via1ifr = VIA1_BASE + 13
&ubyte via1ier = VIA1_BASE + 14
&ubyte via1ora = VIA1_BASE + 15
&ubyte via1prb = VIA1_BASE + 0
&ubyte via1pra = VIA1_BASE + 1
&ubyte via1ddrb = VIA1_BASE + 2
&ubyte via1ddra = VIA1_BASE + 3
&ubyte via1t1l = VIA1_BASE + 4
&ubyte via1t1h = VIA1_BASE + 5
&ubyte via1t1ll = VIA1_BASE + 6
&ubyte via1t1lh = VIA1_BASE + 7
&ubyte via1t2l = VIA1_BASE + 8
&ubyte via1t2h = VIA1_BASE + 9
&ubyte via1sr = VIA1_BASE + 10
&ubyte via1acr = VIA1_BASE + 11
&ubyte via1pcr = VIA1_BASE + 12
&ubyte via1ifr = VIA1_BASE + 13
&ubyte via1ier = VIA1_BASE + 14
&ubyte via1ora = VIA1_BASE + 15
const uword VIA2_BASE = $9f10 ;VIA 6522 #2
&ubyte via2prb = VIA2_BASE + 0
&ubyte via2pra = VIA2_BASE + 1
&ubyte via2ddrb = VIA2_BASE + 2
&ubyte via2ddra = VIA2_BASE + 3
&ubyte via2t1l = VIA2_BASE + 4
&ubyte via2t1h = VIA2_BASE + 5
&ubyte via2t1ll = VIA2_BASE + 6
&ubyte via2t1lh = VIA2_BASE + 7
&ubyte via2t2l = VIA2_BASE + 8
&ubyte via2t2h = VIA2_BASE + 9
&ubyte via2sr = VIA2_BASE + 10
&ubyte via2acr = VIA2_BASE + 11
&ubyte via2pcr = VIA2_BASE + 12
&ubyte via2ifr = VIA2_BASE + 13
&ubyte via2ier = VIA2_BASE + 14
&ubyte via2ora = VIA2_BASE + 15
&ubyte via2prb = VIA2_BASE + 0
&ubyte via2pra = VIA2_BASE + 1
&ubyte via2ddrb = VIA2_BASE + 2
&ubyte via2ddra = VIA2_BASE + 3
&ubyte via2t1l = VIA2_BASE + 4
&ubyte via2t1h = VIA2_BASE + 5
&ubyte via2t1ll = VIA2_BASE + 6
&ubyte via2t1lh = VIA2_BASE + 7
&ubyte via2t2l = VIA2_BASE + 8
&ubyte via2t2h = VIA2_BASE + 9
&ubyte via2sr = VIA2_BASE + 10
&ubyte via2acr = VIA2_BASE + 11
&ubyte via2pcr = VIA2_BASE + 12
&ubyte via2ifr = VIA2_BASE + 13
&ubyte via2ier = VIA2_BASE + 14
&ubyte via2ora = VIA2_BASE + 15
; YM-2151 sound chip
&ubyte YM_ADDRESS = $9f40
&ubyte YM_DATA = $9f41
&ubyte YM_DATA = $9f41
const uword extdev = $9f60
@ -320,7 +324,7 @@ cx16 {
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc
romsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) clobbers(A, X, Y) -> bool @Pc
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
@ -337,12 +341,12 @@ romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y)
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, ubyte fill @Pc) clobbers(A,X,Y)
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, bool fill @Pc) clobbers(A,X,Y)
romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y)
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, ubyte fill @Pc) clobbers(A,X,Y)
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, bool fill @Pc) clobbers(A,X,Y)
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y)
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, bool is_control @Pc) clobbers(A,X,Y)
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
@ -364,16 +368,17 @@ romsub $ff1a = FB_filter_pixels(uword pointer @ R0, uword count @R1) clobbers(A
romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y)
; misc
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, ubyte @Pc
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> ubyte @Pc
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc
romsub $FEBA = BSAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; like cbm.SAVE, but omits the 2-byte prg header
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> bool @Pc
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, bool apply_mask @Pc) clobbers(A,X,Y) -> bool @Pc
romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y)
romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y)
romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y)
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y)
romsub $fede = console_put_char(ubyte char @A, bool wrapping @Pc) clobbers(A,X,Y)
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
@ -381,7 +386,7 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() clobbers(A,X,Y)
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
romsub $ff47 = enter_basic(bool cold_or_warm @Pc) clobbers(A,X,Y)
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
@ -400,16 +405,16 @@ romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; altern
; Audio (bank 10)
romsub $C04B = psg_init() clobbers(A,X,Y)
romsub $C063 = ym_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> ubyte @Pc ; load default YM patches
romsub $C09F = audio_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)initialize PSG and YM audio chips
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize PSG and YM audio chips
; TODO: add more of the audio routines?
asmsub kbdbuf_clear() {
; -- convenience helper routine to clear the keyboard buffer
%asm {{
- jsr c64.GETIN
- jsr cbm.GETIN
bne -
rts
}}
@ -482,7 +487,7 @@ asmsub numbanks() -> uword @AY {
%asm {{
phx
sec
jsr c64.MEMTOP
jsr cbm.MEMTOP
ldy #0
cmp #0
bne +
@ -604,42 +609,82 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
}}
}
sub FB_set_pixels_from_buf(uword buffer, uword count) {
asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @Y) clobbers (A) {
; -- 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 {{
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
; See https://github.com/commanderx16/x16-rom/issues/179
phx
lda buffer
ldy buffer+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
jsr _pixels
plx
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
}}
sty P8ZP_SCRATCH_B1
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
txa
and cx16.VERA_DATA0
ora P8ZP_SCRATCH_B1
sta cx16.VERA_DATA0
rts
}}
}
; ---- system stuff -----
asmsub save_vera_context() clobbers(A) {
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
%asm {{
; note cannot store this on cpu hardware stack because this gets called as a subroutine
lda cx16.VERA_ADDR_L
sta _vera_storage
lda cx16.VERA_ADDR_M
sta _vera_storage+1
lda cx16.VERA_ADDR_H
sta _vera_storage+2
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
lda cx16.VERA_ADDR_M
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
rts
_vera_storage: .byte 0,0,0,0,0,0,0,0
}}
}
asmsub restore_vera_context() clobbers(A) {
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
%asm {{
lda cx16.save_vera_context._vera_storage+7
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+6
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+5
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+4
sta cx16.VERA_ADDR_L
lda cx16.save_vera_context._vera_storage+3
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+2
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+1
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+0
sta cx16.VERA_ADDR_L
rts
}}
}
}
sys {
; ------- lowlevel system routines --------
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
@ -650,29 +695,29 @@ asmsub init_system() {
tay
jsr cx16.mouse_config ; disable mouse
cld
lda VERA_DC_VIDEO
lda cx16.VERA_DC_VIDEO
and #%00000111 ; retain chroma + output mode
sta P8ZP_SCRATCH_REG
lda #$0a
sta $01 ; rom bank 10 (audio)
jsr audio_init ; silence
jsr cx16.audio_init ; silence
stz $01 ; rom bank 0 (kernal)
jsr c64.IOINIT
jsr c64.RESTOR
jsr c64.CINT
lda VERA_DC_VIDEO
jsr cbm.IOINIT
jsr cbm.RESTOR
jsr cbm.CINT
lda cx16.VERA_DC_VIDEO
and #%11111000
ora P8ZP_SCRATCH_REG
sta VERA_DC_VIDEO ; restore old output mode
sta cx16.VERA_DC_VIDEO ; restore old output mode
lda #$90 ; black
jsr c64.CHROUT
jsr cbm.CHROUT
lda #1
sta $00 ; select ram bank 1
jsr c64.CHROUT ; swap fg/bg
jsr cbm.CHROUT ; swap fg/bg
lda #$9e ; yellow
jsr c64.CHROUT
jsr cbm.CHROUT
lda #147 ; clear screen
jsr c64.CHROUT
jsr cbm.CHROUT
lda #0
tax
tay
@ -707,12 +752,12 @@ asmsub cleanup_at_exit() {
}}
}
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta _use_kernal
sei
lda #<_irq_handler
@ -786,57 +831,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) {
%asm {{
sei
@ -906,27 +900,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() {
; 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 {{
sei
stz $01 ; bank the kernal in
jmp (cx16.RESET_VEC)
ldx #$42
ldy #1
tya
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) {
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
; note: this routine cannot be used from inside a irq handler
%asm {{
phx
sta P8ZP_SCRATCH_W1
@ -938,9 +938,13 @@ _loop lda P8ZP_SCRATCH_W1
plx
rts
+ jsr c64.RDTIM
+ sei
jsr cbm.RDTIM
cli
sta P8ZP_SCRATCH_B1
- jsr c64.RDTIM
- sei
jsr cbm.RDTIM
cli
cmp P8ZP_SCRATCH_B1
beq -
@ -1080,10 +1084,23 @@ _longcopy
}}
}
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
jsr c64.CLRCHN ; reset i/o channels
jsr cbm.CLRCHN ; reset i/o channels
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller

View File

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

View File

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

View File

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

View File

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

View File

@ -127,7 +127,7 @@ _startloop dey
}}
}
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc {
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, bool @Pc {
; Locates the first position of the given character in the string,
; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
%asm {{
@ -292,7 +292,7 @@ str = P8ZP_SCRATCH_W1
sta modify_pattern2+2
jsr _match
lda #0
adc #0
rol a
ldx P8ZP_SCRATCH_REG
rts

View File

@ -196,9 +196,9 @@ sub str2uword(str string) -> uword {
; the number may NOT be preceded by a + sign and may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%ir {{
loadm.w r65500,conv.str2uword.string
syscall 11
returnreg.w r0
loadm.w r65535,conv.str2uword.string
syscall 11 (r65535.w) : r0.w
returnr.w r0
}}
}
@ -207,9 +207,9 @@ sub str2word(str string) -> word {
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%ir {{
loadm.w r65500,conv.str2word.string
syscall 12
returnreg.w r0
loadm.w r65535,conv.str2word.string
syscall 12 (r65535.w) : r0.w
returnr.w r0
}}
}

View File

@ -10,8 +10,8 @@ floats {
sub print_f(float value) {
; ---- prints the floating point value (without a newline).
%ir {{
loadm.f fr65500,floats.print_f.value
syscall 25
loadm.f fr65535,floats.print_f.value
syscall 25 (fr65535.f)
return
}}
}
@ -21,15 +21,7 @@ sub pow(float value, float power) -> float {
loadm.f fr0,floats.pow.value
loadm.f fr1,floats.pow.power
fpow.f fr0,fr1
returnreg.f fr0
}}
}
sub fabs(float value) -> float {
%ir {{
loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -37,7 +29,7 @@ sub sin(float angle) -> float {
%ir {{
loadm.f fr0,floats.sin.angle
fsin.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -45,7 +37,7 @@ sub cos(float angle) -> float {
%ir {{
loadm.f fr0,floats.cos.angle
fcos.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -53,7 +45,7 @@ sub tan(float value) -> float {
%ir {{
loadm.f fr0,floats.tan.value
ftan.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -61,7 +53,7 @@ sub atan(float value) -> float {
%ir {{
loadm.f fr0,floats.atan.value
fatan.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -69,7 +61,7 @@ sub ln(float value) -> float {
%ir {{
loadm.f fr0,floats.ln.value
fln.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -77,15 +69,7 @@ sub log2(float value) -> float {
%ir {{
loadm.f fr0,floats.log2.value
flog.f fr0,fr0
returnreg.f fr0
}}
}
sub sqrt(float value) -> float {
%ir {{
loadm.f fr0,floats.sqrt.value
sqrt.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -103,7 +87,7 @@ sub round(float value) -> float {
%ir {{
loadm.f fr0,floats.round.value
fround.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -111,7 +95,7 @@ sub floor(float value) -> float {
%ir {{
loadm.f fr0,floats.floor.value
ffloor.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -120,22 +104,46 @@ sub ceil(float value) -> float {
%ir {{
loadm.f fr0,floats.ceil.value
fceil.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
sub rndf() -> float {
%ir {{
syscall 35
returnreg.f fr0
syscall 35 () : fr0.f
returnr.f fr0
}}
}
sub rndseedf(float seed) {
%ir {{
loadm.f fr65500,floats.rndseedf.seed
syscall 32
loadm.f fr65535,floats.rndseedf.seed
syscall 32 (fr65535.f)
return
}}
}
sub minf(float f1, float f2) -> float {
if f1<f2
return f1
return f2
}
sub maxf(float f1, float f2) -> float {
if f1>f2
return f1
return f2
}
sub clampf(float value, float minimum, float maximum) -> float {
if value<minimum
value=minimum
if value<maximum
return value
return maximum
}
}

View File

@ -161,24 +161,24 @@ math {
sub rnd() -> ubyte {
%ir {{
syscall 33
returnreg.b r0
syscall 33 (): r0.b
returnr.b r0
}}
}
sub rndw() -> uword {
%ir {{
syscall 34
returnreg.w r0
syscall 34 (): r0.w
returnr.w r0
}}
}
sub rndseed(uword seed1, uword seed2) {
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
%ir {{
loadm.w r65500,math.rndseed.seed1
loadm.w r65501,math.rndseed.seed2
syscall 31
loadm.w r65534,math.rndseed.seed1
loadm.w r65535,math.rndseed.seed2
syscall 31 (r65534.w, r65535.w)
return
}}
}

View File

@ -84,10 +84,10 @@ string {
; Note that you can also directly compare strings and string values with eachother using
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
%ir {{
loadm.w r65500,string.compare.st1
loadm.w r65501,string.compare.st2
syscall 29
returnreg.b r0
loadm.w r65534,string.compare.st1
loadm.w r65535,string.compare.st2
syscall 29 (r65534.w, r65535.w) : r0.b
returnr.b r0
}}
}

View File

@ -8,22 +8,22 @@ sys {
sub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt.
%ir {{
syscall 0
syscall 0 ()
}}
}
sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds)
%ir {{
loadm.w r65500,sys.wait.jiffies
syscall 13
loadm.w r65535,sys.wait.jiffies
syscall 13 (r65535.w)
}}
}
sub waitvsync() {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
%ir {{
syscall 14
syscall 14()
}}
}
@ -62,8 +62,8 @@ sys {
sub exit(ubyte returnvalue) {
; -- immediately exit the program with a return code in the A register
%ir {{
loadm.b r65500,sys.exit.returnvalue
syscall 1
loadm.b r65535,sys.exit.returnvalue
syscall 1 (r65535.b)
}}
}
@ -82,33 +82,33 @@ sys {
sub gfx_enable(ubyte mode) {
%ir {{
loadm.b r65500,sys.gfx_enable.mode
syscall 8
loadm.b r65535,sys.gfx_enable.mode
syscall 8 (r65535.b)
}}
}
sub gfx_clear(ubyte color) {
%ir {{
loadm.b r65500,sys.gfx_clear.color
syscall 9
loadm.b r65535,sys.gfx_clear.color
syscall 9 (r65535.b)
}}
}
sub gfx_plot(uword xx, uword yy, ubyte color) {
%ir {{
loadm.w r65500,sys.gfx_plot.xx
loadm.w r65501,sys.gfx_plot.yy
loadm.b r65502,sys.gfx_plot.color
syscall 10
loadm.w r65533,sys.gfx_plot.xx
loadm.w r65534,sys.gfx_plot.yy
loadm.b r65535,sys.gfx_plot.color
syscall 10 (r65533.w, r65534.w, r65535.b)
}}
}
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%ir {{
loadm.w r65500,sys.gfx_getpixel.xx
loadm.w r65501,sys.gfx_getpixel.yy
syscall 30
returnreg.b r0
loadm.w r65534,sys.gfx_getpixel.xx
loadm.w r65535,sys.gfx_getpixel.yy
syscall 30 (r65534.w, r65535.w): r0.b
returnr.b r0
}}
}
}

View File

@ -15,8 +15,8 @@ sub height() -> ubyte {
sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H"
%ir {{
load.w r65500,txt.clear_screen.sequence
syscall 3
load.w r65535,txt.clear_screen.sequence
syscall 3 (r65535.w)
}}
}
@ -38,15 +38,15 @@ sub uppercase() {
sub chrout(ubyte char) {
%ir {{
loadm.b r65500,txt.chrout.char
syscall 2
loadm.b r65535,txt.chrout.char
syscall 2 (r65535.b)
}}
}
sub print (str text) {
%ir {{
loadm.w r65500,txt.print.text
syscall 3
loadm.w r65535,txt.print.text
syscall 3 (r65535.w)
}}
}
@ -122,9 +122,10 @@ sub input_chars (uword buffer) -> ubyte {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel!
%ir {{
loadm.w r65500,txt.input_chars.buffer
syscall 6
returnreg.b r0
loadm.w r65534,txt.input_chars.buffer
load.b r65535,80
syscall 6 (r65534.w, r65535.b): r0.b
returnr.b r0
}}
}

View File

@ -1 +1 @@
8.11
9.0

View File

@ -48,11 +48,12 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").required()
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
val useNewExprCode by cli.option(ArgType.Boolean, fullName = "newexpr", description = "use new expression code-gen (experimental)")
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@ -130,6 +131,7 @@ private fun compileMain(args: Array<String>): Boolean {
useNewExprCode == true,
compilationTarget,
evalStackAddr,
splitWordArrays == true,
processedSymbols,
srcdirs,
outputPath
@ -195,6 +197,7 @@ private fun compileMain(args: Array<String>): Boolean {
useNewExprCode == true,
compilationTarget,
evalStackAddr,
splitWordArrays == true,
processedSymbols,
srcdirs,
outputPath

View File

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

View File

@ -40,6 +40,7 @@ class CompilerArguments(val filepath: Path,
val useNewExprCode: Boolean,
val compilationTarget: String,
val evalStackBaseAddress: UInt?,
val splitWordArrays: Boolean,
val symbolDefs: Map<String, String>,
val sourceDirs: List<String> = emptyList(),
val outputDir: Path = Path(""),
@ -80,6 +81,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
varsHigh = args.varsHigh
useNewExprCode = args.useNewExprCode
evalStackBaseAddress = args.evalStackBaseAddress
splitWordArrays = args.splitWordArrays
outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs
}
@ -258,8 +260,9 @@ fun parseMainModule(filepath: Path,
for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
importer.importImplicitLibraryModule(lib)
// always import prog8_lib and math
importer.importImplicitLibraryModule("math")
if(compilerOptions.compTarget.name!=VMTarget.NAME && !compilerOptions.experimentalCodegen) {
importer.importImplicitLibraryModule("math")
}
importer.importImplicitLibraryModule("prog8_lib")
if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG)
@ -280,10 +283,9 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase()
val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.uppercase()
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
.flatMap { (it as Directive).args }.toSet()
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
val noSysInit = allOptions.any { it.name == "no_sysinit" }
val allOptions = program.modules.flatMap { it.options() }.toSet()
val floatsEnabled = "enable_floats" in allOptions
val noSysInit = "no_sysinit" in allOptions
val zpType: ZeropageType =
if (zpoption == null)
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE

View File

@ -32,6 +32,7 @@ class ModuleImporter(private val program: Program,
val programPath = path.resolve(normalizedFilePath)
if(programPath.exists()) {
println("Compiling program ${Path("").absolute().relativize(programPath)}")
println("Compiler target: $compilationTargetName")
val source = SourceCode.File(programPath)
return Ok(importModule(source))
}
@ -106,13 +107,18 @@ class ModuleImporter(private val program: Program,
removeDirectivesFromImportedModule(importedModule)
// modules can contain blocks with "merge" option.
// their content has to be merged into already existing block with the same name.
// their content has to be merged into already existing other block with the same name.
val blocks = importedModule.statements.filterIsInstance<Block>()
for(block in blocks) {
if("merge" in block.options()) {
val existingBlock = program.allBlocks.first { it.name==block.name}
existingBlock.statements.addAll(block.statements.filter { it !is Directive})
importedModule.statements.remove(block)
val existingBlock = program.allBlocks.firstOrNull { it.name==block.name && it !== block}
if(existingBlock!=null) {
existingBlock.statements.addAll(block.statements.filter { it !is Directive })
importedModule.statements.remove(block)
} else {
val merges = block.statements.filter { it is Directive && it.directive=="%option" && it.args.any { a->a.name=="merge" } }
block.statements.removeAll(merges)
}
}
}

View File

@ -6,7 +6,6 @@ import prog8.ast.base.SyntaxError
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.ast.PtIdentifier
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.compiler.builtinFunctionReturnType
@ -166,7 +165,8 @@ internal class AstChecker(private val program: Program,
}
DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_UW &&
iterableDt != DataType.ARRAY_UW_SPLIT)
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
@ -177,7 +177,8 @@ internal class AstChecker(private val program: Program,
}
DataType.WORD -> {
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W &&
iterableDt != DataType.ARRAY_W_SPLIT)
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
}
DataType.FLOAT -> {
@ -327,8 +328,6 @@ internal class AstChecker(private val program: Program,
err("subroutines can only be defined in the scope of a block or within another subroutine")
if(subroutine.isAsmSubroutine) {
if(compilerOptions.compTarget.name==VMTarget.NAME)
err("cannot use asmsub for vm target, use regular subs")
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
err("number of asm parameter registers is not the isSameAs as number of parameters")
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
@ -336,7 +335,7 @@ internal class AstChecker(private val program: Program,
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL)
err("parameter '${param.first.name}' should be (u)byte or bool")
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
}
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
@ -344,8 +343,8 @@ internal class AstChecker(private val program: Program,
err("parameter '${param.first.name}' should be (u)word (an address) or str")
}
else if(param.second.statusflag!=null) {
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BOOL)
err("parameter '${param.first.name}' should be bool or ubyte")
if (param.first.type != DataType.BOOL)
errors.err("parameter '${param.first.name}' should be of type bool", param.first.position)
}
}
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
@ -359,8 +358,8 @@ internal class AstChecker(private val program: Program,
err("return type #${index + 1} should be (u)word/address")
}
else if(pair.second.statusflag!=null) {
if (pair.first != DataType.UBYTE && pair.first != DataType.BOOL)
err("return type #${index + 1} should be bool or ubyte")
if (pair.first != DataType.BOOL)
err("return type #${index + 1} should be bool")
}
}
@ -431,7 +430,6 @@ internal class AstChecker(private val program: Program,
val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc
if(statusFlagsNoCarry.isNotEmpty())
err("can only use Carry as status flag parameter")
}
// Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly
@ -483,7 +481,9 @@ internal class AstChecker(private val program: Program,
errors.err("target datatype is unknown", assignment.target.position)
// otherwise, another error about missing symbol is already reported.
} else {
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
// allow bitwise operations on different types as long as the size is the same
if (!((assignment.value as? BinaryExpression)?.operator in BitwiseOperators && targetDt.isBytes && valueDt.isBytes || targetDt.isWords && valueDt.isWords))
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
}
}
}
@ -630,6 +630,9 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B, DataType.ARRAY_UB ->
if(arraySize > 256)
err("byte array length must be 1-256")
in SplitWordArrayTypes ->
if(arraySize > 256)
err("split word array length must be 1-256")
DataType.ARRAY_W, DataType.ARRAY_UW ->
if(arraySize > 128)
err("word array length must be 1-128")
@ -678,6 +681,10 @@ internal class AstChecker(private val program: Program,
if (length == 0 || length > 256)
err("string and byte array length must be 1-256")
}
in SplitWordArrayTypes -> {
if (length == 0 || length > 256)
err("split word array length must be 1-256")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if (length == 0 || length > 128)
err("word array length must be 1-128")
@ -690,6 +697,9 @@ internal class AstChecker(private val program: Program,
}
}
}
if(decl.splitArray && decl.type==VarDeclType.MEMORY)
err("@split can't be used on memory mapped arrays")
}
if(decl.datatype==DataType.STR) {
@ -793,7 +803,7 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty())
err("missing option directive argument(s)")
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge")}.any { !it })
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays")}.any { !it })
err("invalid option directive argument(s)")
}
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
@ -961,11 +971,7 @@ internal class AstChecker(private val program: Program,
}
}
if(expr.operator in ComparisonOperators) {
if(leftDt!=rightDt && !(leftDt in ByteDatatypes && rightDt in ByteDatatypes)) {
throw FatalAstException("got comparison with different operand types: $leftDt ${expr.operator} $rightDt ${expr.position}")
}
} else {
if(expr.operator !in ComparisonOperators) {
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
// str+str and str*number have already been const evaluated before we get here.
errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
@ -1145,21 +1151,13 @@ internal class AstChecker(private val program: Program,
}
}
}
else if(funcName[0] == "divmod") {
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
errors.err("all arguments must be ubyte", functionCallStatement.position)
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UBYTE})
errors.err("all arguments must be ubyte", functionCallStatement.position)
}
else if(funcName[0] == "divmodw") {
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
errors.err("all arguments must be uword", functionCallStatement.position)
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UWORD})
errors.err("all arguments must be uword", functionCallStatement.position)
else if(funcName[0].startsWith("divmod")) {
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression) {
errors.err("arguments must be all ubyte or all uword", functionCallStatement.position)
} else {
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
}
}
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
@ -1290,7 +1288,7 @@ internal class AstChecker(private val program: Program,
// check index value 0..255
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
if(dtxNum.isKnown && dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
super.visit(arrayIndexedExpression)
@ -1321,14 +1319,15 @@ internal class AstChecker(private val program: Program,
val whenStmt = whenChoice.parent as When
if(whenChoice.values!=null) {
val conditionType = whenStmt.condition.inferType(program)
if(!conditionType.isKnown)
throw FatalAstException("can't determine when choice datatype $whenChoice")
val constvalues = whenChoice.values!!.map { it.constValue(program) }
for(constvalue in constvalues) {
when {
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
conditionType isnot constvalue.type -> errors.err("choice value datatype differs from condition value", whenChoice.position)
conditionType isnot constvalue.type -> {
if(conditionType.isKnown)
errors.err("choice value datatype differs from condition value", whenChoice.position)
}
}
}
} else {
@ -1439,22 +1438,23 @@ internal class AstChecker(private val program: Program,
}
return err("invalid byte array initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT-> {
// value may be either a single word, or a word arraysize, or a range
if(value.type istype targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.constIndex()
val arraySize = value.value.size
val maxLength = if(targetDt in SplitWordArrayTypes) 256 else 128
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize>128)
return err("word array length must be 1-128")
if(arraySpecSize>maxLength)
return err("array length must be 1-$maxLength")
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true
}
return err("invalid word array size, must be 1-128")
return err("invalid array size, must be 1-$maxLength")
}
return err("invalid word array initialization value ${value.type}, expected $targetDt")
}
@ -1542,6 +1542,7 @@ internal class AstChecker(private val program: Program,
when (it) {
is NumericLiteral -> it.number.toInt()
is AddressOf -> it.identifier.hashCode() and 0xffff
is IdentifierReference -> it.hashCode() and 0xffff
is TypecastExpression -> {
val constVal = it.expression.constValue(program)
val cast = constVal?.cast(it.type)
@ -1561,10 +1562,10 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B -> {
correct = array.all { it in -128..127 }
}
DataType.ARRAY_UW -> {
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> {
correct = array.all { (it in 0..65535) }
}
DataType.ARRAY_W -> {
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> {
correct = array.all { it in -32768..32767 }
}
DataType.ARRAY_F -> correct = true
@ -1613,8 +1614,11 @@ internal class AstChecker(private val program: Program,
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
else {
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes) {
// allow bitwise operations on different types as long as the size is the same
if (!((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)))
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
}
return false

View File

@ -162,9 +162,31 @@ class AstPreprocessor(val program: Program,
nextAssignment.origin = AssignmentOrigin.VARINIT
}
}
if(options.splitWordArrays && (decl.datatype==DataType.ARRAY_W || decl.datatype==DataType.ARRAY_UW)) {
if(!decl.definingBlock.isInLibrary)
return makeSplitArray(decl)
}
if(decl.datatype==DataType.ARRAY_W || decl.datatype==DataType.ARRAY_UW) {
if ("splitarrays" in decl.definingBlock.options())
return makeSplitArray(decl)
if("splitarrays" in decl.definingModule.options())
return makeSplitArray(decl)
}
return noModifications
}
private fun makeSplitArray(decl: VarDecl): Iterable<IAstModification> {
val splitDt = ArrayToElementTypes.getValue(decl.datatype)
val newDecl = VarDecl(
decl.type, decl.origin, splitDt, decl.zeropage, decl.arraysize, decl.name,
decl.value, true, decl.sharedWithAsm, true, decl.position
)
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
// For non-kernal subroutines and non-asm parameters:
// inject subroutine params as local variables (if they're not there yet).

View File

@ -93,6 +93,11 @@ internal class BeforeAsmAstChanger(val program: Program,
)
}
} else {
if(binExpr.left isSameAs assignment.target)
return noModifications
val typeCast = binExpr.left as? TypecastExpression
if(typeCast!=null && typeCast.expression isSameAs assignment.target)
return noModifications
val sourceDt = binExpr.left.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
val (_, left) = binExpr.left.typecastTo(assignment.target.inferType(program).getOrElse { throw AssemblyError(
"unknown dt"
@ -229,13 +234,13 @@ internal class BeforeAsmAstChanger(val program: Program,
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val containingStatement = getContainingStatement(arrayIndexedExpression)
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
return noModifications
}
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization/check for Vm target
val containingStatement = getContainingStatement(arrayIndexedExpression)
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
return noModifications
}
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization for Vm target
val index = arrayIndexedExpression.indexer.indexExpr
if (index !is NumericLiteral && index !is IdentifierReference) {
// replace complex indexing expression with a temp variable to hold the computed index first

View File

@ -30,7 +30,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position)
}
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name,
newvalue, decl.isArray, decl.sharedWithAsm, decl.position)
newvalue, decl.isArray, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
}
@ -47,7 +47,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position)
}
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name,
newarray, true, decl.sharedWithAsm, decl.position)
newarray, true, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
}

View File

@ -142,8 +142,8 @@ _after:
val indexExpr = arrayIndexedExpression.indexer.indexExpr
val indexerDt = indexExpr.inferType(program)
if(indexerDt.isWords) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)!!
if(arrayVar.datatype==DataType.UWORD) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
if(arrayVar!=null && arrayVar.datatype==DataType.UWORD) {
val add: Expression =
if(indexExpr.constValue(program)?.number==0.0)
arrayIndexedExpression.arrayvar.copy()

View File

@ -168,6 +168,7 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
"align_word" -> alignment = PtBlock.BlockAlignment.WORD
"align_page" -> alignment = PtBlock.BlockAlignment.PAGE
"force_output" -> forceOutput=true
"merge", "splitarrays" -> { /* ignore this one */ }
else -> throw FatalAstException("weird directive option: ${arg.name}")
}
}
@ -355,9 +356,9 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
for (asm in srcSub.statements) {
asm as InlineAssembly
if(asm.isIR)
combinedIrAsm += asm.assembly + "\n"
combinedIrAsm += asm.assembly.trimEnd() + "\n"
else
combinedTrueAsm += asm.assembly + "\n"
combinedTrueAsm += asm.assembly.trimEnd() + "\n"
}
if(combinedTrueAsm.isNotEmpty()) {
@ -432,7 +433,12 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
private fun transform(src: AddressOf): PtAddressOf {
val addr = PtAddressOf(src.position)
addr.add(transform(src.identifier))
val (name, dt) = src.identifier.targetNameAndType(program)
if(dt in SplitWordArrayTypes) {
addr.add(PtIdentifier(name+"_lsb", dt, src.identifier.position))
} else {
addr.add(transform(src.identifier))
}
return addr
}

View File

@ -7,14 +7,12 @@ import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.Assignment
import prog8.ast.statements.VarDecl
import prog8.ast.statements.WhenChoice
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.*
internal class LiteralsToAutoVars(private val program: Program,
@ -60,10 +58,12 @@ internal class LiteralsToAutoVars(private val program: Program,
} else {
val arrayDt = array.guessDatatype(program)
if(arrayDt.isKnown) {
val parentAssign = parent as? Assignment
val targetDt = parentAssign?.target?.inferType(program) ?: arrayDt
// turn the array literal it into an identifier reference
val litval2 = array.cast(arrayDt.getOr(DataType.UNDEFINED))
val litval2 = array.cast(targetDt.getOr(DataType.UNDEFINED))
if(litval2!=null) {
val vardecl2 = VarDecl.createAuto(litval2)
val vardecl2 = VarDecl.createAuto(litval2, targetDt.getOr(DataType.UNDEFINED) in SplitWordArrayTypes)
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
return listOf(
IAstModification.ReplaceNode(array, identifier, parent),

View File

@ -162,6 +162,7 @@ internal class StatementReorderer(val program: Program,
null,
false,
it.sharedWithAsm,
it.splitArray,
it.position
)
IAstModification.ReplaceNode(it, newvar, subroutine)
@ -290,8 +291,10 @@ internal class StatementReorderer(val program: Program,
} else {
if (sourceVar.arraysize!!.constIndex() != targetVar.arraysize!!.constIndex())
errors.err("element count mismatch", assign.position)
if (sourceVar.datatype != targetVar.datatype)
errors.err("element type mismatch", assign.position)
if (sourceVar.datatype != targetVar.datatype) {
if(!targetVar.splitArray || (sourceVar.datatype!=DataType.ARRAY_W && sourceVar.datatype!=DataType.ARRAY_UW))
errors.err("element type mismatch", assign.position)
}
}
if(!errors.noErrors())

View File

@ -133,9 +133,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
if(leftDt istype DataType.WORD && rightDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
// cast left to unsigned
val cast = TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED), true, expr.left.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
// cast left to unsigned word. Cast right to unsigned word if it is ubyte
val mods = mutableListOf<IAstModification>()
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
mods += IAstModification.ReplaceNode(expr.left, cast, expr)
if(rightDt istype DataType.UBYTE) {
mods += IAstModification.ReplaceNode(expr.right,
TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position),
expr)
}
return mods
}
if(rightDt istype DataType.BYTE && leftDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
// cast right to unsigned
@ -143,9 +150,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
}
if(rightDt istype DataType.WORD && leftDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
// cast right to unsigned
val cast = TypecastExpression(expr.right, leftDt.getOr(DataType.UNDEFINED), true, expr.right.position)
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
// cast right to unsigned word. Cast left to unsigned word if it is ubyte
val mods = mutableListOf<IAstModification>()
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
mods += IAstModification.ReplaceNode(expr.right, cast, expr)
if(leftDt istype DataType.UBYTE) {
mods += IAstModification.ReplaceNode(expr.left,
TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position),
expr)
}
return mods
}
}

View File

@ -36,6 +36,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
useNewExprCode = false,
compilationTarget = target.name,
evalStackBaseAddress = null,
splitWordArrays = false,
symbolDefs = emptyMap(),
outputDir = outputDir
)
@ -45,6 +46,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
private fun prepareTestFiles(source: String, optimize: Boolean, target: ICompilationTarget): Pair<String, Path> {
val searchIn = mutableListOf(examplesDir)
when (target) {
is C64Target -> searchIn.add(0, assumeDirectory(examplesDir, "c64"))
is Cx16Target -> searchIn.add(0, assumeDirectory(examplesDir, "cx16"))
is VMTarget -> searchIn.add(0, assumeDirectory(examplesDir, "vm"))
is C128Target -> searchIn.add(0, assumeDirectory(examplesDir, "c128"))
@ -68,10 +70,13 @@ class TestCompilerOnExamplesC64: FunSpec({
"bdmusic",
"bdmusic-irq",
"charset",
"cube3d",
"cube3d-sprites",
"plasma",
"rasterbars",
"sprites",
"starfield",
"tehtriz",
"turtle-gfx",
"wizzine",
),
@ -93,13 +98,17 @@ class TestCompilerOnExamplesCx16: FunSpec({
val onlyCx16 = cartesianProduct(
listOf(
"vtui/testvtui",
"pcmaudio/play-adpcm",
"pcmaudio/stream-wav",
"amiga",
"bdmusic",
"bobs",
"bubbleuniverse",
"circles",
"cobramk3-gfx",
"colorbars",
"cube3d",
"cxlogo",
"datetime",
"diskspeed",
"fileseek",
@ -138,7 +147,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
"cube3d",
"cube3d-float",
"cube3d-gfx",
"cxlogo",
"dirlist",
"fibonacci",
"line-circle-gfx",
@ -148,7 +156,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
"mandelbrot-gfx",
"numbergame",
"primes",
"rasterbars",
"screencodes",
"sorting",
"swirl",

View File

@ -53,6 +53,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
useNewExprCode = false,
compilationTarget = Cx16Target.NAME,
evalStackBaseAddress = null,
splitWordArrays = false,
symbolDefs = emptyMap(),
sourceDirs,
outputDir

View File

@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK>
</PROGRAM>
""")

View File

@ -112,7 +112,7 @@ class TestMemory: FunSpec({
}
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -150,7 +150,7 @@ class TestMemory: FunSpec({
}
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -162,7 +162,7 @@ class TestMemory: FunSpec({
test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -174,7 +174,7 @@ class TestMemory: FunSpec({
test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -185,7 +185,7 @@ class TestMemory: FunSpec({
}
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -198,7 +198,7 @@ class TestMemory: FunSpec({
test("memory mapped array not in mapped IO ram") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -211,7 +211,7 @@ class TestMemory: FunSpec({
test("memory mapped array in mapped IO ram") {
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)

View File

@ -532,7 +532,7 @@ class TestOptimization: FunSpec({
uword @shared zz
zz += 60 ; NOT ok to remove initializer, should evaluate to 60
ubyte @shared xx
xx = 6+lsb(abs(xx)) ; is not an initializer because it references xx
xx = 6+lsb(mkword(xx,22)) ; is not an initializer because it references xx
}
}
"""

View File

@ -26,7 +26,7 @@ class TestSubroutines: FunSpec({
sub start() {
func("abc")
uword[] commands = ["abc", func]
uword[] commands = ["abc", 1.234]
}
}"""
val errors = ErrorReporterForTests()

View File

@ -27,14 +27,14 @@ class TestTypecasts: FunSpec({
main {
sub start() {
float fl
floats.print_f(abs(fl))
floats.print_f(lsb(fl))
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors)
result shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UBYTE, BYTE, UWORD, WORD]"
errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UWORD, WORD]"
}
test("not casting bool operands to logical operators") {

View File

@ -10,70 +10,70 @@ main {
txt.print("ubyte shift left\n")
a = shiftlb0()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb1()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb2()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb3()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb4()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb5()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb6()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb7()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb8()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftlb9()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
txt.print("enter to continue:\n")
void c64.CHRIN()
void cbm.CHRIN()
txt.print("ubyte shift right\n")
a = shiftrb0()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb1()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb2()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb3()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb4()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb5()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb6()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb7()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb8()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
a = shiftrb9()
txt.print_ubbin(a, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
txt.print("enter to continue:\n")
void c64.CHRIN()
void cbm.CHRIN()
@ -82,70 +82,70 @@ main {
byte signedb
signedb = shiftlsb0()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb1()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb2()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb3()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb4()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb5()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb6()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb7()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb8()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftlsb9()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
txt.print("enter to continue:\n")
void c64.CHRIN()
void cbm.CHRIN()
txt.print("signed byte shift right\n")
signedb = shiftrsb0()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb1()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb2()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb3()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb4()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb5()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb6()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb7()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb8()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
signedb = shiftrsb9()
txt.print_ubbin(signedb as ubyte, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
txt.print("enter to continue:\n")
void c64.CHRIN()
void cbm.CHRIN()
@ -154,233 +154,233 @@ main {
uword uw
uw = shiftluw0()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw1()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw2()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw3()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw4()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw5()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw6()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw7()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw8()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw9()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw10()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw11()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw12()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw13()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw14()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw15()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw16()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftluw17()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
txt.print("enter to continue:\n")
void c64.CHRIN()
void cbm.CHRIN()
txt.print("uword shift right\n")
uw = shiftruw0()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw1()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw2()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw3()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw4()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw5()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw6()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw7()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw8()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw9()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw10()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw11()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw12()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw13()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw14()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw15()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw16()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
uw = shiftruw17()
txt.print_uwbin(uw, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
txt.print("enter to continue:\n")
void c64.CHRIN()
void cbm.CHRIN()
txt.print("signed word shift left\n")
word sw
sw = shiftlsw0()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw1()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw2()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw3()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw4()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw5()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw6()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw7()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw8()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw9()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw10()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw11()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw12()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw13()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw14()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw15()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw16()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftlsw17()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
txt.print("enter to continue:\n")
void c64.CHRIN()
void cbm.CHRIN()
txt.print("signed word shift right\n")
sw = shiftrsw0()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw1()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw2()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw3()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw4()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw5()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw6()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw7()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw8()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw9()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw10()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw11()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw12()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw13()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw14()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw15()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw16()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
sw = shiftrsw17()
txt.print_uwbin(sw as uword, true)
c64.CHROUT('\n')
cbm.CHROUT('\n')
}

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