Compare commits

...
121 Commits
Author SHA1 Message Date
Irmen de Jong 10ddd5b127 fixed missing non-boolean operand cast in logical expressions 2022-07-12 22:28:06 +02:00
Irmen de Jong f46e131f18 todo 2022-07-12 19:41:51 +02:00
Irmen de Jong feb5c8be95 vm: some more peephole optimizations 2022-07-12 19:04:19 +02:00
Irmen de Jong edf12bec71 improve bool params typecasting, fix compiler crash on abs(floatvar) 2022-07-12 17:52:37 +02:00
Irmen de Jong ff1fc28287 added immediate value vm logical instructions because these are so common 2022-07-12 16:12:32 +02:00
Irmen de Jong 314398ba4c added immediate value vm arithmetic instructions because these are so common 2022-07-12 15:21:26 +02:00
Irmen de Jong 840331347b added a few more vm optimizations and unit tests 2022-07-12 12:42:37 +02:00
Irmen de Jong 6181b12ab8 added -esa option to override the evalstack location, and shift cx16.r0-r15 accordingly 2022-07-11 19:29:04 +02:00
Irmen de Jong 68da661edc optimize comparison to true/1 into comparison to zero, optimize while/until conditions 2022-07-11 16:42:52 +02:00
Irmen de Jong 88cbb6913d tweak bool type handling 2022-07-11 14:55:50 +02:00
Irmen de Jong 7a26646e1b tweak bool type handling 2022-07-11 02:08:12 +02:00
Irmen de Jong 92eb3b0bf6 bool logical testcase 2022-07-09 22:29:38 +02:00
Irmen de Jong fb63434eee tweak maze example 2022-07-09 22:13:30 +02:00
Irmen de Jong 97f90d9684 Merge branch 'master' into bool_type 2022-07-09 22:09:49 +02:00
Irmen de Jong f91786367f added maze example 2022-07-09 22:00:46 +02:00
Irmen de Jong 6a57337a68 improved bool type checking 2022-07-08 22:59:35 +02:00
Irmen de Jong 211e2bb37a improved bool type checking 2022-07-08 22:29:13 +02:00
Irmen de Jong d2d08bf143 fix compiler error about bool vs ubyte 2022-07-08 22:03:05 +02:00
Irmen de Jong 8acb37b6c2 use bool type in examples and libraries 2022-07-08 21:50:32 +02:00
Irmen de Jong 81b3d2db4f fix compiler crash 2022-07-08 21:50:06 +02:00
Irmen de Jong 9633c0b07a added bool to syntax files 2022-07-07 23:30:41 +02:00
Irmen de Jong 1dfa8ee7d8 add ARRAY_BOOL array type 2022-07-07 23:07:30 +02:00
Irmen de Jong 1163543a98 fix bool param lookup problem 2022-07-07 22:23:56 +02:00
Irmen de Jong bdb7de34be added several compiler checks against weird boolean type use in expressions 2022-07-07 22:23:56 +02:00
Irmen de Jong 9500fc11ac document new bool datatype and removal of boolean() conversion function 2022-07-07 22:23:56 +02:00
Irmen de Jong 65daf29acd fix compiler crash related to word types in certain comparison expressions 2022-07-07 22:23:56 +02:00
Irmen de Jong 298b25cf7d fix compiler crash on certain typecasting assignment 2022-07-07 22:23:56 +02:00
Irmen de Jong 41f4e22a17 introduce BOOL type 2022-07-07 22:23:56 +02:00
Irmen de Jong 288c57c144 ack to allow user to override the following two with command line redefinition: 2022-07-07 22:16:08 +02:00
Irmen de Jong 7ff8923569 document -D command 2022-07-06 23:45:41 +02:00
Irmen de Jong b41779bd02 added -D command line option to define symbols in the assembly file 2022-07-06 23:40:36 +02:00
Irmen de Jong beea6bc794 about bool 2022-07-04 20:26:03 +02:00
Irmen de Jong fee58e98c5 tiny optimization 2022-07-03 13:05:30 +02:00
Irmen de Jong c51c1da618 psg micro optimizations 2022-07-03 11:55:13 +02:00
Irmen de Jong ea2812f50f add max volume to psg envelope 2022-07-03 11:26:56 +02:00
Irmen de Jong 3ec05709d5 convert the sounds in cx16 tehtriz to use the psg module instead 2022-07-03 01:40:29 +02:00
Irmen de Jong 4bdac7404a added sustain to psg envelope 2022-07-03 00:55:25 +02:00
Irmen de Jong cc41218d37 added nicer vm example 2022-07-03 00:41:04 +02:00
Irmen de Jong 4b336b1853 if passing a subroutine or label name as an uword argument, without &, add the addressof automatically 2022-07-02 23:55:32 +02:00
Irmen de Jong e1c77ce236 fix pop() name scoping 2022-07-02 23:27:08 +02:00
Irmen de Jong 064d412ec8 added cx16.push_vera_context() and cx16.pop_vera_context() for use in irq handlers 2022-07-02 23:13:00 +02:00
Irmen de Jong 7fff4f249d optimize msb(cx16.r0) -> cx16.r0H, lsb(cx16.r0) -> cx16.r0L 2022-07-02 21:38:22 +02:00
Irmen de Jong 7a3745f642 psg tweaks 2022-07-02 20:33:40 +02:00
Irmen de Jong f8658f6afa precalc vera freq to not use floating point math anymore 2022-07-02 19:40:18 +02:00
Irmen de Jong 223b725a10 psg abstraction and attack/release envelope 2022-07-02 18:47:12 +02:00
Irmen de Jong 25aad8d7be improve const-evaluation of builtin expressions 2022-07-02 16:29:01 +02:00
Irmen de Jong b2c9b7635d revert restriction on certain associative operator reshuffling
it caused larger generated code
2022-07-02 13:59:24 +02:00
Irmen de Jong 24d13dd120 fix problematic optimizations to logical expressions 2022-07-02 00:56:24 +02:00
Irmen de Jong 965340ff90 logical and/or/xor/not all replaced by bitwise &,|,^,~ (ast, codegens)
this also fixed some invalid outcomes of logical expressions!
2022-07-02 00:38:17 +02:00
Irmen de Jong 8e36fe6bef temporary workaround for code problem around 'not' 2022-07-01 01:01:15 +02:00
Irmen de Jong 2eb41a8caf temporary workaround for code problem around 'not' 2022-07-01 00:38:19 +02:00
Irmen de Jong fb989ae62f cx16: reset rom/ram/monitor banks at program exit to sane values. 2022-07-01 00:14:38 +02:00
Irmen de Jong 7901ec2a64 "not" no longer in LogicalOperators because it makes assembler generate invalid code somehow 2022-06-30 22:49:27 +02:00
Irmen de Jong f675dbc726 vm var allocator now also recognises the memory-mapped variables. no longer crashes 2022-06-30 22:09:49 +02:00
Irmen de Jong 2ad4fdbbb9 added cx16 version of bdmusic, needs ADSR though 2022-06-30 21:33:48 +02:00
Irmen de Jong 97cb0cbd08 tweak "not" removal/rewriting 2022-06-30 02:16:30 +02:00
Irmen de Jong 4ca0805de1 bump version 2022-06-29 01:35:14 +02:00
Irmen de Jong 4b358abbb7 "not" operator removed from ast and codegen (it's been replaced with x==0 as equivalent) 2022-06-29 01:13:08 +02:00
Irmen de Jong dc82a0fc16 better not(x) replacement by x==0 2022-06-28 23:50:23 +02:00
Irmen de Jong 435d6f6f3f vm: and/or/xor/not are all bitwise operations again 2022-06-28 03:17:51 +02:00
Irmen de Jong ef92451d1a fix logical expressions on arbitrary values, for now with boolean() around the operands 2022-06-28 01:18:36 +02:00
Irmen de Jong 06184bdcb1 get rid of failed mccarthy shortcut evaluation 2022-06-27 21:44:52 +02:00
Irmen de Jong af98d01053 failed attempt at McCarthy shortcut evaluation 2022-06-27 21:40:48 +02:00
Irmen de Jong bb1cda0916 fix: boolean values of terms in logical expressions are now properly evaluated 2022-06-26 23:55:34 +02:00
Irmen de Jong a6d0ea347c bank caching not required for pcm_play() 2022-06-26 22:08:10 +02:00
Irmen de Jong 0fcd57192b cx16diskio.f_read() now correctly deals with banked ram boundary 2022-06-26 21:42:56 +02:00
Irmen de Jong a6ffa5738b update to kotlin 1.7.0 2022-06-26 18:54:29 +02:00
Irmen de Jong c75bd97537 update kotest 2022-06-26 18:51:03 +02:00
Irmen de Jong eea09f4de5 fix invalid asm label sometimes generated for multiple loops in same subroutine 2022-06-24 02:26:45 +02:00
Irmen de Jong 5656ec11d3 fix missing abs(byte) routine 2022-06-24 01:51:54 +02:00
Irmen de Jong eb53e44cb0 zsound stream test 2022-06-24 01:51:33 +02:00
Irmen de Jong 69f3106062 first vm peephole optimizer 2022-06-22 00:21:06 +02:00
Irmen de Jong 8ab99f6129 zsound combo example 2022-06-21 00:38:59 +02:00
Irmen de Jong 53a3c59a91 language for sphinx 2022-06-15 22:38:00 +02:00
Irmen de Jong df36983049 version 8.2 2022-06-15 22:31:29 +02:00
Irmen de Jong bda016bb3b optimized 6502 codegen for logical expressions 2022-06-15 22:17:15 +02:00
Irmen de Jong cc174b7b85 added boolean() builtin function and use it to get rid of !=0 comparisons 2022-06-14 23:34:45 +02:00
Irmen de Jong bf9d120081 logical operators now always return a boolean byte result, instead of sometimes word type as well
(preparing for codegen simplifications for these)
2022-06-13 01:37:16 +02:00
Irmen de Jong 775c85fc18 don't swap operands that would change function evaluation order + vm: fix label casing error 2022-06-13 00:25:45 +02:00
Irmen de Jong 5a756aaed9 Pipe expression "|>" removed from the language 2022-06-12 18:41:42 +02:00
Irmen de Jong dca092fd7c fix pipe expression when start term is constant number 2022-06-12 16:59:28 +02:00
Irmen de Jong c6e92ecac4 some code cleanup 2022-06-12 16:15:08 +02:00
Irmen de Jong 93008ff605 tweak zsound examples 2022-06-12 14:51:24 +02:00
Irmen de Jong 43c7b935df fixed zsound pcm player example 2022-06-11 03:31:42 +02:00
Irmen de Jong 8f9a0a244a trying to add zsound pcm player example as well 2022-06-10 23:35:37 +02:00
Irmen de Jong fd13bd864e some notes added to zsound demo player 2022-06-09 23:36:07 +02:00
Irmen de Jong 710f27afa9 bump library versions 2022-06-09 22:44:17 +02:00
Irmen de Jong f537793b0b added zsound demo player example (cx16) 2022-06-08 23:57:01 +02:00
Irmen de Jong f7183e38ee tweak trivial subroutine inlining 2022-06-08 21:05:03 +02:00
Irmen de Jong 0a65dfdd10 optimized codegen for some more simple expressions with +/- 2022-06-07 22:30:08 +02:00
Irmen de Jong 3075578245 optimized codegen for assigning value or variable to indexed pointer. (6502) 2022-06-06 18:30:19 +02:00
Irmen de Jong b042b7705e fix invalid removal of repeated assignments. 2022-06-06 17:27:06 +02:00
Irmen de Jong d56eb397f9 fix codegen for rol/ror on pointer indexed 2022-06-06 16:07:45 +02:00
Irmen de Jong 3054a1d32d api change: removed swap() builtin function (too complex in codegen for little used function) 2022-06-06 16:01:11 +02:00
Irmen de Jong 0a3cd652b0 vm: fix codegen for storing to pointer indexed 2022-06-06 14:18:12 +02:00
Irmen de Jong f70b914779 fix optimized codegen for 2 arg functions, sometimes was passing wrong arg value due to register overwriting 2022-06-06 13:21:45 +02:00
Irmen de Jong 46ca0ac10d properly optimize X - -1 and X + -1, this also fixes type change of ubyte - 2 + 10 2022-06-05 15:35:29 +02:00
Irmen de Jong 031f647952 allow casting negative numbers to unsigned, result = 2's complement 2022-06-05 14:21:10 +02:00
Irmen de Jong 8f1c86f550 fixed several old test files 2022-06-05 14:20:08 +02:00
Irmen de Jong 926fdecd13 fix problematic path handling on windows in error messages 2022-06-05 11:54:19 +02:00
Irmen de Jong af2ca7a67e fix problematic characters that cause path errors on Windows 2022-06-05 11:46:37 +02:00
Irmen de Jong 9e3e2ff81a fix assembly generation error when pipe character is part of string literal 2022-06-04 22:25:51 +02:00
Irmen de Jong a9fe6472d9 remove old screencode syntax from docs 2022-06-04 22:07:31 +02:00
Irmen de Jong a862a81480 added unit test for name shadowing warning 2022-06-04 21:35:48 +02:00
Irmen de Jong dbb92881a1 fixed X register corruption in some cases of rol() and ror() 2022-06-04 21:10:48 +02:00
Irmen de Jong 10bf7f5d07 fix: again gives proper name redefinition errors in same scope 2022-06-04 20:15:46 +02:00
Irmen de Jong 1e61d84fd1 vm: fix expression codegen for pointer indexing 2022-06-04 19:32:35 +02:00
Irmen de Jong 8618ba1b60 fix 6502 expression codegen for pointer indexing 2022-06-04 18:46:16 +02:00
Irmen de Jong 3c8c44155d vm: loadix instruction added for indirect addressing via pointer 2022-06-04 18:07:57 +02:00
Irmen de Jong 2002412026 optimized codegen for pointer indexing (read expressions) 2022-06-04 17:20:17 +02:00
Irmen de Jong 7f69517fd4 preparing optimizing pointer indexing 2022-06-04 16:18:27 +02:00
Irmen de Jong 851f8645b5 Merge remote-tracking branch 'origin/master' 2022-06-04 14:23:41 +02:00
Irmen de Jong c40cfaa388 preparing optimizing pointer indexing 2022-06-04 14:23:02 +02:00
Irmen de Jong 0349d1d57c diskio: moved cx16 optimized f_read() to cx16diskio instead
so unfortunately you have to select the faster version yourself when on cx16
2022-06-04 00:33:27 +02:00
Irmen de Jong 53049c02ee diskio: moved cx16 optimized f_read() to cx16diskio instead
so unfortunately you have to select the faster version yourself when on cx16
2022-06-04 00:25:17 +02:00
Irmen de Jong 73a3a61729 swap() checks for unsupported code gen 2022-06-03 23:41:24 +02:00
Irmen de Jong 5fe6aa2800 fix swap() code for pointervars 2022-06-03 23:13:35 +02:00
Irmen de Jong c7eafd7c79 cx16: fix macptr() signature and use it in diskio.f_read() for big increase in load speed 2022-06-02 00:37:18 +02:00
Irmen de Jong 10b5fb5d72 fix for total size returnvalue of diskio.f_read_all() 2022-06-01 01:13:19 +02:00
Irmen de Jong c4eaa944e2 thoughts 2022-05-30 23:37:41 +02:00
Irmen de Jong a735939d1e removed confusing GPL software license reference and copyright header from library files. (because of exclusion in output files)
Reworded software license and exclusion clause somewhat again in attempt to make it even clearer.
2022-05-30 20:12:20 +02:00
194 changed files with 5613 additions and 4007 deletions

No files matched your search

+11 -11
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.1.0" />
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.3.2" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.1.0/kotest-assertions-core-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/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.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.1.0/kotest-assertions-shared-jvm-5.1.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.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
+15 -15
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.1.0" />
<properties maven-id="io.kotest:kotest-property-jvm:5.3.2" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.1.0/kotest-property-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.1.0/kotest-assertions-shared-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.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-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.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.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.3.2/kotest-property-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.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-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
+30 -30
View File
@@ -1,39 +1,39 @@
<component name="libraryTable">
<library name="io.kotest.runner.junit5.jvm" type="repository">
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.1.0" />
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.3.2" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.1.0/kotest-runner-junit5-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.1.0/kotest-framework-api-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.0/kotlinx-coroutines-test-jvm-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.1.0/kotest-assertions-shared-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.3.2/kotest-runner-junit5-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.3.2/kotest-framework-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.1.0/kotest-framework-engine-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.138/classgraph-4.8.138.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.1/kotlinx-coroutines-test-jvm-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.3.2/kotest-framework-engine-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.146/classgraph-4.8.146.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/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.0/kotlinx-coroutines-debug-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.1/kotlinx-coroutines-debug-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/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$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.1.0/kotest-framework-discovery-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.1.0/kotest-assertions-core-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.1.0/kotest-extensions-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.3.2/kotest-framework-discovery-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.3.2/kotest-extensions-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.12.2/mockk-1.12.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.2/mockk-dsl-jvm-1.12.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.2/mockk-dsl-1.12.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.12.2/mockk-common-1.12.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.2/mockk-agent-jvm-1.12.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.2/mockk-agent-api-1.12.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.2/mockk-agent-common-1.12.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.12.3/mockk-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.12.3/mockk-common-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.3/mockk-dsl-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.3/mockk-dsl-jvm-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.3/mockk-agent-jvm-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.3/mockk-agent-api-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.3/mockk-agent-common-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.5/byte-buddy-1.12.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.5/byte-buddy-agent-1.12.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.1.0/kotest-framework-concurrency-jvm-5.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.6/byte-buddy-1.12.6.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.6/byte-buddy-agent-1.12.6.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.3.2/kotest-framework-concurrency-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.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!/" />
@@ -41,11 +41,11 @@
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.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.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
+6 -6
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.14" />
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.14/kotlin-result-jvm-1.1.14.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.10/kotlin-stdlib-common-1.6.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
+7 -3
View File
@@ -1,11 +1,15 @@
<component name="libraryTable">
<library name="takes" type="repository">
<properties maven-id="org.takes:takes:1.19" />
<properties maven-id="org.takes:takes:1.20" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.19/takes-1.19.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.42/cactoos-0.42.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.20/takes-1.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.50/cactoos-0.50.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
+4 -4
View File
@@ -1,9 +1,9 @@
This sofware license is for Prog8 the compiler + associated libraries.
This sofware license is for Prog8 the compiler + associated library files.
Any and all outputs generated by the compiler (intermediary
source code and compiled binary programs) are excluded from that
and you can do with them whatever you want.
Exception: All output files generated by the compiler (intermediary files
and compiled binary programs) are excluded from this; you can do with those
whatever you want.
+4 -3
View File
@@ -16,10 +16,11 @@ https://prog8.readthedocs.io/
Software license
----------------
GNU GPL 3.0, see file LICENSE
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
- prog8 (the compiler + libraries) is licensed under GNU GPL 3.0
- *exception:* the resulting files created by running the compiler are free to use in whatever way desired.
- The compiler and its libraries are free to use according to the terms of the GNU GPL 3.0
- *exception:* the resulting files (intermediate source codes and resulting binary program) created by the compiler
are excluded from the GPL and are free to use in whatever way desired, commercially or not.
What does Prog8 provide?
+19 -1
View File
@@ -40,6 +40,20 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
vars
}
val allMemMappedVariables: Collection<StMemVar> by lazy {
val vars = mutableListOf<StMemVar>()
fun collect(node: StNode) {
for(child in node.children) {
if(child.value.type== StNodeType.MEMVAR)
vars.add(child.value as StMemVar)
else
collect(child.value)
}
}
collect(this)
vars
}
override fun lookup(scopedName: List<String>) = flat[scopedName]
}
@@ -186,7 +200,11 @@ class StConstant(name: String, val dt: DataType, val value: Double, position: Po
}
class StMemVar(name: String, val dt: DataType, val address: UInt, position: Position) :
class StMemVar(name: String,
val dt: DataType,
val address: UInt,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
position: Position) :
StNode(name, StNodeType.MEMVAR, position) {
override fun printProperties() {
print("$name dt=$dt address=${address.toHex()}")
+16 -14
View File
@@ -8,6 +8,12 @@ import kotlin.math.round
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
}
override fun printProperties() {
print(type)
}
@@ -73,6 +79,7 @@ class PtBuiltinFunctionCall(val name: String,
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
val left: PtExpression
get() = children[0] as PtExpression
val right: PtExpression
@@ -126,10 +133,12 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type!=DataType.FLOAT) {
val rounded = round(number)
if (rounded != number)
throw IllegalArgumentException("refused rounding of float to avoid loss of precision")
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
}
}
@@ -149,23 +158,16 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
}
class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
}
val segments: List<PtExpression>
get() = children.map { it as PtExpression }
override fun printProperties() {}
}
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
val value: PtExpression
get() = children.single() as PtExpression
init {
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
if(operator !in setOf("+", "-", "~"))
throw IllegalArgumentException("invalid prefix operator: $operator")
}
override fun printProperties() {
print(operator)
}
+1 -1
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.14"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
}
sourceSets {
@@ -20,5 +20,7 @@ class CompilationOptions(val output: OutputType,
var asmQuiet: Boolean = false,
var asmListfile: Boolean = false,
var experimentalCodegen: Boolean = false,
var outputDir: Path = Path("")
var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()
)
+17 -17
View File
@@ -6,12 +6,14 @@ enum class DataType {
UWORD, // pass by value
WORD, // pass by value
FLOAT, // pass by value
BOOL, // pass by value
STR, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference
ARRAY_W, // pass by reference
ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference
UNDEFINED;
/**
@@ -19,11 +21,12 @@ enum class DataType {
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT)
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
UWORD -> targetType.oneOf(UWORD, FLOAT)
WORD -> targetType.oneOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this
else -> false
@@ -53,13 +56,7 @@ enum class DataType {
enum class CpuRegister {
A,
X,
Y;
fun asRegisterOrPair(): RegisterOrPair = when(this) {
A -> RegisterOrPair.A
X -> RegisterOrPair.X
Y -> RegisterOrPair.Y
}
Y
}
enum class RegisterOrPair {
@@ -115,19 +112,20 @@ enum class BranchCondition {
}
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.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 IntegerArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F
DataType.ARRAY_F, DataType.ARRAY_BOOL
)
val PassByValueDatatypes = NumericDatatypes
val PassByReferenceDatatypes = IterableDatatypes
@@ -137,14 +135,16 @@ val ArrayToElementTypes = mapOf(
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT
DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL
)
val ElementToArrayTypes = mapOf(
DataType.BYTE to DataType.ARRAY_B,
DataType.UBYTE to DataType.ARRAY_UB,
DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F
DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL
)
val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
@@ -18,8 +18,8 @@ interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
val ESTACK_LO: UInt
val ESTACK_HI: UInt
var ESTACK_LO: UInt
var ESTACK_HI: UInt
val PROGRAM_LOAD_ADDRESS : UInt
val opcodeNames: Set<String>
@@ -32,4 +32,9 @@ interface IMachineDefinition {
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean
fun overrideEvalStack(evalStackBaseAddress: UInt) {
require(evalStackBaseAddress and 255u == 0u)
ESTACK_LO = evalStackBaseAddress
ESTACK_HI = evalStackBaseAddress + 256u
}
}
+4 -3
View File
@@ -1,10 +1,11 @@
package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val LogicalOperators = setOf("and", "or", "xor", "not")
val BitwiseOperators = setOf("&", "|", "^")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%")
val BitwiseOperators = setOf("&", "|", "^", "~")
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
fun invertedComparisonOperator(operator: String) =
when (operator) {
+9 -3
View File
@@ -1,16 +1,22 @@
package prog8.code.core
import java.nio.file.InvalidPathException
import kotlin.io.path.Path
import kotlin.io.path.absolute
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
fun toClickableStr(): String {
val path = Path(file).absolute().normalize()
return "file://$path:$line:$startCol:"
return try {
val path = Path(file).absolute().normalize().toString()
"file://$path:$line:$startCol:"
} catch(x: InvalidPathException) {
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
"file://$file:$line:$startCol:"
}
}
companion object {
val DUMMY = Position("<dummy>", 0, 0, 0)
val DUMMY = Position("~dummy~", 0, 0, 0)
}
}
+4 -4
View File
@@ -34,7 +34,7 @@ sealed class SourceCode {
* Where this [SourceCode] instance came from.
* This can be one of the following:
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [File])
* * `$stringSourcePrefix44c56085>` if was created via [String]
* * `string:44c56085` if was created via [String]
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [Resource])
*/
abstract val origin: String
@@ -55,7 +55,7 @@ sealed class SourceCode {
* filename prefix to designate library files that will be retreived from internal resources rather than disk
*/
const val libraryFilePrefix = "library:"
const val stringSourcePrefix = "<String@"
const val stringSourcePrefix = "string:"
val curdir: Path = Path(".").toAbsolutePath()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
fun isRegularFilesystemPath(pathString: String) =
@@ -64,12 +64,12 @@ sealed class SourceCode {
/**
* Turn a plain String into a [SourceCode] object.
* [origin] will be something like `$stringSourcePrefix44c56085>`.
* [origin] will be something like `string:44c56085`.
*/
class Text(override val text: String): SourceCode() {
override val isFromResources = false
override val isFromFilesystem = false
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}>"
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}"
override val name = "<unnamed-text>"
}
@@ -15,8 +15,8 @@ class AtariMachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x2000u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
override lateinit var zeropage: Zeropage
@@ -16,8 +16,8 @@ class C128MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
override lateinit var zeropage: Zeropage
@@ -16,8 +16,8 @@ class C64MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
override val ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
override var ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
override var ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
override lateinit var zeropage: Zeropage
@@ -15,8 +15,8 @@ class CX16MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x0400u // $0400-$04ff inclusive
override val ESTACK_HI = 0x0500u // $0500-$05ff inclusive
override var ESTACK_LO = 0x0400u // $0400-$04ff inclusive
override var ESTACK_HI = 0x0500u // $0500-$05ff inclusive
override lateinit var zeropage: Zeropage
@@ -17,8 +17,8 @@ class VirtualMachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val ESTACK_LO = 0u // not actually used
override val ESTACK_HI = 0u // not actually used
override var ESTACK_LO = 0u // not actually used
override var ESTACK_HI = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used
+1 -1
View File
@@ -29,7 +29,7 @@ dependencies {
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
}
@@ -18,7 +18,6 @@ import kotlin.io.path.Path
import kotlin.io.path.writeLines
internal const val generatedLabelPrefix = "prog8_label_"
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
@@ -73,19 +72,12 @@ class AsmGen(internal val program: Program,
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
private var generatedLabelSequenceNumber: Int = 0
internal fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++
return "${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
}
internal fun outputSourceLine(node: Node) {
out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
}
internal fun out(str: String, splitlines: Boolean = true) {
val fragment = (if(" | " in str) str.replace("|", "\n") else str).trim('\n')
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\n')
if (splitlines) {
for (line in fragment.splitToSequence('\n')) {
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
@@ -334,7 +326,6 @@ class AsmGen(internal val program: Program,
is RepeatLoop -> translate(stmt)
is When -> translate(stmt)
is AnonymousScope -> translate(stmt)
is Pipe -> translatePipeExpression(stmt.source, stmt.segments, stmt, isStatement = true, pushResultOnEstack = false)
is VarDecl -> { /* do nothing; variables are handled elsewhere */ }
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
@@ -444,12 +435,6 @@ class AsmGen(internal val program: Program,
internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
private fun translateBuiltinFunctionCallExpression(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine): DataType =
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, false, scope)
private fun translateBuiltinFunctionCallStatement(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine) =
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, true, scope)
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
@@ -550,7 +535,7 @@ class AsmGen(internal val program: Program,
if(jump is Jump) {
translateCompareAndJumpIfTrue(booleanCondition, jump)
} else {
val endLabel = makeLabel("if_end")
val endLabel = program.makeLabel("if_end")
translateCompareAndJumpIfFalse(booleanCondition, endLabel)
translate(stmt.truepart)
out(endLabel)
@@ -558,8 +543,8 @@ class AsmGen(internal val program: Program,
}
else {
// both true and else parts
val elseLabel = makeLabel("if_else")
val endLabel = makeLabel("if_end")
val elseLabel = program.makeLabel("if_else")
val endLabel = program.makeLabel("if_end")
translateCompareAndJumpIfFalse(booleanCondition, elseLabel)
translate(stmt.truepart)
jmp(endLabel)
@@ -575,7 +560,7 @@ class AsmGen(internal val program: Program,
}
private fun translate(stmt: RepeatLoop) {
val endLabel = makeLabel("repeatend")
val endLabel = program.makeLabel("repeatend")
loopEndLabels.push(endLabel)
when (stmt.iterations) {
@@ -628,7 +613,7 @@ class AsmGen(internal val program: Program,
private fun repeatWordCount(count: Int, stmt: RepeatLoop) {
require(count in 257..65535)
val repeatLabel = makeLabel("repeat")
val repeatLabel = program.makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt)
out("""
@@ -669,7 +654,7 @@ $repeatLabel""")
private fun repeatWordCountInAY(endLabel: String, stmt: RepeatLoop) {
// note: A/Y must have been loaded with the number of iterations!
// no need to explicitly test for 0 iterations as this is done in the countdown logic below
val repeatLabel = makeLabel("repeat")
val repeatLabel = program.makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
out("""
sta $counterVar
@@ -690,7 +675,7 @@ $repeatLabel lda $counterVar
private fun repeatByteCount(count: Int, stmt: RepeatLoop) {
require(count in 2..256)
val repeatLabel = makeLabel("repeat")
val repeatLabel = program.makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
out(" lda #${count and 255} | sta $counterVar")
@@ -708,7 +693,7 @@ $repeatLabel lda $counterVar
private fun repeatCountInY(stmt: RepeatLoop, endLabel: String) {
// note: Y must just have been loaded with the (variable) number of loops to be performed!
val repeatLabel = makeLabel("repeat")
val repeatLabel = program.makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
out(" beq $endLabel | sty $counterVar")
@@ -745,7 +730,7 @@ $repeatLabel lda $counterVar
}
}
val counterVar = makeLabel("counter")
val counterVar = program.makeLabel("counter")
when(dt) {
DataType.UBYTE, DataType.UWORD -> {
val result = zeropage.allocate(listOf(counterVar), dt, null, stmt.position, errors)
@@ -760,7 +745,7 @@ $repeatLabel lda $counterVar
}
private fun translate(stmt: When) {
val endLabel = makeLabel("choice_end")
val endLabel = program.makeLabel("choice_end")
val choiceBlocks = mutableListOf<Pair<String, AnonymousScope>>()
val conditionDt = stmt.condition.inferType(program)
if(!conditionDt.isKnown)
@@ -771,7 +756,7 @@ $repeatLabel lda $counterVar
assignExpressionToRegister(stmt.condition, RegisterOrPair.AY)
for(choice in stmt.choices) {
val choiceLabel = makeLabel("choice")
val choiceLabel = program.makeLabel("choice")
if(choice.values==null) {
// the else choice
translate(choice.statements)
@@ -835,14 +820,14 @@ $repeatLabel lda $counterVar
} else {
if(stmt.elsepart.isEmpty()) {
val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else")
val elseLabel = program.makeLabel("branch_else")
out(" $instruction $elseLabel")
translate(stmt.truepart)
out(elseLabel)
} else {
val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else")
val endLabel = makeLabel("branch_end")
val elseLabel = program.makeLabel("branch_else")
val endLabel = program.makeLabel("branch_end")
out(" $instruction $elseLabel")
translate(stmt.truepart)
jmp(endLabel)
@@ -1180,14 +1165,14 @@ $repeatLabel lda $counterVar
return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel)
when(dt) {
DataType.UBYTE, DataType.UWORD -> {
DataType.BOOL, DataType.UBYTE, DataType.UWORD -> {
if(operator=="<") {
out(" jmp $jumpIfFalseLabel")
return
} else if(operator==">=") {
return
}
if(dt==DataType.UBYTE) {
if(dt==DataType.UBYTE || dt==DataType.BOOL) {
assignExpressionToRegister(left, RegisterOrPair.A, false)
if (left is IFunctionCall && !left.isSimple)
out(" cmp #0")
@@ -1267,7 +1252,7 @@ $repeatLabel lda $counterVar
// optimized code if the expression is just an identifier (variable)
val varname = asmVariableName(variable)
when(dt) {
DataType.UBYTE -> when(operator) {
DataType.UBYTE, DataType.BOOL -> when(operator) {
"==" -> out(" lda $varname | bne $jumpIfFalseLabel")
"!=" -> out(" lda $varname | beq $jumpIfFalseLabel")
">" -> out(" lda $varname | beq $jumpIfFalseLabel")
@@ -2812,123 +2797,6 @@ $repeatLabel lda $counterVar
}
}
internal fun translatePipeExpression(source: Expression, segments: List<Expression>, scope: Node, isStatement: Boolean, pushResultOnEstack: Boolean) {
// TODO more efficient code generation to avoid needless assignments to the temp var
// the source: an expression (could be anything) producing a value.
// one or more segment expressions, all are a IFunctionCall node, and LACKING the implicit first argument.
// when 'isStatement'=true, the last segment expression should be treated as a funcion call statement (discarding any result value if there is one)
val subroutine = scope.definingSubroutine!!
var valueDt = source.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
var valueSource: AsmAssignSource =
if(source is IFunctionCall) {
val resultReg = returnRegisterOfFunction(source.target)
assignExpressionToRegister(source, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT))
AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
} else {
AsmAssignSource.fromAstSource(source, program, this)
}
// the segments (except the last one): function calls taking one or more parameters and producing a value.
// directly assign their first argument from the previous call's returnvalue (and take the rest, if any, from the call itself)
segments.dropLast(1).forEach {
it as IFunctionCall
valueDt = translateFunctionCallWithFirstArg(it, valueSource, false, subroutine)
val resultReg = returnRegisterOfFunction(it.target)
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
}
// the last segment: function call taking one or more parameters and optionally producing a result value.
val lastCall = segments.last() as IFunctionCall
if(isStatement) {
translateFunctionCallWithFirstArg(lastCall, valueSource, true, subroutine)
} else {
valueDt = translateFunctionCallWithFirstArg(lastCall, valueSource, false, subroutine)
if(pushResultOnEstack) {
when (valueDt) {
in ByteDatatypes -> {
out(" sta P8ESTACK_LO,x | dex")
}
in WordDatatypes -> {
out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
out(" jsr floats.push_fac1")
}
else -> throw AssemblyError("invalid dt")
}
}
}
}
private fun translateFunctionCallWithFirstArg(
fcall: IFunctionCall,
firstArg: AsmAssignSource,
isStatement: Boolean,
scope: Subroutine
): DataType {
if(fcall.args.isNotEmpty())
TODO("deal with additional args (non-unary function): ${fcall.target.nameInSource} (... , ${fcall.args.joinToString(", ")})")
when(val targetStmt = fcall.target.targetStatement(program)!!) {
is BuiltinFunctionPlaceholder -> {
return if(isStatement) {
translateBuiltinFunctionCallStatement(fcall, firstArg, scope)
DataType.UNDEFINED
} else {
translateBuiltinFunctionCallExpression(fcall, firstArg, scope)
}
}
is Subroutine -> {
val argDt = targetStmt.parameters.single().type
if(targetStmt.isAsmSubroutine) {
// argument via registers
val argRegister = targetStmt.asmParameterRegisters.single().registerOrPair!!
val assignArgument = AsmAssignment(
firstArg,
AsmAssignTarget.fromRegisters(argRegister, argDt in SignedDatatypes, scope, program, this),
false, program.memsizer, fcall.position
)
translateNormalAssignment(assignArgument)
} else {
val assignArgument: AsmAssignment =
if(functioncallAsmGen.optimizeIntArgsViaRegisters(targetStmt)) {
// argument goes via registers as optimization
val paramReg: RegisterOrPair = when(argDt) {
in ByteDatatypes -> RegisterOrPair.A
in WordDatatypes -> RegisterOrPair.AY
DataType.FLOAT -> RegisterOrPair.FAC1
else -> throw AssemblyError("invalid dt")
}
AsmAssignment(
firstArg,
AsmAssignTarget(TargetStorageKind.REGISTER, program, this, argDt, scope, register = paramReg),
false, program.memsizer, fcall.position
)
} else {
// arg goes via parameter variable
val argVarName = asmVariableName(targetStmt.scopedName + targetStmt.parameters.single().name)
AsmAssignment(
firstArg,
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, argDt, scope, argVarName),
false, program.memsizer, fcall.position
)
}
translateNormalAssignment(assignArgument)
}
if(targetStmt.shouldSaveX())
saveRegisterLocal(CpuRegister.X, scope)
out(" jsr ${asmSymbolName(fcall.target)}")
if(targetStmt.shouldSaveX())
restoreRegisterLocal(CpuRegister.X)
return if(isStatement) DataType.UNDEFINED else targetStmt.returntypes.single()
}
else -> throw AssemblyError("invalid call target")
}
}
internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) {
// note: because A is pushed first so popped last, saving A is often not required here.
val parameter = target.subroutineParameter
@@ -3006,7 +2874,7 @@ $repeatLabel lda $counterVar
}
}
} else {
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, target.datatype, scope, variableAsmName = asmVariableName(target.name))
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, target.datatype, scope, variableAsmName = asmVariableName(target.scopedName))
if (dt in ByteDatatypes) {
out(" pla")
assignRegister(RegisterOrPair.A, tgt)
@@ -3034,6 +2902,9 @@ $repeatLabel lda $counterVar
}
}
internal fun needAsaveForExpr(arg: Expression): Boolean =
arg !is NumericLiteral && arg !is IdentifierReference && (arg !is DirectMemoryRead || !arg.isSimple)
private val subroutineExtrasCache = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>()
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
@@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import prog8.ast.generatedLabelPrefix
import prog8.code.core.*
import java.io.File
import java.nio.file.Path
@@ -6,13 +6,11 @@ import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.BuiltinFunctionCallStatement
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.Subroutine
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
import prog8.compiler.BuiltinFunctions
import prog8.compiler.FSignature
import prog8.compiler.builtinFunctionReturnType
internal class BuiltinFunctionsAsmGen(private val program: Program,
@@ -30,44 +28,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
}
internal fun translateFunctionCallWithFirstArg(bfc: IFunctionCall, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
val name = bfc.target.nameInSource.single()
val func = BuiltinFunctions.getValue(name)
val argExpression =
when(singleArg.kind) {
SourceStorageKind.LITERALNUMBER -> singleArg.number!!
SourceStorageKind.EXPRESSION -> singleArg.expression!!
SourceStorageKind.ARRAY -> singleArg.array!!
else -> {
// TODO make it so that we can assign efficiently from something else as an expression....namely: register(s)
// this is useful in pipe expressions for instance, to skip the use of a temporary variable
// but for now, just assign it to a temporary variable and use that as a source
// Idea: to do this without having to rewrite every single function in translateFunctioncall(),
// hack a special IdentifierReference like "!6502.A/X/Y/AX/AY/XY" to reference a cpu register
val tempvar = asmgen.getTempVarName(singleArg.datatype)
val assignTempvar = AsmAssignment(
singleArg,
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, singleArg.datatype, scope, variableAsmName = asmgen.asmVariableName(tempvar)),
false, program.memsizer, Position.DUMMY
)
assignAsmGen.translateNormalAssignment(assignTempvar)
// now use an expression to assign this tempvar
val ident = IdentifierReference(tempvar, Position.DUMMY)
ident.linkParents(scope)
ident
}
}
val argExpressions = mutableListOf(argExpression)
val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY)
fcall.linkParents(scope)
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
return if(isStatement) {
DataType.UNDEFINED
} else {
builtinFunctionReturnType(func.name).getOrElse { throw AssemblyError("unknown dt") }
}
}
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if (discardResult && func.pure)
return // can just ignore the whole function call altogether
@@ -82,7 +42,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
"swap" -> funcSwap(fcall)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
@@ -512,6 +471,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
@@ -520,6 +480,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X)
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
@@ -613,6 +574,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
@@ -621,6 +583,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X)
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
@@ -655,7 +618,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
if(arrayvar.targetVarDecl(program)!!.datatype==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, null)
} else {
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
}
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
}
@@ -705,426 +674,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
}
private fun funcSwap(fcall: IFunctionCall) {
val first = fcall.args[0]
val second = fcall.args[1]
// optimized simple case: swap two variables
if(first is IdentifierReference && second is IdentifierReference) {
val firstName = asmgen.asmVariableName(first)
val secondName = asmgen.asmVariableName(second)
val dt = first.inferType(program)
if(dt istype DataType.BYTE || dt istype DataType.UBYTE) {
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | sty $secondName")
return
}
if(dt istype DataType.WORD || dt istype DataType.UWORD) {
asmgen.out("""
ldy $firstName
lda $secondName
sta $firstName
sty $secondName
ldy $firstName+1
lda $secondName+1
sta $firstName+1
sty $secondName+1
""")
return
}
if(dt istype DataType.FLOAT) {
asmgen.out("""
lda #<$firstName
sta P8ZP_SCRATCH_W1
lda #>$firstName
sta P8ZP_SCRATCH_W1+1
lda #<$secondName
sta P8ZP_SCRATCH_W2
lda #>$secondName
sta P8ZP_SCRATCH_W2+1
jsr floats.func_swap_f
""")
return
}
}
// optimized simple case: swap two memory locations
if(first is DirectMemoryRead && second is DirectMemoryRead) {
val addr1 = (first.addressExpression as? NumericLiteral)?.number?.toHex()
val addr2 = (second.addressExpression as? NumericLiteral)?.number?.toHex()
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
when {
addr1!=null && addr2!=null -> {
asmgen.out(" ldy $addr1 | lda $addr2 | sta $addr1 | sty $addr2")
return
}
addr1!=null && name2!=null -> {
asmgen.out(" ldy $addr1 | lda $name2 | sta $addr1 | sty $name2")
return
}
name1!=null && addr2 != null -> {
asmgen.out(" ldy $name1 | lda $addr2 | sta $name1 | sty $addr2")
return
}
name1!=null && name2!=null -> {
asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2")
return
}
addr1==null && addr2==null && name1==null && name2==null -> {
val firstExpr = first.addressExpression as? BinaryExpression
val secondExpr = second.addressExpression as? BinaryExpression
if(firstExpr!=null && secondExpr!=null) {
val pointerVariable = firstExpr.left as? IdentifierReference
val firstOffset = firstExpr.right
val secondOffset = secondExpr.right
if(pointerVariable != null
&& pointerVariable isSameAs secondExpr.left
&& firstExpr.operator == "+" && secondExpr.operator == "+"
&& (firstOffset is NumericLiteral || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
&& (secondOffset is NumericLiteral || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
) {
if(firstOffset is NumericLiteral && secondOffset is NumericLiteral) {
if(firstOffset!=secondOffset) {
swapArrayValues(
DataType.UBYTE,
asmgen.asmVariableName(pointerVariable), firstOffset,
asmgen.asmVariableName(pointerVariable), secondOffset
)
return
}
} else if(firstOffset is TypecastExpression && secondOffset is TypecastExpression) {
if(firstOffset.type in WordDatatypes && secondOffset.type in WordDatatypes) {
val firstOffsetVar = firstOffset.expression as? IdentifierReference
val secondOffsetVar = secondOffset.expression as? IdentifierReference
if(firstOffsetVar!=null && secondOffsetVar!=null) {
if(firstOffsetVar!=secondOffsetVar) {
swapArrayValues(
DataType.UBYTE,
asmgen.asmVariableName(pointerVariable), firstOffsetVar,
asmgen.asmVariableName(pointerVariable), secondOffsetVar
)
return
}
}
}
} else if(firstOffset is IdentifierReference || secondOffset is IdentifierReference) {
throw AssemblyError("expected a typecast-to-word for index variable at ${firstOffset.position} and/or ${secondOffset.position}")
}
}
}
}
}
}
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
val arrayVarName1 = asmgen.asmVariableName(first.arrayvar)
val arrayVarName2 = asmgen.asmVariableName(second.arrayvar)
val elementIDt = first.inferType(program)
val elementDt = elementIDt.getOrElse { throw AssemblyError("unknown dt") }
val firstNum = first.indexer.indexExpr as? NumericLiteral
val firstVar = first.indexer.indexExpr as? IdentifierReference
val secondNum = second.indexer.indexExpr as? NumericLiteral
val secondVar = second.indexer.indexExpr as? IdentifierReference
if(firstNum!=null && secondNum!=null) {
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
return
} else if(firstVar!=null && secondVar!=null) {
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondVar)
return
} else if(firstNum!=null && secondVar!=null) {
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondVar)
return
} else if(firstVar!=null && secondNum!=null) {
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondNum)
return
}
}
// all other types of swap() calls are done via a temporary variable
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
return when (expr) {
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine, variableAsmName = asmgen.asmVariableName(expr))
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine, array = expr)
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine, memory = DirectMemoryWrite(expr.addressExpression, expr.position))
else -> throw AssemblyError("invalid expression object $expr")
}
}
when(val datatype: DataType = first.inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes, in WordDatatypes -> {
asmgen.assignExpressionToVariable(first, "P8ZP_SCRATCH_W1", datatype, null)
asmgen.assignExpressionToVariable(second, "P8ZP_SCRATCH_W2", datatype, null)
val assignFirst = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
targetFromExpr(first, datatype),
false, program.memsizer, first.position
)
val assignSecond = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
targetFromExpr(second, datatype),
false, program.memsizer, second.position
)
asmgen.translateNormalAssignment(assignFirst)
asmgen.translateNormalAssignment(assignSecond)
}
DataType.FLOAT -> {
// via temp variable and FAC1
asmgen.assignExpressionTo(first, AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.FLOAT, first.definingSubroutine, "floats.tempvar_swap_float"))
asmgen.assignExpressionTo(second, AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, null, register=RegisterOrPair.FAC1))
asmgen.translateNormalAssignment(
AsmAssignment(
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, datatype, register = RegisterOrPair.FAC1),
targetFromExpr(first, datatype),
false, program.memsizer, first.position
)
)
asmgen.translateNormalAssignment(
AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, "floats.tempvar_swap_float"),
targetFromExpr(second, datatype),
false, program.memsizer, second.position
)
)
}
else -> throw AssemblyError("weird swap dt")
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexValue2: NumericLiteral) {
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
when(elementDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out("""
lda $arrayVarName1+$index1
ldy $arrayVarName2+$index2
sta $arrayVarName2+$index2
sty $arrayVarName1+$index1
""")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out("""
lda $arrayVarName1+$index1
ldy $arrayVarName2+$index2
sta $arrayVarName2+$index2
sty $arrayVarName1+$index1
lda $arrayVarName1+$index1+1
ldy $arrayVarName2+$index2+1
sta $arrayVarName2+$index2+1
sty $arrayVarName1+$index1+1
""")
}
DataType.FLOAT -> {
asmgen.out("""
lda #<(${arrayVarName1}+$index1)
sta P8ZP_SCRATCH_W1
lda #>(${arrayVarName1}+$index1)
sta P8ZP_SCRATCH_W1+1
lda #<(${arrayVarName2}+$index2)
sta P8ZP_SCRATCH_W2
lda #>(${arrayVarName2}+$index2)
sta P8ZP_SCRATCH_W2+1
jsr floats.func_swap_f
""")
}
else -> throw AssemblyError("invalid aray elt type")
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexName2: IdentifierReference) {
val idxAsmName1 = asmgen.asmVariableName(indexName1)
val idxAsmName2 = asmgen.asmVariableName(indexName2)
when(elementDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
ldx $idxAsmName1
ldy $idxAsmName2
lda $arrayVarName1,x
pha
lda $arrayVarName2,y
sta $arrayVarName1,x
pla
sta $arrayVarName2,y
ldx P8ZP_SCRATCH_REG
""")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
lda $idxAsmName1
asl a
tax
lda $idxAsmName2
asl a
tay
lda $arrayVarName1,x
pha
lda $arrayVarName2,y
sta $arrayVarName1,x
pla
sta $arrayVarName2,y
lda $arrayVarName1+1,x
pha
lda $arrayVarName2+1,y
sta $arrayVarName1+1,x
pla
sta $arrayVarName2+1,y
ldx P8ZP_SCRATCH_REG
""")
}
DataType.FLOAT -> {
asmgen.out("""
lda #>$arrayVarName1
sta P8ZP_SCRATCH_W1+1
lda $idxAsmName1
asl a
asl a
clc
adc $idxAsmName1
adc #<$arrayVarName1
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ lda #>$arrayVarName2
sta P8ZP_SCRATCH_W2+1
lda $idxAsmName2
asl a
asl a
clc
adc $idxAsmName2
adc #<$arrayVarName2
sta P8ZP_SCRATCH_W2
bcc +
inc P8ZP_SCRATCH_W2+1
+ jsr floats.func_swap_f
""")
}
else -> throw AssemblyError("invalid aray elt type")
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexName2: IdentifierReference) {
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
val idxAsmName2 = asmgen.asmVariableName(indexName2)
when(elementDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out("""
lda $arrayVarName1 + $index1
pha
ldy $idxAsmName2
lda $arrayVarName2,y
sta $arrayVarName1 + $index1
pla
sta $arrayVarName2,y
""")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out("""
lda $arrayVarName1 + $index1
pha
lda $idxAsmName2
asl a
tay
lda $arrayVarName2,y
sta $arrayVarName1 + $index1
pla
sta $arrayVarName2,y
lda $arrayVarName1 + $index1+1
pha
lda $arrayVarName2+1,y
sta $arrayVarName1 + $index1+1
pla
sta $arrayVarName2+1,y
""")
}
DataType.FLOAT -> {
asmgen.out("""
lda #<(${arrayVarName1}+$index1)
sta P8ZP_SCRATCH_W1
lda #>(${arrayVarName1}+$index1)
sta P8ZP_SCRATCH_W1+1
lda #>$arrayVarName1
sta P8ZP_SCRATCH_W1+1
lda $idxAsmName2
asl a
asl a
clc
adc $idxAsmName2
adc #<$arrayVarName1
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ jsr floats.func_swap_f
""")
}
else -> throw AssemblyError("invalid aray elt type")
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteral) {
val idxAsmName1 = asmgen.asmVariableName(indexName1)
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
when(elementDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out("""
lda $arrayVarName2 + $index2
pha
ldy $idxAsmName1
lda $arrayVarName1,y
sta $arrayVarName2 + $index2
pla
sta $arrayVarName1,y
""")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out("""
lda $arrayVarName2 + $index2
pha
lda $idxAsmName1
asl a
tay
lda $arrayVarName1,y
sta $arrayVarName2 + $index2
pla
sta $arrayVarName1,y
lda $arrayVarName2 + $index2+1
pha
lda $arrayVarName1+1,y
sta $arrayVarName2 + $index2+1
pla
sta $arrayVarName1+1,y
""")
}
DataType.FLOAT -> {
asmgen.out("""
lda #>$arrayVarName1
sta P8ZP_SCRATCH_W1+1
lda $idxAsmName1
asl a
asl a
clc
adc $idxAsmName1
adc #<$arrayVarName1
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ lda #<(${arrayVarName2}+$index2)
sta P8ZP_SCRATCH_W2
lda #>(${arrayVarName2}+$index2)
sta P8ZP_SCRATCH_W2+1
jsr floats.func_swap_f
""")
}
else -> throw AssemblyError("invalid aray elt type")
}
}
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope)
val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)
@@ -1312,12 +861,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
} else {
val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteral || fcall.args[0] is IdentifierReference)
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
if(!needAsave) {
val mr0 = fcall.args[0] as? DirectMemoryRead
val mr1 = fcall.args[1] as? DirectMemoryRead
if (mr0 != null)
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference
if (mr1 != null)
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral && mr1.addressExpression !is IdentifierReference)
}
@@ -34,8 +34,6 @@ internal class ExpressionsAsmGen(private val program: Program,
is IdentifierReference -> translateExpression(expression)
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PipeExpression -> asmgen.translatePipeExpression(expression.source, expression.segments,
expression, isStatement = false, pushResultOnEstack = true )
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
@@ -138,7 +136,7 @@ internal class ExpressionsAsmGen(private val program: Program,
private fun translateExpression(typecast: TypecastExpression) {
translateExpressionInternal(typecast.expression)
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> {
DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> {
@@ -534,8 +532,8 @@ internal class ExpressionsAsmGen(private val program: Program,
}
in ComparisonOperators -> {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.constValue(program)?.number?.toInt()
if(rightVal==0)
val rightVal = expr.right.constValue(program)?.number
if(rightVal==0.0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
@@ -562,6 +560,42 @@ internal class ExpressionsAsmGen(private val program: Program,
}
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
if(expr.isSimple) {
if(operator=="!=") {
when (dt) {
in ByteDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
asmgen.out("""
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
in WordDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
DataType.FLOAT -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
asmgen.out("""
jsr floats.SIGN
sta P8ESTACK_LO,x
dex""")
return
}
else -> {}
}
}
/* operator == is not worth it to special case, the code is mostly larger */
}
translateExpressionInternal(expr)
when(operator) {
"==" -> {
@@ -666,22 +700,6 @@ internal class ExpressionsAsmGen(private val program: Program,
else -> throw AssemblyError("weird type")
}
}
"not" -> {
when(type) {
// if reg==0 ->
/*
lda P8ESTACK_LO+1,x
beq +
lda #1
+ eor #1
sta P8ESTACK_LO+1,x
rts
*/
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.not_byte")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.not_word")
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
}
}
@@ -692,6 +710,25 @@ internal class ExpressionsAsmGen(private val program: Program,
throw AssemblyError("unknown dt")
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!!
if(arrayVarDecl.datatype==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayExpr.inferType(program) isnot DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(arrayExpr.arrayvar)) {
asmgen.out(" lda ($arrayVarName),y")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
}
asmgen.out(" sta P8ESTACK_LO,x | dex")
return
}
val constIndexNum = arrayExpr.indexer.constIndex()
if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
@@ -767,9 +804,6 @@ internal class ExpressionsAsmGen(private val program: Program,
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
"and" -> asmgen.out(" jsr prog8_lib.and_b")
"or" -> asmgen.out(" jsr prog8_lib.or_b")
"xor" -> asmgen.out(" jsr prog8_lib.xor_b")
else -> throw AssemblyError("invalid operator $operator")
}
}
@@ -801,9 +835,6 @@ internal class ExpressionsAsmGen(private val program: Program,
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
"and" -> asmgen.out(" jsr prog8_lib.and_w")
"or" -> asmgen.out(" jsr prog8_lib.or_w")
"xor" -> asmgen.out(" jsr prog8_lib.xor_w")
else -> throw AssemblyError("invalid operator $operator")
}
}
@@ -820,7 +851,7 @@ internal class ExpressionsAsmGen(private val program: Program,
">=" -> asmgen.out(" jsr floats.greatereq_f")
"==" -> asmgen.out(" jsr floats.equal_f")
"!=" -> asmgen.out(" jsr floats.notequal_f")
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
"%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
else -> throw AssemblyError("invalid operator $operator")
}
}
@@ -31,10 +31,10 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
}
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
val modifiedLabel = program.makeLabel("for_modified")
val modifiedLabel2 = program.makeLabel("for_modifiedb")
asmgen.loopEndLabels.push(endLabel)
val stepsize=range.step.constValue(program)!!.number.toInt()
@@ -238,8 +238,8 @@ $endLabel""")
}
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident)
val decl = ident.targetVarDecl(program)!!
@@ -263,7 +263,7 @@ $endLabel""")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
val length = decl.arraysize!!.constIndex()!!
val indexVar = asmgen.makeLabel("for_index")
val indexVar = program.makeLabel("for_index")
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
@@ -299,7 +299,7 @@ $loopLabel sty $indexVar
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
val length = decl.arraysize!!.constIndex()!! * 2
val indexVar = asmgen.makeLabel("for_index")
val indexVar = program.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
asmgen.out("""
ldy #0
@@ -359,8 +359,8 @@ $loopLabel sty $indexVar
}
// not one of the easy cases, generate more complex code...
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
@@ -471,8 +471,8 @@ $loopLabel""")
}
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out("""
@@ -497,8 +497,8 @@ $endLabel""")
}
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out("""
@@ -534,8 +534,8 @@ $endLabel""")
}
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out("""
@@ -561,8 +561,8 @@ $loopLabel""")
}
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out("""
@@ -111,10 +111,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} else {
// 2 byte params, second in Y, first in A
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
if(!call.args[1].isSimple)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
if(!call.args[1].isSimple)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pla")
}
} else {
@@ -159,7 +159,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
argOrder.forEach {
val param = callee.parameters[it]
val targetVar = callee.searchAsmParameter(param.name)!!
val targetVar = callee.searchParameter(param.name)!!
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine)
}
}
@@ -70,8 +70,17 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition:
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
asmgen.out(".endweak")
if(options.symbolDefs.isNotEmpty()) {
asmgen.out("; -- user supplied symbols on the command line")
for((name, value) in options.symbolDefs) {
asmgen.out("$name = $value")
}
}
when(options.output) {
OutputType.RAW -> {
@@ -127,11 +136,8 @@ internal class ProgramAndVarsGen(
"cx16" -> {
if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr main.start | lda #4 | sta $01")
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
asmgen.out(" jsr main.start")
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr main.start | lda #31 | sta $01")
@@ -589,7 +595,7 @@ internal class ProgramAndVarsGen(
}
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"")
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
for (chunk in outputBytes.chunked(16))
@@ -116,9 +116,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
}
}
println(" number of allocated vars: $numberOfAllocatableVariables")
println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
println(" zeropage free space: ${zeropage.free.size} bytes")
// println(" number of allocated vars: $numberOfAllocatableVariables")
// println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
// println(" zeropage free space: ${zeropage.free.size} bytes")
}
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
@@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.Subroutine
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen
@@ -37,7 +40,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
val origAstTarget: AssignTarget? = null
)
{
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
val asmVarname: String by lazy {
if (array == null)
@@ -122,9 +124,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val expression: Expression? = null
)
{
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
val asmVarname: String
get() = if(array==null)
variableAsmName!!
@@ -132,8 +131,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
asmgen.asmVariableName(array.arrayvar)
companion object {
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
val cv = value.constValue(program)
if(cv!=null)
@@ -72,6 +72,25 @@ internal class AssignmentAsmGen(private val program: Program,
val value = assign.source.array!!
val elementDt = assign.source.datatype
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
val arrayVarDecl = value.arrayvar.targetVarDecl(program)!!
if(arrayVarDecl.datatype==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")
if(value.inferType(program) isnot DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(value.arrayvar)) {
asmgen.out(" lda ($arrayVarName),y")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
}
assignRegisterByte(assign.target, CpuRegister.A)
return
}
val constIndex = value.indexer.constIndex()
if (constIndex!=null) {
// constant array index value
@@ -263,7 +282,6 @@ internal class AssignmentAsmGen(private val program: Program,
"+" -> {}
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype)
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype)
"not" -> augmentableAsmGen.inplaceBooleanNot(target, target.datatype)
else -> throw AssemblyError("invalid prefix operator")
}
}
@@ -271,35 +289,8 @@ internal class AssignmentAsmGen(private val program: Program,
containmentCheckIntoA(value)
assignRegisterByte(assign.target, CpuRegister.A)
}
is PipeExpression -> {
asmgen.translatePipeExpression(value.source, value.segments, value, false, false)
val resultDt = value.inferType(program)
val register =
if(resultDt.isBytes) RegisterOrPair.A
else if(resultDt.isWords) RegisterOrPair.AY
else if(resultDt istype DataType.FLOAT) RegisterOrPair.FAC1
else throw AssemblyError("invalid dt")
asmgen.assignRegister(register, assign.target)
}
is BinaryExpression -> {
if(value.operator in ComparisonOperators) {
// TODO real optimized code for comparison expressions that yield a boolean result value
assignConstantByte(assign.target, 0)
val origTarget = assign.target.origAstTarget
if(origTarget!=null) {
val assignTrue = AnonymousScope(mutableListOf(
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
), assign.position)
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
val ifelse = IfElse(value.copy(), assignTrue, assignFalse, assign.position)
ifelse.linkParents(value)
asmgen.translate(ifelse)
}
else {
// no orig ast assign target so can't use the workaround, so fallback to stack eval
fallbackToStackEval(assign)
}
} else {
if(!attemptAssignOptimizedBinexpr(value, assign)) {
// All remaining binary expressions just evaluate via the stack for now.
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
// because the code here is the implementation of exactly that...)
@@ -310,6 +301,240 @@ internal class AssignmentAsmGen(private val program: Program,
}
}
private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean {
if(expr.operator in ComparisonOperators) {
if(expr.right.constValue(program)?.number == 0.0) {
if(expr.operator == "==" || expr.operator=="!=") {
when(assign.target.datatype) {
in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true
else -> {
// do nothing, this is handled by a type cast.
}
}
}
}
val origTarget = assign.target.origAstTarget
if(origTarget!=null) {
assignConstantByte(assign.target, 0)
val assignTrue = AnonymousScope(mutableListOf(
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
), assign.position)
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
val ifelse = IfElse(expr.copy(), assignTrue, assignFalse, assign.position)
ifelse.linkParents(expr)
asmgen.translate(ifelse)
return true
}
}
if(!expr.inferType(program).isInteger)
return false
if(expr.operator!="+" && expr.operator!="-")
return false
val dt = expr.inferType(program).getOrElse { throw AssemblyError("invalid dt") }
val left = expr.left
val right = expr.right
if(dt in ByteDatatypes) {
when (right) {
is IdentifierReference -> {
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
val symname = asmgen.asmVariableName(right)
if(expr.operator=="+")
asmgen.out(" clc | adc $symname")
else
asmgen.out(" sec | sbc $symname")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
is NumericLiteral -> {
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
if(expr.operator=="+")
asmgen.out(" clc | adc #${right.number.toHex()}")
else
asmgen.out(" sec | sbc #${right.number.toHex()}")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else -> return false
}
} else if(dt in WordDatatypes) {
when (right) {
is AddressOf -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
val symbol = asmgen.asmVariableName(right.identifier)
if(expr.operator=="+")
asmgen.out("""
clc
adc #<$symbol
pha
tya
adc #>$symbol
tay
pla""")
else
asmgen.out("""
sec
sbc #<$symbol
pha
tya
sbc #>$symbol
tay
pla""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
is IdentifierReference -> {
val symname = asmgen.asmVariableName(right)
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
if(expr.operator=="+")
asmgen.out("""
clc
adc $symname
pha
tya
adc $symname+1
tay
pla""")
else
asmgen.out("""
sec
sbc $symname
pha
tya
sbc $symname+1
tay
pla""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
is NumericLiteral -> {
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
if(expr.operator=="+") {
asmgen.out("""
clc
adc #<${right.number.toHex()}
pha
tya
adc #>${right.number.toHex()}
tay
pla""")
} else if(expr.operator=="-") {
asmgen.out("""
sec
sbc #<${right.number.toHex()}
pha
tya
sbc #>${right.number.toHex()}
tay
pla""")
}
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
is TypecastExpression -> {
val castedValue = right.expression
if(right.type in WordDatatypes && castedValue.inferType(program).isBytes) {
if(castedValue is IdentifierReference) {
val castedSymname = asmgen.asmVariableName(castedValue)
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
if(expr.operator=="+")
asmgen.out("""
clc
adc $castedSymname
bcc +
iny
+""")
else
asmgen.out("""
sec
sbc $castedSymname
bcs +
dey
+""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true
}
}
}
else -> return false
}
}
return false
}
private fun attemptAssignToByteCompareZero(expr: BinaryExpression, assign: AsmAssignment): Boolean {
when (expr.operator) {
"==" -> {
when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | and #1 | eor #1")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
"!=" -> {
when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
else -> return false
}
}
private fun fallbackToStackEval(assign: AsmAssignment) {
// TODO DON'T STACK-EVAL... perhaps by using a temp var? so that it becomes augmentable assignment expression?
// or don't try to solve it here in this one case and rather rewrite the whole stack based value evaluation.
@@ -566,6 +791,20 @@ internal class AssignmentAsmGen(private val program: Program,
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes)
} else {
if(!(valueDt isAssignableTo targetDt)) {
if(valueDt in WordDatatypes && targetDt in ByteDatatypes) {
// word to byte, just take the lsb
return assignCastViaLsbFunc(value, target)
} else if(valueDt in WordDatatypes && targetDt in WordDatatypes) {
// word to word, just assign
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
} else if(valueDt in ByteDatatypes && targetDt in ByteDatatypes) {
// byte to byte, just assign
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
}
else
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker")
}
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
}
return
@@ -634,7 +873,7 @@ internal class AssignmentAsmGen(private val program: Program,
when(sourceDt) {
DataType.UBYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
DataType.BYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.UWORD, DataType.WORD -> {
@@ -657,7 +896,7 @@ internal class AssignmentAsmGen(private val program: Program,
}
DataType.BYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
DataType.UBYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.UWORD -> {
@@ -687,7 +926,7 @@ internal class AssignmentAsmGen(private val program: Program,
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
DataType.WORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
DataType.FLOAT -> {
@@ -708,7 +947,7 @@ internal class AssignmentAsmGen(private val program: Program,
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
DataType.UWORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
DataType.FLOAT -> {
@@ -749,7 +988,7 @@ internal class AssignmentAsmGen(private val program: Program,
when(sourceDt) {
DataType.UBYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
DataType.BYTE -> {
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
}
DataType.UWORD, DataType.WORD -> {
@@ -783,7 +1022,7 @@ internal class AssignmentAsmGen(private val program: Program,
}
DataType.BYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
DataType.UBYTE -> {
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
}
DataType.UWORD -> {
@@ -830,7 +1069,7 @@ internal class AssignmentAsmGen(private val program: Program,
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
DataType.WORD -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
@@ -858,7 +1097,7 @@ internal class AssignmentAsmGen(private val program: Program,
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
DataType.UWORD -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
@@ -1365,6 +1604,22 @@ internal class AssignmentAsmGen(private val program: Program,
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
// assigning an indexed pointer var
if (target.constArrayIndexValue==0u) {
asmgen.out(" lda $sourceName")
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
} else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y")
} else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
asmgen.out(" lda $sourceName | sta (P8ZP_SCRATCH_W2),y")
}
}
return
}
if (target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
@@ -1897,6 +2152,22 @@ internal class AssignmentAsmGen(private val program: Program,
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
// assigning an indexed pointer var
if (target.constArrayIndexValue==0u) {
asmgen.out(" lda #0")
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
} else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
asmgen.out(" lda #0 | sta (${target.asmVarname}),y")
} else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
asmgen.out(" lda #0 | sta (P8ZP_SCRATCH_W2),y")
}
}
return
}
if (target.constArrayIndexValue!=null) {
val indexValue = target.constArrayIndexValue!!
asmgen.out(" stz ${target.asmVarname}+$indexValue")
@@ -1915,10 +2186,7 @@ internal class AssignmentAsmGen(private val program: Program,
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
in Cx16VirtualRegisters -> {
asmgen.out(
" stz cx16.${
target.register.toString().lowercase()
} | stz cx16.${target.register.toString().lowercase()}+1")
asmgen.out(" stz cx16.${target.register.toString().lowercase()} | stz cx16.${target.register.toString().lowercase()}+1")
}
else -> throw AssemblyError("weird register")
}
@@ -1940,13 +2208,29 @@ internal class AssignmentAsmGen(private val program: Program,
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
// assigning an indexed pointer var
if (target.constArrayIndexValue==0u) {
asmgen.out(" lda #${byte.toHex()}")
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
} else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y")
} else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
asmgen.out(" lda #${byte.toHex()} | sta (P8ZP_SCRATCH_W2),y")
}
}
return
}
if (target.constArrayIndexValue!=null) {
val indexValue = target.constArrayIndexValue!!
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y")
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y")
}
}
TargetStorageKind.REGISTER -> when(target.register!!) {
@@ -28,7 +28,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"+" -> {}
"-" -> inplaceNegate(target, type)
"~" -> inplaceInvert(target, type)
"not" -> inplaceBooleanNot(target, type)
else -> throw AssemblyError("invalid prefix operator")
}
}
@@ -391,9 +390,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
asmgen.storeAIntoZpPointerVar(sourceName)
@@ -428,9 +427,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&", "and" -> asmgen.out(" and $otherName")
"|", "or" -> asmgen.out(" ora $otherName")
"^", "xor" -> asmgen.out(" eor $otherName")
"&" -> asmgen.out(" and $otherName")
"|" -> asmgen.out(" ora $otherName")
"^" -> asmgen.out(" eor $otherName")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
asmgen.storeAIntoZpPointerVar(sourceName)
@@ -485,17 +484,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.storeAIntoZpPointerVar(sourceName)
}
}
"&", "and" -> {
"&" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and #$value")
asmgen.storeAIntoZpPointerVar(sourceName)
}
"|", "or" -> {
"|"-> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" ora #$value")
asmgen.storeAIntoZpPointerVar(sourceName)
}
"^", "xor" -> {
"^" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
asmgen.storeAIntoZpPointerVar(sourceName)
@@ -563,15 +562,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&", "and" -> {
"&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
}
"|", "or" -> {
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
"^", "xor" -> {
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
@@ -649,9 +648,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
"==" -> {
asmgen.out("""
lda $otherName
@@ -740,9 +739,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
}
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
"&" -> asmgen.out(" lda $name | and #$value | sta $name")
"|" -> asmgen.out(" lda $name | ora #$value | sta $name")
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
"==" -> {
asmgen.out("""
lda $name
@@ -785,15 +784,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sbc P8ZP_SCRATCH_B1
sta $name""")
}
"|", "or" -> {
"|" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" ora $name | sta $name")
}
"&", "and" -> {
"&" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" and $name | sta $name")
}
"^", "xor" -> {
"^" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" eor $name | sta $name")
}
@@ -828,11 +827,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
dec $name+1
+""")
}
"|", "or" -> {
"|" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" ora $name | sta $name")
}
"&", "and" -> {
"&" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) {
@@ -842,7 +841,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1")
}
}
"^", "xor" -> {
"^" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" eor $name | sta $name")
}
@@ -1059,7 +1058,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
}
"&", "and" -> {
"&" -> {
when {
value == 0 -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
@@ -1096,7 +1095,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
}
}
"|", "or" -> {
"|" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
@@ -1104,7 +1103,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
}
}
"^", "xor" -> {
"^" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
@@ -1268,7 +1267,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&", "and" -> {
"&" -> {
asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
@@ -1277,8 +1276,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1")
}
}
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1349,9 +1348,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""")
}
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
"&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1521,7 +1520,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&", "and" -> {
"&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) {
@@ -1531,11 +1530,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1")
}
}
"|", "or" -> {
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
"^", "xor" -> {
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
@@ -1567,15 +1566,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
remainderVarByWordInAY()
}
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&", "and" -> {
"&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
}
"|", "or" -> {
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
}
"^", "xor" -> {
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
}
@@ -1800,136 +1799,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
internal fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
when (dt) {
DataType.UBYTE -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
beq +
lda #1
+ eor #1
sta ${target.asmVarname}""")
}
TargetStorageKind.MEMORY -> {
val mem = target.memory!!
when (mem.addressExpression) {
is NumericLiteral -> {
val addr = (mem.addressExpression as NumericLiteral).number.toHex()
asmgen.out("""
lda $addr
beq +
lda #1
+ eor #1
sta $addr""")
}
is IdentifierReference -> {
val sourceName = asmgen.loadByteFromPointerIntoA(mem.addressExpression as IdentifierReference)
asmgen.out("""
beq +
lda #1
+ eor #1""")
asmgen.storeAIntoZpPointerVar(sourceName)
}
else -> {
asmgen.assignExpressionToVariable(mem.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
asmgen.out("""
beq +
lda #1
+ eor #1""")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
}
}
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.A -> asmgen.out("""
cmp #0
beq +
lda #1
+ eor #1""")
RegisterOrPair.X -> asmgen.out("""
txa
beq +
lda #1
+ eor #1
tax""")
RegisterOrPair.Y -> asmgen.out("""
tya
beq +
lda #1
+ eor #1
tay""")
else -> throw AssemblyError("invalid reg dt for byte not")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for byte stack not")
else -> throw AssemblyError("no asm gen for in-place not of ubyte ${target.kind}")
}
}
DataType.UWORD -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
ora ${target.asmVarname}+1
beq +
lda #1
+ eor #1
sta ${target.asmVarname}
lsr a
sta ${target.asmVarname}+1""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.AX -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
ora P8ZP_SCRATCH_REG
beq +
lda #0
tax
beq ++
+ lda #1
+""")
}
RegisterOrPair.AY -> {
asmgen.out("""
sty P8ZP_SCRATCH_REG
ora P8ZP_SCRATCH_REG
beq +
lda #0
tay
beq ++
+ lda #1
+""")
}
RegisterOrPair.XY -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
tya
ora P8ZP_SCRATCH_REG
beq +
ldy #0
ldx #0
beq ++
+ ldx #1
+""")
}
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
else -> throw AssemblyError("invalid reg dt for word not")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for word stack not")
else -> throw AssemblyError("no asm gen for in-place not of uword for ${target.kind}")
}
}
else -> throw AssemblyError("boolean-not of invalid type")
}
}
internal fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
when (dt) {
DataType.UBYTE -> {
+1 -1
View File
@@ -28,7 +28,7 @@ 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.14"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
}
@@ -135,6 +135,8 @@ class AstToXmlConverter(internal val program: PtProgram,
xml.attr("nosysinit", options.noSysInit.toString())
xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
xml.attr("optimize", options.optimize.toString())
if(options.evalStackBaseAddress!=null)
xml.attr("evalstackbase", options.evalStackBaseAddress!!.toString())
if(options.zpReserved.isNotEmpty()) {
xml.startChildren()
options.zpReserved.forEach {
@@ -144,6 +146,15 @@ class AstToXmlConverter(internal val program: PtProgram,
xml.endElt()
}
}
if(options.symbolDefs.isNotEmpty()) {
xml.startChildren()
options.symbolDefs.forEach { name, value ->
xml.elt("symboldef")
xml.attr("name", name)
xml.attr("value", value)
xml.endElt()
}
}
xml.endElt()
}
@@ -172,7 +183,6 @@ class AstToXmlConverter(internal val program: PtProgram,
is PtMemoryByte -> write(it)
is PtMemMapped -> write(it)
is PtNumber -> write(it)
is PtPipe -> write(it)
is PtPostIncrDecr -> write(it)
is PtPrefix -> write(it)
is PtRange -> write(it)
@@ -204,14 +214,6 @@ class AstToXmlConverter(internal val program: PtProgram,
xml.endElt()
}
private fun write(pipe: PtPipe) {
xml.elt("pipe")
xml.attr("type", pipe.type.name)
xml.startChildren()
pipe.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(array: PtArray) {
xml.elt("array")
xml.attr("type", array.type.name)
+2 -2
View File
@@ -24,12 +24,12 @@ compileTestKotlin {
}
dependencies {
implementation project(':virtualmachine')
implementation project(':codeAst')
implementation project(':codeCore')
implementation project(':virtualmachine')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
}
@@ -13,8 +13,7 @@ import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
internal class AssemblyProgram(override val name: String,
private val allocations: VariableAllocator
class AssemblyProgram(override val name: String, private val allocations: VariableAllocator
) : IAssemblyProgram {
private val globalInits = mutableListOf<VmCodeLine>()
@@ -68,11 +67,12 @@ internal class AssemblyProgram(override val name: String,
fun addGlobalInits(chunk: VmCodeChunk) = globalInits.addAll(chunk.lines)
fun addBlock(block: VmCodeChunk) = blocks.add(block)
fun getBlocks(): List<VmCodeChunk> = blocks
}
internal sealed class VmCodeLine
sealed class VmCodeLine
internal class VmCodeInstruction(
class VmCodeInstruction(
opcode: Opcode,
type: VmDataType?=null,
reg1: Int?=null, // 0-$ffff
@@ -82,39 +82,39 @@ internal class VmCodeInstruction(
value: Int?=null, // 0-$ffff
fpValue: Float?=null,
labelSymbol: List<String>?=null // alternative to value for branch/call/jump labels
): VmCodeLine() {
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
): VmCodeLine() {
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
init {
if(reg1!=null && (reg1<0 || reg1>65536))
throw IllegalArgumentException("reg1 out of bounds")
if(reg2!=null && (reg2<0 || reg2>65536))
throw IllegalArgumentException("reg2 out of bounds")
if(fpReg1!=null && (fpReg1<0 || fpReg1>65536))
throw IllegalArgumentException("fpReg1 out of bounds")
if(fpReg2!=null && (fpReg2<0 || fpReg2>65536))
throw IllegalArgumentException("fpReg2 out of bounds")
init {
if(reg1!=null && (reg1<0 || reg1>65536))
throw IllegalArgumentException("reg1 out of bounds")
if(reg2!=null && (reg2<0 || reg2>65536))
throw IllegalArgumentException("reg2 out of bounds")
if(fpReg1!=null && (fpReg1<0 || fpReg1>65536))
throw IllegalArgumentException("fpReg1 out of bounds")
if(fpReg2!=null && (fpReg2<0 || fpReg2>65536))
throw IllegalArgumentException("fpReg2 out of bounds")
if(value!=null && opcode !in OpcodesWithAddress) {
when (type) {
VmDataType.BYTE -> {
if (value < -128 || value > 255)
throw IllegalArgumentException("value out of range for byte: $value")
}
VmDataType.WORD -> {
if (value < -32768 || value > 65535)
throw IllegalArgumentException("value out of range for word: $value")
}
VmDataType.FLOAT, null -> {}
if(value!=null && opcode !in OpcodesWithAddress) {
when (type) {
VmDataType.BYTE -> {
if (value < -128 || value > 255)
throw IllegalArgumentException("value out of range for byte: $value")
}
VmDataType.WORD -> {
if (value < -32768 || value > 65535)
throw IllegalArgumentException("value out of range for word: $value")
}
VmDataType.FLOAT, null -> {}
}
}
}
}
internal class VmCodeLabel(val name: List<String>): VmCodeLine()
class VmCodeLabel(val name: List<String>): VmCodeLine()
internal class VmCodeComment(val comment: String): VmCodeLine()
internal class VmCodeChunk(initial: VmCodeLine? = null) {
class VmCodeChunk(initial: VmCodeLine? = null) {
val lines = mutableListOf<VmCodeLine>()
init {
@@ -110,9 +110,6 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += VmCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
}
"not" -> {
code += VmCodeInstruction(Opcode.NOTM, vmDt, value = address)
}
else -> throw AssemblyError("weird prefix operator")
}
return code
@@ -159,6 +156,19 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
val variable = array.variable.targetName
var variableAddr = codeGen.allocations.get(variable)
val itemsize = codeGen.program.memsizer.memorySize(array.type)
if(array.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(itemsize!=1)
throw AssemblyError("non-array var indexing requires bytes dt")
if(array.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
val idxReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(array.index, idxReg, -1)
code += VmCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, value = variableAddr)
return code
}
val fixedIndex = constIntValue(array.index)
if(zero) {
if(fixedIndex!=null) {
@@ -41,11 +41,10 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
"mkword" -> funcMkword(call, resultRegister)
"sort" -> funcSort(call)
"reverse" -> funcReverse(call)
"swap" -> funcSwap(call)
"rol" -> funcRolRor2(Opcode.ROXL, call, resultRegister)
"ror" -> funcRolRor2(Opcode.ROXR, call, resultRegister)
"rol2" -> funcRolRor2(Opcode.ROL, call, resultRegister)
"ror2" -> funcRolRor2(Opcode.ROR, call, resultRegister)
"rol" -> funcRolRor(Opcode.ROXL, call, resultRegister)
"ror" -> funcRolRor(Opcode.ROXR, call, resultRegister)
"rol2" -> funcRolRor(Opcode.ROL, call, resultRegister)
"ror2" -> funcRolRor(Opcode.ROR, call, resultRegister)
else -> TODO("builtinfunc ${call.name}")
}
}
@@ -112,21 +111,21 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
}
DataType.BYTE -> {
val andReg = codeGen.vmRegisters.nextFree()
val notNegativeLabel = codeGen.createLabelName()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=andReg, value=0x80)
code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=andReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=andReg, labelSymbol = notNegativeLabel)
val compareReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
code += VmCodeLabel(notNegativeLabel)
}
DataType.WORD -> {
val andReg = codeGen.vmRegisters.nextFree()
val notNegativeLabel = codeGen.createLabelName()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=andReg, value=0x8000)
code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=andReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=andReg, labelSymbol = notNegativeLabel)
val compareReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000)
code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
code += VmCodeLabel(notNegativeLabel)
}
@@ -184,19 +183,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
return code
}
private fun funcSwap(call: PtBuiltinFunctionCall): VmCodeChunk {
val left = call.args[0]
val right = call.args[1]
val leftReg = codeGen.vmRegisters.nextFree()
val rightReg = codeGen.vmRegisters.nextFree()
val code = VmCodeChunk()
code += exprGen.translateExpression(left, leftReg, -1)
code += exprGen.translateExpression(right, rightReg, -1)
code += assignRegisterTo(left, rightReg)
code += assignRegisterTo(right, leftReg)
return code
}
private fun funcReverse(call: PtBuiltinFunctionCall): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
@@ -224,7 +210,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
DataType.ARRAY_UW -> Syscall.SORT_UWORD
DataType.ARRAY_W -> Syscall.SORT_WORD
DataType.STR -> Syscall.SORT_UBYTE
DataType.ARRAY_F -> throw java.lang.IllegalArgumentException("sorting a floating point array is not supported")
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
else -> throw IllegalArgumentException("weird type to sort")
}
val code = VmCodeChunk()
@@ -368,7 +354,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
return code
}
private fun funcRolRor2(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val vmDt = codeGen.vmType(call.args[0].type)
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
@@ -40,7 +40,7 @@ class CodeGen(internal val program: PtProgram,
internal val errors: IErrorReporter
): IAssemblyGenerator {
internal val allocations = VariableAllocator(symbolTable, program, errors)
internal val allocations = VariableAllocator(symbolTable, program)
private val expressionEval = ExpressionGen(this)
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
private val assignmentGen = AssignmentGen(this, expressionEval)
@@ -58,11 +58,21 @@ class CodeGen(internal val program: PtProgram,
}
}
if(options.symbolDefs.isNotEmpty())
throw AssemblyError("virtual target doesn't support symbols defined on the commandline")
if(options.evalStackBaseAddress!=null)
throw AssemblyError("virtual target doesn't use eval-stack")
for (block in program.allBlocks()) {
vmprog.addBlock(translate(block))
}
println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}")
if(options.optimize) {
val optimizer = VmPeepholeOptimizer(vmprog, allocations)
optimizer.optimize()
}
println("Vm codegen: virtual registers=${vmRegisters.peekNext()} memory usage=${allocations.freeMem}")
return vmprog
}
@@ -84,7 +94,6 @@ class CodeGen(internal val program: PtProgram,
is PtReturn -> translate(node)
is PtJump -> translate(node)
is PtWhen -> translate(node)
is PtPipe -> expressionEval.translate(node, 0)
is PtForLoop -> translate(node)
is PtIfElse -> translate(node)
is PtPostIncrDecr -> translate(node)
@@ -340,14 +349,10 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
}
else -> {
val valueReg = vmRegisters.nextFree()
if(value>0) {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= value)
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, reg2 = valueReg)
}
else {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= -value)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = valueReg)
code += if(value>0) {
VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, value=value)
} else {
VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, value=-value)
}
}
}
@@ -378,13 +383,13 @@ class CodeGen(internal val program: PtProgram,
if(value>0) {
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = valueReg, reg2 = operandReg)
code += VmCodeInstruction(Opcode.ADDR, dt, reg1 = valueReg, reg2 = operandReg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
}
else {
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = valueReg, reg2 = operandReg)
code += VmCodeInstruction(Opcode.SUBR, dt, reg1 = valueReg, reg2 = operandReg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
}
}
@@ -396,12 +401,10 @@ class CodeGen(internal val program: PtProgram,
val code = VmCodeChunk()
if(factor==1f)
return code
if(factor==0f) {
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
code += if(factor==0f) {
VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
} else {
val factorReg = vmRegisters.nextFreeFloat()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += VmCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
VmCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
}
return code
}
@@ -437,13 +440,10 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += VmCodeInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
} else {
if (factor == 0) {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
}
else {
val factorReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
code += VmCodeInstruction(Opcode.MUL, dt, reg1=reg, reg2=factorReg)
code += if (factor == 0) {
VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
} else {
VmCodeInstruction(Opcode.MUL, dt, reg1=reg, value=factor)
}
}
return code
@@ -480,12 +480,10 @@ class CodeGen(internal val program: PtProgram,
val code = VmCodeChunk()
if(factor==1f)
return code
if(factor==0f) {
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
code += if(factor==0f) {
VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
} else {
val factorReg = vmRegisters.nextFreeFloat()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += VmCodeInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
VmCodeInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
}
return code
}
@@ -527,16 +525,13 @@ class CodeGen(internal val program: PtProgram,
else
VmCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
} else {
if (factor == 0) {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
}
else {
val factorReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
code += if(signed)
VmCodeInstruction(Opcode.DIVS, dt, reg1=reg, reg2=factorReg)
code += if (factor == 0) {
VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
} else {
if(signed)
VmCodeInstruction(Opcode.DIVS, dt, reg1=reg, value=factor)
else
VmCodeInstruction(Opcode.DIV, dt, reg1=reg, reg2=factorReg)
VmCodeInstruction(Opcode.DIV, dt, reg1=reg, value=factor)
}
}
return code
@@ -806,6 +801,7 @@ class CodeGen(internal val program: PtProgram,
internal fun vmType(type: DataType): VmDataType {
return when(type) {
DataType.BOOL,
DataType.UBYTE,
DataType.BYTE -> VmDataType.BYTE
DataType.UWORD,
@@ -63,7 +63,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
is PtPipe -> code += translate(expr, resultRegister)
is PtRange,
is PtArray,
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
@@ -72,46 +71,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
return code
}
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
val segments = pipe.segments
var valueDt = segments[0].type
var valueReg = if(pipe.void) codeGen.vmRegisters.nextFree() else resultRegister
fun addImplicitArgToSegment(segment: PtExpression, sourceReg: Int, sourceDt: DataType): PtExpression {
return when (segment) {
is PtFunctionCall -> {
val segWithArg = PtFunctionCall(segment.functionName, segment.void, segment.type, segment.position)
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
segWithArg.children.addAll(segment.args)
segWithArg
}
is PtBuiltinFunctionCall -> {
val segWithArg = PtBuiltinFunctionCall(segment.name, segment.void, segment.hasNoSideEffects, segment.type, segment.position)
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
segWithArg.children.addAll(segment.args)
segWithArg
}
else -> throw AssemblyError("weird segment type")
}
}
val code = VmCodeChunk()
code += translateExpression(segments[0], valueReg, -1)
for (segment in segments.subList(1, segments.size-1)) {
val sourceReg = valueReg
val sourceDt = valueDt
if(segment.type!=valueDt) {
valueDt = segment.type
valueReg = codeGen.vmRegisters.nextFree()
}
val segmentWithImplicitArgument = addImplicitArgToSegment(segment, sourceReg, sourceDt)
code += translateExpression(segmentWithImplicitArgument, valueReg, -1)
}
val segWithArg = addImplicitArgToSegment(segments.last(), valueReg, valueDt)
code += translateExpression(segWithArg, resultRegister, -1)
return code
}
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
code += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister
@@ -149,6 +108,18 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
val code = VmCodeChunk()
val idxReg = codeGen.vmRegisters.nextFree()
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
if(arrayIx.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(eltSize!=1)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayIx.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
code += translateExpression(arrayIx.index, idxReg, -1)
code += VmCodeInstruction(Opcode.LOADIX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation)
return code
}
if(arrayIx.index is PtNumber) {
// optimized code when index is known - just calculate the memory address here
val memOffset = (arrayIx.index as PtNumber).number.toInt() * eltSize
@@ -178,13 +149,8 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += VmCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
}
"~" -> {
val regMask = codeGen.vmRegisters.nextFree()
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask)
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=regMask)
}
"not" -> {
code += VmCodeInstruction(Opcode.NOT, vmDt, reg1=resultRegister)
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, value=mask)
}
else -> throw AssemblyError("weird prefix operator")
}
@@ -253,18 +219,18 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
}
}
DataType.FLOAT -> {
when(cast.value.type) {
code += when(cast.value.type) {
DataType.UBYTE -> {
code += VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.BYTE -> {
code += VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.UWORD -> {
code += VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.WORD -> {
code += VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
else -> throw AssemblyError("weird cast value type")
}
@@ -283,9 +249,9 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
"*" -> operatorMultiply(binExpr, vmDt, resultRegister, resultFpRegister)
"/" -> operatorDivide(binExpr, vmDt, resultRegister, resultFpRegister, signed)
"%" -> operatorModulo(binExpr, vmDt, resultRegister)
"|", "or" -> operatorOr(binExpr, vmDt, resultRegister)
"&", "and" -> operatorAnd(binExpr, vmDt, resultRegister)
"^", "xor" -> operatorXor(binExpr, vmDt, resultRegister)
"|" -> operatorOr(binExpr, vmDt, resultRegister)
"&" -> operatorAnd(binExpr, vmDt, resultRegister)
"^" -> operatorXor(binExpr, vmDt, resultRegister)
"<<" -> operatorShiftLeft(binExpr, vmDt, resultRegister)
">>" -> operatorShiftRight(binExpr, vmDt, resultRegister, signed)
"==" -> operatorEquals(binExpr, vmDt, resultRegister, false)
@@ -420,13 +386,9 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
comparisonCall.children.add(binExpr.left)
comparisonCall.children.add(binExpr.right)
code += translate(comparisonCall, resultRegister, -1)
if(notEquals) {
val maskReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=maskReg, value=1)
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=maskReg)
} else {
code += VmCodeInstruction(Opcode.NOT, vmDt, reg1=resultRegister)
}
if(!notEquals)
code += VmCodeInstruction(Opcode.INV, vmDt, reg1=resultRegister)
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
@@ -496,10 +458,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
private fun operatorXor(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=rightResultReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.XORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@@ -513,10 +480,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
private fun operatorAnd(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=rightResultReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += VmCodeInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.ANDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@@ -530,10 +502,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
private fun operatorOr(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=rightResultReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += VmCodeInstruction(Opcode.OR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.ORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@@ -550,9 +527,14 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
throw IllegalArgumentException("floating-point modulo not supported")
val code = VmCodeChunk()
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=rightResultReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += VmCodeInstruction(Opcode.MOD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.MODR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@@ -573,9 +555,9 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += if(signed)
VmCodeInstruction(Opcode.DIVS, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
VmCodeInstruction(Opcode.DIVSR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
else
VmCodeInstruction(Opcode.DIV, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
VmCodeInstruction(Opcode.DIVR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
@@ -584,12 +566,20 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += codeGen.divideByConst(vmDt, resultRegister, factor, signed)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += if(signed)
VmCodeInstruction(Opcode.DIVS, vmDt, reg1=resultRegister, reg2=rightResultReg)
else
VmCodeInstruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=rightResultReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += if (signed)
VmCodeInstruction(Opcode.DIVS, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
else
VmCodeInstruction(Opcode.DIV, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += if (signed)
VmCodeInstruction(Opcode.DIVSR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
else
VmCodeInstruction(Opcode.DIVR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
}
return code
@@ -643,7 +633,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += VmCodeInstruction(Opcode.MUL, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
code += VmCodeInstruction(Opcode.MULR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
}
} else {
if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
@@ -658,7 +648,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=rightResultReg)
code += VmCodeInstruction(Opcode.MULR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
@@ -697,10 +687,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += VmCodeInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
}
else {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += VmCodeInstruction(Opcode.SUB, vmDt, fpReg1=resultFpRegister, fpReg2=rightResultFpReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += VmCodeInstruction(Opcode.SUB, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
} else {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += VmCodeInstruction(Opcode.SUBR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
}
}
} else {
if((binExpr.right as? PtNumber)?.number==1.0) {
@@ -708,10 +703,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
}
else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=rightResultReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += VmCodeInstruction(Opcode.SUB, vmDt, reg1 = resultRegister, value = (binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.SUBR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
}
return code
@@ -753,10 +753,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += VmCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
}
else {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += VmCodeInstruction(Opcode.ADD, vmDt, fpReg1=resultFpRegister, fpReg2=rightResultFpReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += VmCodeInstruction(Opcode.ADD, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
} else {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += VmCodeInstruction(Opcode.ADDR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
}
}
} else {
if((binExpr.left as? PtNumber)?.number==1.0) {
@@ -768,10 +773,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
code += VmCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
}
else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=rightResultReg)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += VmCodeInstruction(Opcode.ADD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.ADDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
}
return code
@@ -4,7 +4,7 @@ import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.*
class VariableAllocator(private val st: SymbolTable, private val program: PtProgram, errors: IErrorReporter) {
class VariableAllocator(private val st: SymbolTable, private val program: PtProgram) {
private val allocations = mutableMapOf<List<String>, Int>()
private var freeMemoryStart: Int
@@ -26,6 +26,18 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg
allocations[variable.scopedName] = nextLocation
nextLocation += memsize
}
for (memvar in st.allMemMappedVariables) {
// TODO virtual machine doesn't have memory mapped variables, so treat them as regular allocated variables for now
val memsize =
when (memvar.dt) {
in NumericDatatypes -> program.memsizer.memorySize(memvar.dt)
in ArrayDatatypes -> program.memsizer.memorySize(memvar.dt, memvar.length!!)
else -> throw InternalCompilerException("weird dt")
}
allocations[memvar.scopedName] = nextLocation
nextLocation += memsize
}
freeMemoryStart = nextLocation
}
@@ -0,0 +1,189 @@
package prog8.codegen.virtual
import prog8.vm.Instruction
import prog8.vm.Opcode
import prog8.vm.VmDataType
internal class VmOptimizerException(msg: String): Exception(msg)
class VmPeepholeOptimizer(private val vmprog: AssemblyProgram, private val allocations: VariableAllocator) {
fun optimize() {
vmprog.getBlocks().forEach { block ->
do {
val indexedInstructions = block.lines.withIndex()
.filter { it.value is VmCodeInstruction }
.map { IndexedValue(it.index, (it.value as VmCodeInstruction).ins) }
val changed = removeNops(block, indexedInstructions)
|| removeDoubleLoadsAndStores(block, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(block, indexedInstructions)
|| removeWeirdBranches(block, indexedInstructions)
|| removeDoubleSecClc(block, indexedInstructions)
|| cleanupPushPop(block, indexedInstructions)
// TODO other optimizations:
// more complex optimizations such as unused registers
} while(changed)
}
}
private fun cleanupPushPop(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// push followed by pop to same target, or different target->replace with load
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode==Opcode.PUSH) {
if(idx < block.lines.size-1) {
val insAfter = block.lines[idx+1] as? VmCodeInstruction
if(insAfter!=null && insAfter.ins.opcode ==Opcode.POP) {
if(ins.reg1==insAfter.ins.reg1) {
block.lines.removeAt(idx)
block.lines.removeAt(idx)
} else {
block.lines[idx] = VmCodeInstruction(Opcode.LOADR, ins.type, reg1=insAfter.ins.reg1, reg2=ins.reg1)
block.lines.removeAt(idx+1)
}
changed = true
}
}
}
}
return changed
}
private fun removeDoubleSecClc(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// double sec, clc
// sec+clc or clc+sec
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode==Opcode.SEC || ins.opcode==Opcode.CLC) {
if(idx < block.lines.size-1) {
val insAfter = block.lines[idx+1] as? VmCodeInstruction
if(insAfter?.ins?.opcode == ins.opcode) {
block.lines.removeAt(idx)
changed = true
}
else if(ins.opcode==Opcode.SEC && insAfter?.ins?.opcode==Opcode.CLC) {
block.lines.removeAt(idx)
changed = true
}
else if(ins.opcode==Opcode.CLC && insAfter?.ins?.opcode==Opcode.SEC) {
block.lines.removeAt(idx)
changed = true
}
}
}
}
return changed
}
private fun removeWeirdBranches(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// jump/branch to label immediately below
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode==Opcode.JUMP && ins.labelSymbol!=null) {
// if jumping to label immediately following this
if(idx < block.lines.size-1) {
val label = block.lines[idx+1] as? VmCodeLabel
if(label?.name == ins.labelSymbol) {
block.lines.removeAt(idx)
changed = true
}
}
}
}
return changed
}
private fun removeUselessArithmetic(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
when (ins.opcode) {
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
if (ins.value == 1) {
block.lines.removeAt(idx)
changed = true
}
}
Opcode.ADD, Opcode.SUB -> {
if (ins.value == 1) {
block.lines[idx] = VmCodeInstruction(
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
ins.type,
ins.reg1
)
changed = true
} else if (ins.value == 0) {
block.lines.removeAt(idx)
changed = true
}
}
Opcode.AND -> {
if (ins.value == 0) {
block.lines[idx] = VmCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
changed = true
} else if (ins.value == 255 && ins.type == VmDataType.BYTE) {
block.lines.removeAt(idx)
changed = true
} else if (ins.value == 65535 && ins.type == VmDataType.WORD) {
block.lines.removeAt(idx)
changed = true
}
}
Opcode.OR -> {
if (ins.value == 0) {
block.lines.removeAt(idx)
changed = true
} else if ((ins.value == 255 && ins.type == VmDataType.BYTE) || (ins.value == 65535 && ins.type == VmDataType.WORD)) {
block.lines[idx] = VmCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
changed = true
}
}
Opcode.XOR -> {
if (ins.value == 0) {
block.lines.removeAt(idx)
changed = true
}
}
else -> {}
}
}
return changed
}
private fun removeNops(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.NOP) {
changed = true
block.lines.removeAt(idx)
}
}
return changed
}
private fun removeDoubleLoadsAndStores(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
var changed = false
indexedInstructions.forEach { (idx, ins) ->
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
// TODO: detect multiple float ffrom/fto to the same target, only keep first
// TODO: detect multiple sequential rnd with same reg1, only keep one
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
// TODO: detect multiple same ands, ors; only keep first
// ...
}
return changed
}
}
private interface ICodeChange { // TODO not used? remove?
fun perform(block: VmCodeChunk)
class Remove(val idx: Int): ICodeChange {
override fun perform(block: VmCodeChunk) {
block.lines.removeAt(idx)
}
}
}
@@ -22,6 +22,9 @@ class BinExprSplitter(private val program: Program, private val options: Compila
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(options.compTarget.name == VMTarget.NAME)
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
return noModifications
@@ -22,9 +22,6 @@ class ConstExprEvaluator {
"&" -> bitwiseand(left, right)
"|" -> bitwiseor(left, right)
"^" -> bitwisexor(left, right)
"and" -> logicaland(left, right)
"or" -> logicalor(left, right)
"xor" -> logicalxor(left, right)
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
">" -> NumericLiteral.fromBoolean(left > right, left.position)
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
@@ -58,57 +55,6 @@ class ConstExprEvaluator {
return NumericLiteral(left.type, result.toDouble(), left.position)
}
private fun logicalxor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-bitxor $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicalor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-or $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicaland(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) {
@@ -1,19 +1,15 @@
package prog8.optimizer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.ExpressionError
import prog8.ast.*
import prog8.ast.base.FatalAstException
import prog8.ast.base.UndefinedSymbolError
import prog8.ast.expressions.*
import prog8.ast.statements.ForLoop
import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType
import prog8.code.core.IntegerDatatypes
import prog8.compiler.BuiltinFunctions
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@@ -35,59 +31,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
// Try to turn a unary prefix expression into a single constant value.
// Compile-time constant sub expressions will be evaluated on the spot.
// For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
val subexpr = expr.expression
if (subexpr is NumericLiteral) {
// accept prefixed literal values (such as -3, not true)
return when (expr.operator) {
"+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent))
"-" -> when (subexpr.type) {
in IntegerDatatypes -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral.optimalInteger(-subexpr.number.toInt(), subexpr.position),
parent))
}
DataType.FLOAT -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.FLOAT, -subexpr.number, subexpr.position),
parent))
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
"~" -> when (subexpr.type) {
DataType.BYTE -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.BYTE, subexpr.number.toInt().inv().toDouble(), subexpr.position),
parent))
}
DataType.UBYTE -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.UBYTE, (subexpr.number.toInt().inv() and 255).toDouble(), subexpr.position),
parent))
}
DataType.WORD -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.WORD, subexpr.number.toInt().inv().toDouble(), subexpr.position),
parent))
}
DataType.UWORD -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.UWORD, (subexpr.number.toInt().inv() and 65535).toDouble(), subexpr.position),
parent))
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
"not" -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral.fromBoolean(subexpr.number == 0.0, subexpr.position),
parent))
}
else -> throw ExpressionError(expr.operator, subexpr.position)
}
}
return noModifications
val constValue = expr.constValue(program) ?: return noModifications
return listOf(IAstModification.ReplaceNode(expr, constValue, parent))
}
/*
@@ -192,6 +137,21 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
modifications += IAstModification.ReplaceNode(expr, result, parent)
}
if(leftconst==null && rightconst!=null && rightconst.number<0.0) {
if (expr.operator == "-") {
// X - -1 ---> X + 1
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
val plusExpr = BinaryExpression(expr.left, "+", posNumber, expr.position)
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
}
else if (expr.operator == "+") {
// X + -1 ---> X - 1
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
val plusExpr = BinaryExpression(expr.left, "-", posNumber, expr.position)
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
}
}
val leftBinExpr = expr.left as? BinaryExpression
val rightBinExpr = expr.right as? BinaryExpression
@@ -302,7 +262,6 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
// the args of a fuction are constfolded via recursion already.
val constvalue = functionCallExpr.constValue(program)
return if(constvalue!=null)
listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent))
@@ -310,6 +269,14 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
noModifications
}
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
val constvalue = bfc.constValue(program)
return if(constvalue!=null)
listOf(IAstModification.ReplaceNode(bfc, constvalue, parent))
else
noModifications
}
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
val fromCast = rangeFrom.cast(targetDt)
@@ -423,7 +390,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
// both operators are the same.
// If associative, we can simply shuffle the const operands around to optimize.
if(expr.operator in AssociativeOperators) {
if(expr.operator in AssociativeOperators && maySwapOperandOrder(expr)) {
return if(leftIsConst) {
if(subleftIsConst)
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
@@ -1,9 +1,15 @@
package prog8.optimizer
import prog8.ast.*
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.Assignment
import prog8.ast.statements.IfElse
import prog8.ast.statements.Jump
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
@@ -13,7 +19,7 @@ import kotlin.math.pow
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
class ExpressionSimplifier(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
class ExpressionSimplifier(private val program: Program) : AstWalker() {
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
@@ -80,12 +86,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
throw FatalAstException("can't determine datatype of both expression operands $expr")
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null)
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
return listOf(IAstModification.SwapOperands(expr))
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
if(parent !is Assignment || !(expr.left isSameAs parent.target))
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr))
return listOf(IAstModification.SwapOperands(expr))
}
@@ -187,35 +193,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
// boolvar & 1 --> boolvar
// boolvar & 2 --> false
if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
if(rightVal?.number==1.0) {
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
} else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) {
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
}
}
// simplify when a term is constant and directly determines the outcome
val constTrue = NumericLiteral.fromBoolean(true, expr.position)
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
val newExpr: Expression? = when (expr.operator) {
"or" -> {
when {
leftVal != null && leftVal.asBooleanValue || rightVal != null && rightVal.asBooleanValue -> constTrue
leftVal != null && !leftVal.asBooleanValue -> expr.right
rightVal != null && !rightVal.asBooleanValue -> expr.left
else -> null
}
}
"and" -> {
when {
leftVal != null && !leftVal.asBooleanValue || rightVal != null && !rightVal.asBooleanValue -> constFalse
leftVal != null && leftVal.asBooleanValue -> expr.right
rightVal != null && rightVal.asBooleanValue -> expr.left
else -> null
}
}
"xor" -> {
when {
leftVal != null && !leftVal.asBooleanValue -> expr.right
rightVal != null && !rightVal.asBooleanValue -> expr.left
leftVal != null && leftVal.asBooleanValue -> PrefixExpression("not", expr.right, expr.right.position)
rightVal != null && rightVal.asBooleanValue -> PrefixExpression("not", expr.left, expr.left.position)
else -> null
}
}
"|" -> {
when {
leftVal?.number==0.0 -> expr.right
@@ -259,6 +249,28 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else -> null
}
if(rightVal!=null && leftDt==DataType.BOOL) {
// see if we can replace comparison against true/1 with simpler comparison against zero
if (expr.operator == "==") {
if (rightVal.number == 1.0) {
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
return listOf(
IAstModification.SetExpression({expr.operator="!="}, expr, parent),
IAstModification.ReplaceNode(expr.right, zero, expr)
)
}
}
if (expr.operator == "!=") {
if (rightVal.number == 1.0) {
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
return listOf(
IAstModification.SetExpression({expr.operator="=="}, expr, parent),
IAstModification.ReplaceNode(expr.right, zero, expr)
)
}
}
}
if(newExpr != null)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
@@ -277,6 +289,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
}
} else {
if(arg is IdentifierReference && arg.nameInSource.size==2
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
// lsb(cx16.r0) -> cx16.r0L
val highReg = IdentifierReference(listOf("cx16", arg.nameInSource[1]+'L'), arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program)
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
// useless lsb() of byte value
@@ -298,6 +316,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
parent))
}
} else {
if(arg is IdentifierReference && arg.nameInSource.size==2
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
// msb(cx16.r0) -> cx16.r0H
val highReg = IdentifierReference(listOf("cx16", arg.nameInSource[1]+'H'), arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program)
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
// useless msb() of byte value, replace with 0
@@ -312,49 +336,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return noModifications
}
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
val range = containment.iterable as? RangeExpression
if(range!=null && range.step.constValue(program)?.number==1.0) {
val from = range.from.constValue(program)
val to = range.to.constValue(program)
val value = containment.element
if(from!=null && to!=null && value.isSimple) {
if(to.number-from.number>6.0) {
// replace containment test with X>=from and X<=to
val left = BinaryExpression(value, ">=", from, containment.position)
val right = BinaryExpression(value.copy(), "<=", to, containment.position)
val comparison = BinaryExpression(left, "and", right, containment.position)
return listOf(IAstModification.ReplaceNode(containment, comparison, parent))
}
}
}
return noModifications
}
override fun after(pipeExpr: PipeExpression, parent: Node) = processPipe(pipeExpr, parent)
override fun after(pipe: Pipe, parent: Node) = processPipe(pipe, parent)
private fun processPipe(pipe: IPipe, parent: Node): Iterable<IAstModification> {
if(pipe.source.isSimple) {
val segments = pipe.segments
if(segments.size==1) {
// replace the whole pipe with a normal function call
val funcname = (segments[0] as IFunctionCall).target
val call = if(pipe is Pipe)
FunctionCallStatement(funcname, mutableListOf(pipe.source), true, pipe.position)
else
FunctionCallExpression(funcname, mutableListOf(pipe.source), pipe.position)
return listOf(IAstModification.ReplaceNode(pipe as Node, call, parent))
} else if(segments.size>1) {
// replace source+firstsegment by firstsegment(source) call as the new source
val firstSegment = segments.removeAt(0) as IFunctionCall
val call = FunctionCallExpression(firstSegment.target, mutableListOf(pipe.source), pipe.position)
return listOf(IAstModification.ReplaceNode(pipe.source, call, pipe as Node))
}
}
return noModifications
}
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
return when {
subBinExpr.left isSameAs x -> subBinExpr.right
@@ -646,7 +627,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteral?): BinExprWithConstants {
if (expr.operator in AssociativeOperators && leftVal != null) {
if (expr.operator in AssociativeOperators && leftVal != null && maySwapOperandOrder(expr)) {
// swap left and right so that right is always the constant
val tmp = expr.left
expr.left = expr.right
@@ -60,8 +60,8 @@ fun Program.inlineSubroutines(): Int {
return inliner.applyModifications()
}
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
val opti = ExpressionSimplifier(this, errors)
fun Program.simplifyExpressions() : Int {
val opti = ExpressionSimplifier(this)
opti.visit(this)
return opti.applyModifications()
}
+45 -12
View File
@@ -14,6 +14,8 @@ import prog8.code.core.InternalCompilerException
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
class Inliner(val program: Program): AstWalker() {
class DetermineInlineSubs(val program: Program): IAstVisitor {
@@ -142,8 +144,10 @@ class Inliner(val program: Program): AstWalker() {
}
private fun makeFullyScoped(call: FunctionCallExpression) {
val sub = call.target.targetSubroutine(program)!!
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)
val scopedCall = FunctionCallExpression(call.target.copy(), scopedArgs.toMutableList(), call.position)
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
}
@@ -168,41 +172,70 @@ class Inliner(val program: Program): AstWalker() {
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
val sub = gosub.identifier.targetStatement(program) as? Subroutine
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
return if(sub==null)
noModifications
else
possibleInlineFcallStmt(sub, gosub, parent)
}
private fun possibleInlineFcallStmt(sub: Subroutine, origNode: Node, parent: Node): Iterable<IAstModification> {
if(sub.inline && sub.parameters.isEmpty()) {
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))
return if(sub.isAsmSubroutine) {
// simply insert the asm for the argument-less routine
listOf(IAstModification.ReplaceNode(gosub, sub.statements.single().copy(), parent))
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
} else {
// note that we don't have to process any args, because we online inline parameterless subroutines.
when (val toInline = sub.statements.first()) {
is Return -> noModifications
else -> listOf(IAstModification.ReplaceNode(gosub, toInline.copy(), parent))
is Return -> {
val fcall = toInline.value as? FunctionCallExpression
if(fcall!=null) {
// insert the function call expression as a void function call directly
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
listOf(IAstModification.ReplaceNode(origNode, call, parent))
} else
noModifications
}
else -> listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
}
}
}
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
return if(sub==null)
noModifications
else
possibleInlineFcallStmt(sub, functionCallStatement, parent)
}
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
return if(sub.isAsmSubroutine) {
// simply insert the asm for the argument-less routine
listOf(IAstModification.ReplaceNode(functionCallStatement, sub.statements.single().copy(), parent))
// cannot inline assembly directly in the Ast here as an Asm node is not an expression....
noModifications
} else {
// note that we don't have to process any args, because we online inline parameterless subroutines.
when (val toInline = sub.statements.first()) {
is Return -> noModifications
else -> listOf(IAstModification.ReplaceNode(functionCallStatement, toInline.copy(), parent))
is Return -> {
// is an expression, so we have to have a Return here in the inlined sub
// note that we don't have to process any args, because we online inline parameterless subroutines.
if(toInline.value!=null)
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
else
noModifications
}
else -> noModifications
}
}
}
return noModifications
}
// TODO also inline function call expressions, and remove it from the StatementOptimizer
}
@@ -16,46 +16,6 @@ class StatementOptimizer(private val program: Program,
private val compTarget: ICompilationTarget
) : AstWalker() {
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
// if the first instruction in the called subroutine is a return statement with a simple value (NOT being a parameter),
// remove the jump altogeter and inline the returnvalue directly. (only if not part of a pipe expression)
fun scopePrefix(variable: IdentifierReference): IdentifierReference {
val target = variable.targetStatement(program) as INamedStatement
return IdentifierReference(target.scopedName, variable.position)
}
val subroutine = functionCallExpr.target.targetSubroutine(program)
if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Return && first.value?.isSimple==true && parent !is IPipe) {
val copy = when(val orig = first.value!!) {
is AddressOf -> {
val scoped = scopePrefix(orig.identifier)
AddressOf(scoped, orig.position)
}
is DirectMemoryRead -> {
when(val expr = orig.addressExpression) {
is NumericLiteral -> DirectMemoryRead(expr.copy(), orig.position)
else -> return noModifications
}
}
is IdentifierReference -> {
if(orig.targetVarDecl(program)?.origin == VarDeclOrigin.SUBROUTINEPARAM)
return noModifications
else
scopePrefix(orig)
}
is NumericLiteral -> orig.copy()
is StringLiteral -> orig.copy()
else -> return noModifications
}
return listOf(IAstModification.ReplaceNode(functionCallExpr, copy, parent))
}
}
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(functionCallStatement.target.nameInSource.size==1) {
val functionName = functionCallStatement.target.nameInSource[0]
@@ -148,7 +108,7 @@ class StatementOptimizer(private val program: Program,
// empty true part? switch with the else part
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
val invertedCondition = PrefixExpression("not", ifElse.condition, ifElse.condition.position)
val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, ifElse.condition.position), ifElse.condition.position)
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
return listOf(
@@ -313,7 +273,7 @@ class StatementOptimizer(private val program: Program,
val op1 = binExpr.operator
val op2 = rExpr.operator
if(rExpr.left is NumericLiteral && op2 in AssociativeOperators) {
if(rExpr.left is NumericLiteral && op2 in AssociativeOperators && maySwapOperandOrder(binExpr)) {
// associative operator, make sure the constant numeric value is second (right)
return listOf(IAstModification.SwapOperands(rExpr))
}
@@ -352,7 +312,7 @@ class StatementOptimizer(private val program: Program,
if(binExpr.operator in AssociativeOperators && binExpr.right isSameAs assignment.target) {
// associative operator, swap the operands so that the assignment target is first (left)
// unless the other operand is the same in which case we don't swap (endless loop!)
if (!(binExpr.left isSameAs binExpr.right))
if (!(binExpr.left isSameAs binExpr.right) && maySwapOperandOrder(binExpr))
return listOf(IAstModification.SwapOperands(binExpr))
}
@@ -1,7 +1,10 @@
package prog8.optimizer
import prog8.ast.*
import prog8.ast.expressions.*
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression
import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
@@ -28,23 +31,23 @@ class UnusedCodeRemover(private val program: Program,
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
reportUnreachable(breakStmt)
return emptyList()
return noModifications
}
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
reportUnreachable(jump)
return emptyList()
return noModifications
}
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
reportUnreachable(returnStmt)
return emptyList()
return noModifications
}
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(functionCallStatement.target.nameInSource.last() == "exit")
reportUnreachable(functionCallStatement)
return emptyList()
return noModifications
}
private fun reportUnreachable(stmt: Statement) {
@@ -236,7 +239,6 @@ class UnusedCodeRemover(private val program: Program,
is PrefixExpression,
is BinaryExpression,
is TypecastExpression,
is PipeExpression,
is IFunctionCall -> { /* don't remove */ }
else -> {
if(assign1.value !is IFunctionCall)
+3 -2
View File
@@ -34,13 +34,14 @@ dependencies {
implementation project(':codeGenCpu6502')
implementation project(':codeGenVirtual')
implementation project(':codeGenExperimental')
implementation project(':virtualmachine')
implementation 'org.antlr:antlr4-runtime:4.10.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.1.0'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
}
configurations.all {
+1
View File
@@ -23,5 +23,6 @@
<orderEntry type="module" module-name="codeGenCpu6502" />
<orderEntry type="module" module-name="codeGenExperimental" />
<orderEntry type="module" module-name="codeGenVirtual" />
<orderEntry type="module" module-name="virtualmachine" />
</component>
</module>
-3
View File
@@ -1,8 +1,5 @@
; Prog8 definitions for the Atari800XL
; Including memory registers, I/O registers, Basic and Kernal subroutines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
atari {
-2
View File
@@ -1,6 +1,4 @@
; Prog8 definitions for the Text I/O and Screen routines for the Atari 800XL
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%import syslib
%import conv
-2
View File
@@ -1,6 +1,4 @@
; Prog8 definitions for floating point handling on the Commodore 128
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%option enable_floats
%import floats_functions
+1 -3
View File
@@ -1,8 +1,5 @@
; Prog8 definitions for the Commodore-128
; Including memory registers, I/O registers, Basic and Kernal subroutines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
c64 {
@@ -725,6 +722,7 @@ cx16 {
; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack.
; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless
; you're doing insane nesting of expressions...)
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
&uword r0 = $1b00
&uword r1 = $1b02
&uword r2 = $1b04
-4
View File
@@ -1,8 +1,4 @@
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
%import syslib
%import conv
-5
View File
@@ -1,8 +1,4 @@
; Prog8 definitions for floating point handling on the Commodore-64
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
%option enable_floats
%import floats_functions
@@ -13,7 +9,6 @@ floats {
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586
float tempvar_swap_float ; used for some swap() operations
; ---- C64 basic and kernal ROM float constants and functions ----
+6 -2
View File
@@ -38,8 +38,12 @@ graphics {
if y1>y2 {
; make sure dy is always positive to have only 4 instead of 8 special cases
swap(x1, x2)
swap(y1, y2)
cx16.r0 = x1
x1 = x2
x2 = cx16.r0
cx16.r0L = y1
y1 = y2
y2 = cx16.r0L
}
word @zp dx = (x2 as word)-x1
word @zp dy = (y2 as word)-y1
+1 -4
View File
@@ -1,9 +1,5 @@
; Prog8 definitions for the Commodore-64
; Including memory registers, I/O registers, Basic and Kernal subroutines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
c64 {
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
@@ -689,6 +685,7 @@ cx16 {
; (because there's no room for them in the zeropage)
; they are allocated at the bottom of the eval-stack (should be ample space unless
; you're doing insane nesting of expressions...)
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
&uword r0 = $cf00
&uword r1 = $cf02
&uword r2 = $cf04
-4
View File
@@ -1,8 +1,4 @@
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
%import syslib
%import conv
-3
View File
@@ -1,7 +1,4 @@
; Number conversions routines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
conv {
+98 -2
View File
@@ -1,6 +1,4 @@
; Cx16 specific disk drive I/O routines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%import diskio
@@ -73,4 +71,102 @@ cx16diskio {
}}
}
; replacement function that makes use of fast block read capability of the X16
; use this in place of regular diskio.f_read()
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
if diskio.have_first_byte {
diskio.have_first_byte=false
@(bufferpointer) = diskio.first_byte
bufferpointer++
diskio.list_blocks++
num_bytes--
}
void c64.CHKIN(11) ; use #11 as input channel again
; 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)
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
}}
repeat num_bytes {
%asm {{
jsr c64.CHRIN
sta cx16.r5
m_in_buffer sta $ffff
inc m_in_buffer+1
bne +
inc m_in_buffer+2
+ inc diskio.list_blocks
bne +
inc diskio.list_blocks+1
+
}}
if cx16.r5==$0d { ; chance on I/o error status?
diskio.first_byte = c64.READST()
if diskio.first_byte & $40 {
diskio.f_close() ; end of file, close it
diskio.list_blocks-- ; don't count that last CHRIN read
}
if diskio.first_byte
return diskio.list_blocks ; number of bytes read
}
}
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()
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
if diskio.have_first_byte {
diskio.have_first_byte=false
@(bufferpointer) = diskio.first_byte
bufferpointer++
total_read = 1
}
while not c64.READST() {
uword size = cx16diskio.f_read(bufferpointer, 256)
total_read += size
bufferpointer += size
}
return total_read
}
}
-4
View File
@@ -1,8 +1,4 @@
; Prog8 definitions for floating point handling on the CommanderX16
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
%option enable_floats
%import floats_functions
+9 -5
View File
@@ -28,7 +28,7 @@ gfx2 {
uword width = 0
uword height = 0
ubyte bpp = 0
ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
bool monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
sub screen_mode(ubyte mode) {
when mode {
@@ -127,7 +127,7 @@ gfx2 {
position(0, 0)
}
sub monochrome_stipple(ubyte enable) {
sub monochrome_stipple(bool enable) {
monochrome_dont_stipple_flag = not enable
}
@@ -394,8 +394,12 @@ _done
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
if y1>y2 {
; make sure dy is always positive to have only 4 instead of 8 special cases
swap(x1, x2)
swap(y1, y2)
cx16.r0 = x1
x1 = x2
x2 = cx16.r0
cx16.r0 = y1
y1 = y2
y2 = cx16.r0
}
word @zp dx = (x2 as word)-x1
word @zp dy = (y2 as word)-y1
@@ -679,7 +683,7 @@ _done
}
}
sub position2(uword @zp x, uword y, ubyte also_port_1) {
sub position2(uword @zp x, uword y, bool also_port_1) {
position(x, y)
if also_port_1 {
when active_mode {
+7 -7
View File
@@ -13,20 +13,20 @@ palette {
cx16.vpoke(1, vera_palette_ptr, msb(color))
}
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
sub set_rgb_be(uword palette_ptr, uword num_colors) {
; 1 word per color entry, $0rgb in big endian format
vera_palette_ptr = $fa00
repeat num_colors {
cx16.vpoke(1, vera_palette_ptr+1, @(palette_bytes_ptr))
palette_bytes_ptr++
cx16.vpoke(1, vera_palette_ptr, @(palette_bytes_ptr))
palette_bytes_ptr++
cx16.vpoke(1, vera_palette_ptr+1, @(palette_ptr))
palette_ptr++
cx16.vpoke(1, vera_palette_ptr, @(palette_ptr))
palette_ptr++
vera_palette_ptr+=2
}
}
sub set_rgb(uword palette_words_ptr, uword num_colors) {
; 1 word per color entry (in little endian format so $gb0r)
; 1 word per color entry (in little endian format as layed out in video memory, so $gb0r)
vera_palette_ptr = $fa00
repeat num_colors*2 {
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))
+149
View File
@@ -0,0 +1,149 @@
%import syslib
psg {
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
; 00 frequency word LSB
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
; 02 bit 7 =right, bit 6 = left, bits 5-0 = volume 0-63 levels
; 03 bit 7,6 = waveform, bits 5-0 = Pulse width 0-63
; waveform: 0=pulse, 1=sawtooth, 2=triangle, 3=noise
const ubyte PULSE = %00000000
const ubyte SAWTOOTH = %01000000
const ubyte TRIANGLE = %10000000
const ubyte NOISE = %11000000
const ubyte LEFT = %01000000
const ubyte RIGHT = %10000000
sub voice(ubyte voice_num, ubyte channel, ubyte volume, ubyte waveform, ubyte pulsewidth) {
envelope_states[voice_num] = 255
cx16.r0 = $f9c2 + 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 = channel | volume
cx16.VERA_ADDR_L++
cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume
}
; sub freq_hz(ubyte voice_num, float hertz) {
; ; this would rely on floating point math to convert hertz to vera frequency
; ; TODO should be replaced by integer math maybe with a lookup table?
; uword vera_freq = (hertz / 0.3725290298461914) as uword
; freq(voice_num, vera_freq)
; }
sub freq(ubyte voice_num, uword vera_freq) {
cx16.r0 = $f9c0 + 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)
}
sub volume(ubyte voice_num, ubyte vol) {
cx16.r0 = $f9c2 + voice_num * 4
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | vol)
envelope_volumes[voice_num] = mkword(vol, 0)
envelope_maxvolumes[voice_num] = vol
}
sub pulse_width(ubyte voice_num, ubyte pw) {
cx16.r0 = $f9c3 + voice_num * 4
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | pw)
}
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
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)
envelope_maxvolumes[voice_num] = maxvolume
envelope_states[voice_num] = 0
}
sub silent() {
for cx16.r1L in 0 to 15 {
envelope_states[cx16.r1L] = 255
envelope_volumes[cx16.r1L] = 0
volume(cx16.r1L, 0)
}
}
sub envelopes_irq() {
; If you want to use real-time volume envelopes (Attack-Sustain-Release),
; 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)
; cx16.r0 = the volume word (volume scaled by 256)
; cx16.r1L = the voice number
; cx16.r2L = attack value
; calculate new volumes
for cx16.r1L in 0 to 15 {
when envelope_states[cx16.r1L] {
0 -> {
; attack
cx16.r2L = envelope_maxvolumes[cx16.r1L]
cx16.r0 = envelope_volumes[cx16.r1L] + envelope_attacks[cx16.r1L] * $0040
if msb(cx16.r0) > cx16.r2L or envelope_attacks[cx16.r1L]==0 {
cx16.r0 = mkword(cx16.r2L, 0)
envelope_attacks[cx16.r1L] = 0
envelope_states[cx16.r1L] = 1 ; start sustain
}
envelope_volumes[cx16.r1L] = cx16.r0
}
1 -> {
; sustain
if envelope_sustains[cx16.r1L] {
envelope_sustains[cx16.r1L]--
} else {
envelope_states[cx16.r1L] = 2 ; start release
}
}
2 -> {
; release
cx16.r0 = envelope_volumes[cx16.r1L] - envelope_releases[cx16.r1L] * $0040
if msb(cx16.r0) & %11000000 {
cx16.r0 = 0
envelope_releases[cx16.r1L] = 0
}
envelope_volumes[cx16.r1L] = cx16.r0
}
}
}
; set new volumes of all 16 voices, using vera stride of 4
cx16.push_vera_context()
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = $c2
cx16.VERA_ADDR_M = $f9
cx16.VERA_ADDR_H = 1 | %00110000
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_L = $c2
cx16.VERA_ADDR_M = $f9
cx16.VERA_ADDR_H = 1 | %00110000
for cx16.r1L in 0 to 15 {
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
}
cx16.pop_vera_context()
}
ubyte[16] envelope_states
uword[16] envelope_volumes ; scaled by 256
ubyte[16] envelope_attacks
ubyte[16] envelope_sustains
ubyte[16] envelope_releases
ubyte[16] envelope_maxvolumes
}
+56 -10
View File
@@ -1,9 +1,5 @@
; Prog8 definitions for the CommanderX16
; Including memory registers, I/O registers, Basic and Kernal subroutines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
c64 {
@@ -367,7 +363,7 @@ romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() clobbers(A,X,Y)
romsub $ff44 = macptr() clobbers(A,X,Y)
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY) clobbers(A) -> ubyte @Pc, uword @XY
romsub $ff47 = enter_basic(ubyte 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()
@@ -415,28 +411,28 @@ asmsub mouse_pos() -> ubyte @A {
inline asmsub rombank(ubyte bank @A) {
; -- set the rom banks
%asm {{
sta $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
sta $01
}}
}
inline asmsub rambank(ubyte bank @A) {
; -- set the ram bank
%asm {{
sta $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
sta $00
}}
}
inline asmsub getrombank() -> ubyte @A {
; -- get the current rom bank
%asm {{
lda $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
lda $01
}}
}
inline asmsub getrambank() -> ubyte @A {
; -- get the current ram bank
%asm {{
lda $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
lda $00
}}
}
@@ -643,8 +639,12 @@ asmsub init_system_phase2() {
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
; just an rts here, nothing special to clean up on cx16
%asm {{
lda #1
sta $00 ; ram bank 1
lda #4
sta $01 ; rom bank 4 (kernal)
stz $2d ; hack to reset machine code monitor bank to 0
rts
}}
}
@@ -735,6 +735,52 @@ IRQ_SCRATCH_ZPWORD2 .word 0
}}
}
inline 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 {{
lda cx16.VERA_ADDR_L
pha
lda cx16.VERA_ADDR_M
pha
lda cx16.VERA_ADDR_H
pha
lda cx16.VERA_CTRL
pha
eor #1
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
pha
lda cx16.VERA_ADDR_M
pha
lda cx16.VERA_ADDR_H
pha
lda cx16.VERA_CTRL
pha
}}
}
inline 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 {{
pla
sta cx16.VERA_CTRL
pla
sta cx16.VERA_ADDR_H
pla
sta cx16.VERA_ADDR_M
pla
sta cx16.VERA_ADDR_L
pla
sta cx16.VERA_CTRL
pla
sta cx16.VERA_ADDR_H
pla
sta cx16.VERA_ADDR_M
pla
sta cx16.VERA_ADDR_L
}}
}
asmsub restore_irq() clobbers(A) {
%asm {{
-4
View File
@@ -1,8 +1,4 @@
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; indent format: TABS, size=8
%import syslib
%import conv
+25 -21
View File
@@ -1,6 +1,4 @@
; C64 and Cx16 disk drive I/O routines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%import textio
%import string
@@ -8,7 +6,7 @@
diskio {
sub directory(ubyte drivenumber) -> ubyte {
sub directory(ubyte drivenumber) -> bool {
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
c64.SETNAM(1, "$")
@@ -63,12 +61,12 @@ io_error:
; internal variables for the iterative file lister / loader
ubyte list_skip_disk_name
bool list_skip_disk_name
uword list_pattern
uword list_blocks
ubyte iteration_in_progress = false
bool iteration_in_progress = false
ubyte @zp first_byte
ubyte have_first_byte
bool have_first_byte
str list_filename = "?" * 32
@@ -100,7 +98,7 @@ io_error:
; ----- iterative file lister functions (uses io channel 12) -----
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> ubyte {
sub lf_start_list(ubyte drivenumber, 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()
@@ -129,7 +127,7 @@ io_error:
return false
}
sub lf_next_entry() -> ubyte {
sub lf_next_entry() -> bool {
; -- retrieve the next entry from an iterative file listing session.
; results will be found in list_blocks and list_filename.
; if it returns false though, there are no more entries (or an error occurred).
@@ -201,7 +199,7 @@ close_end:
; ----- iterative file loader functions (uses io channel 11) -----
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte {
sub f_open(ubyte drivenumber, 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()
@@ -228,6 +226,8 @@ 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!
if not iteration_in_progress or not num_bytes
return 0
@@ -241,6 +241,7 @@ close_end:
}
void c64.CHKIN(11) ; use #11 as input channel again
%asm {{
lda bufferpointer
sta m_in_buffer+1
@@ -263,13 +264,15 @@ m_in_buffer sta $ffff
if cx16.r5==$0d { ; chance on I/o error status?
first_byte = c64.READST()
if first_byte & $40
if first_byte & $40 {
f_close() ; end of file, close it
list_blocks-- ; don't count that last CHRIN read
}
if first_byte
return list_blocks
return list_blocks ; number of bytes read
}
}
return list_blocks
return list_blocks ; number of bytes read
}
sub f_read_all(uword bufferpointer) -> uword {
@@ -277,19 +280,20 @@ m_in_buffer sta $ffff
if not iteration_in_progress
return 0
list_blocks = 0 ; we reuse this variable for the total number of bytes read
uword total_read = 0
if have_first_byte {
have_first_byte=false
@(bufferpointer) = first_byte
bufferpointer++
list_blocks++
total_read = 1
}
while not c64.READST() {
list_blocks += f_read(bufferpointer, 256)
bufferpointer += 256
uword size = f_read(bufferpointer, 256)
total_read += size
bufferpointer += size
}
return list_blocks
return total_read
}
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
@@ -339,7 +343,7 @@ _end rts
; ----- iterative file saver functions (uses io channel 14) -----
sub f_open_w(ubyte drivenumber, uword filenameptr) -> ubyte {
sub f_open_w(ubyte drivenumber, uword filenameptr) -> bool {
; -- open a file for iterative writing with f_write
f_close_w()
@@ -354,7 +358,7 @@ _end rts
return false
}
sub f_write(uword bufferpointer, uword num_bytes) -> ubyte {
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
; -- write the given umber of bytes to the currently open file
if num_bytes!=0 {
void c64.CHKOUT(14) ; use #14 as input channel again
@@ -404,7 +408,7 @@ io_error:
goto done
}
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> bool {
c64.SETNAM(string.length(filenameptr), filenameptr)
c64.SETLFS(1, drivenumber, 0)
uword @shared end_address = address + size
@@ -474,7 +478,7 @@ io_error:
; 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.
sub load_headerless_cx16(ubyte drivenumber, uword filenameptr, uword address_override, ubyte headerless) -> uword {
sub load_headerless_cx16(ubyte drivenumber, uword filenameptr, uword address_override, bool headerless) -> uword {
c64.SETNAM(string.length(filenameptr), filenameptr)
ubyte secondary = 1
cx16.r1 = 0
-2
View File
@@ -1,8 +1,6 @@
; Internal Math library routines - always included by the compiler
; Generic machine independent 6502 code.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
; some more interesting routines can be found here:
; http://6502org.wikidot.com/software-math
; http://codebase64.org/doku.php?id=base:6502_6510_maths
-2
View File
@@ -1,6 +1,4 @@
; Internal Math library routines - always included by the compiler
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
math {
%asminclude "library:math.asm"
+2 -2
View File
@@ -86,9 +86,9 @@ func_all_w_stack .proc
abs_b_stack .proc
; -- push abs(A) on stack (as unsigned word)
jsr abs_b_into_A
jsr abs_b_into_AY
sta P8ESTACK_LO,x
stz p8ESTACK_HI,x
stz P8ESTACK_HI,x
dex
rts
.pend
-115
View File
@@ -1,7 +1,5 @@
; Internal library routines - always included by the compiler
; Generic machine independent 6502 code.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
@@ -60,27 +58,6 @@ inv_word .proc
rts
.pend
not_byte .proc
lda P8ESTACK_LO+1,x
beq +
lda #1
+ eor #1
sta P8ESTACK_LO+1,x
rts
.pend
not_word .proc
lda P8ESTACK_LO + 1,x
ora P8ESTACK_HI + 1,x
beq +
lda #1
+ eor #1
sta P8ESTACK_LO + 1,x
lsr a
sta P8ESTACK_HI + 1,x
rts
.pend
bitand_b .proc
; -- bitwise and (of 2 bytes)
lda P8ESTACK_LO+2,x
@@ -144,98 +121,6 @@ bitxor_w .proc
rts
.pend
and_b .proc
; -- logical and (of 2 bytes)
lda P8ESTACK_LO+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
beq +
lda #1
+ and P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
rts
.pend
or_b .proc
; -- logical or (of 2 bytes)
lda P8ESTACK_LO+2,x
ora P8ESTACK_LO+1,x
beq +
lda #1
+ inx
sta P8ESTACK_LO+1,x
rts
.pend
xor_b .proc
; -- logical xor (of 2 bytes)
lda P8ESTACK_LO+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
beq +
lda #1
+ eor P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
rts
.pend
and_w .proc
; -- logical and (word and word -> byte)
lda P8ESTACK_LO+2,x
ora P8ESTACK_HI+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
ora P8ESTACK_HI+1,x
beq +
lda #1
+ and P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
rts
.pend
or_w .proc
; -- logical or (word or word -> byte)
lda P8ESTACK_LO+2,x
ora P8ESTACK_LO+1,x
ora P8ESTACK_HI+2,x
ora P8ESTACK_HI+1,x
beq +
lda #1
+ inx
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
rts
.pend
xor_w .proc
; -- logical xor (word xor word -> byte)
lda P8ESTACK_LO+2,x
ora P8ESTACK_HI+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
ora P8ESTACK_HI+1,x
beq +
lda #1
+ eor P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
rts
.pend
add_w .proc
; -- push word+word / uword+uword
-2
View File
@@ -1,6 +1,4 @@
; Internal library routines - always included by the compiler
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
prog8_lib {
%asminclude "library:prog8_lib.asm"
-3
View File
@@ -1,7 +1,4 @@
; 0-terminated string manipulation routines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
string {
-3
View File
@@ -1,7 +1,4 @@
; Number conversions routines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
conv {
-2
View File
@@ -1,6 +1,4 @@
; Prog8 definitions for floating point handling on the VirtualMachine
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%option enable_floats
-2
View File
@@ -1,6 +1,4 @@
; Internal Math library routines - always included by the compiler
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
math {
@@ -1,6 +1,4 @@
; Internal library routines - always included by the compiler
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%import textio
-3
View File
@@ -1,7 +1,4 @@
; 0-terminated string manipulation routines. For the Virtual Machine target.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
string {
sub length(str st) -> ubyte {
+9 -4
View File
@@ -1,7 +1,4 @@
; Prog8 definitions for the Virtual Machine
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
sys {
; ------- lowlevel system routines --------
@@ -98,13 +95,21 @@ sys {
syscall 10
}}
}
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%asm {{
loadm.w r0, {sys.gfx_getpixel.xx}
loadm.w r1, {sys.gfx_getpixel.yy}
syscall 30
return
}}
}
}
cx16 {
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the VirtualMachine as well but their location in memory is different
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
&uword r0 = $0002
&uword r1 = $0004
&uword r2 = $0006
-2
View File
@@ -1,6 +1,4 @@
; Prog8 definitions for the Text I/O console routines for the Virtual Machine
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
%import conv
+1 -1
View File
@@ -1 +1 @@
8.1
8.3
+44 -3
View File
@@ -3,6 +3,7 @@ package prog8
import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.code.core.CbmPrgLauncherType
import prog8.code.core.toHex
import prog8.code.target.*
import prog8.code.target.virtual.VirtualMachineDefinition
import prog8.compiler.CompilationResult
@@ -17,7 +18,6 @@ import java.time.LocalDateTime
import kotlin.system.exitProcess
fun main(args: Array<String>) {
val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim()
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
@@ -46,10 +46,11 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
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}')").default(C64Target.NAME)
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 startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a p8-virt listing in the VM instead")
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
val evalStackAddrString by cli.option(ArgType.String, fullName = "esa", description = "override the eval-stack base address (must be page aligned)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@@ -84,6 +85,27 @@ private fun compileMain(args: Array<String>): Boolean {
return runVm(moduleFiles.first())
}
var evalStackAddr: UInt? = null
if(evalStackAddrString!=null) {
try {
evalStackAddr = if (evalStackAddrString!!.startsWith("0x"))
evalStackAddrString!!.substring(2).toUInt(16)
else if (evalStackAddrString!!.startsWith("$"))
evalStackAddrString!!.substring(1).toUInt(16)
else
evalStackAddrString!!.toUInt()
} catch(nx: NumberFormatException) {
System.err.println("invalid address for evalstack: $evalStackAddrString")
return false
}
if(evalStackAddr !in 256u..65536u-512u || (evalStackAddr and 255u != 0u)) {
System.err.println("invalid address for evalstack: ${evalStackAddr.toHex()}")
return false
}
}
val processedSymbols = processSymbolDefs(symbolDefs) ?: return false
if(watchMode==true) {
val watchservice = FileSystems.getDefault().newWatchService()
val allImportedFiles = mutableSetOf<Path>()
@@ -104,6 +126,8 @@ private fun compileMain(args: Array<String>): Boolean {
asmListfile == true,
experimentalCodegen == true,
compilationTarget,
evalStackAddr,
processedSymbols,
srcdirs,
outputPath
)
@@ -156,6 +180,8 @@ private fun compileMain(args: Array<String>): Boolean {
asmListfile == true,
experimentalCodegen == true,
compilationTarget,
evalStackAddr,
processedSymbols,
srcdirs,
outputPath
)
@@ -193,6 +219,21 @@ private fun compileMain(args: Array<String>): Boolean {
return true
}
private fun processSymbolDefs(symbolDefs: List<String>): Map<String, String>? {
val result = mutableMapOf<String, String>()
val defPattern = """(.+)\s*=\s*(.+)""".toRegex()
for(def in symbolDefs) {
val match = defPattern.matchEntire(def.trim())
if(match==null) {
System.err.println("invalid symbol definition (expected NAME=VALUE): $def")
return null
}
val (_, name, value) = match.groupValues
result[name.trim()] = value.trim()
}
return result
}
fun runVm(listingFilename: String): Boolean {
val name =
if(listingFilename.endsWith(".p8virt"))
+12 -1
View File
@@ -38,6 +38,8 @@ class CompilerArguments(val filepath: Path,
val asmListfile: Boolean,
val experimentalCodegen: Boolean,
val compilationTarget: String,
val evalStackBaseAddress: UInt?,
val symbolDefs: Map<String, String>,
val sourceDirs: List<String> = emptyList(),
val outputDir: Path = Path(""),
val errors: IErrorReporter = ErrorReporter())
@@ -77,10 +79,17 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
asmQuiet = args.quietAssembler
asmListfile = args.asmListfile
experimentalCodegen = args.experimentalCodegen
evalStackBaseAddress = args.evalStackBaseAddress
outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs
}
program = programresult
importedFiles = imported
if(compilationOptions.evalStackBaseAddress!=null) {
compTarget.machine.overrideEvalStack(compilationOptions.evalStackBaseAddress!!)
}
processAst(program, args.errors, compilationOptions)
if (compilationOptions.optimize) {
// println("*********** AST RIGHT BEFORE OPTIMIZING *************")
@@ -330,6 +339,8 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
errors.report()
program.reorderStatements(errors, compilerOptions)
errors.report()
program.changeNotExpression(errors)
errors.report()
program.addTypecasts(errors, compilerOptions)
errors.report()
program.variousCleanups(errors, compilerOptions)
@@ -347,7 +358,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
remover.applyModifications()
while (true) {
// keep optimizing expressions and statements until no more steps remain
val optsDone1 = program.simplifyExpressions(errors)
val optsDone1 = program.simplifyExpressions()
val optsDone2 = program.splitBinaryExpressions(compilerOptions)
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
val optsDone4 = program.inlineSubroutines()
+1 -1
View File
@@ -33,7 +33,7 @@ internal class ErrorReporter: IErrorReporter {
if(msg !in alreadyReportedMessages) {
when(it.severity) {
MessageSeverity.ERROR -> printer.print("\u001b[91mERROR\u001B[0m ") // bright red
MessageSeverity.WARNING -> printer.print("\u001b[93mWARN\u001B[0m ") // bright yellow
MessageSeverity.WARNING -> printer.print("\u001b[93mWARN\u001B[0m ") // bright yellow
}
printer.println(msg)
alreadyReportedMessages.add(msg)
@@ -10,9 +10,7 @@ import prog8.ast.determineGosubArguments
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.ast.*
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.SourceCode
import prog8.code.core.*
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
@@ -50,7 +48,6 @@ class IntermediateAstMaker(val program: Program) {
is InlineAssembly -> transform(statement)
is Jump -> transform(statement)
is Label -> transform(statement)
is Pipe -> transform(statement)
is PostIncrDecr -> transform(statement)
is RepeatLoop -> transform(statement)
is Return -> transform(statement)
@@ -80,7 +77,6 @@ class IntermediateAstMaker(val program: Program) {
is FunctionCallExpression -> transform(expr)
is IdentifierReference -> transform(expr)
is NumericLiteral -> transform(expr)
is PipeExpression -> transform(expr)
is PrefixExpression -> transform(expr)
is RangeExpression -> transform(expr)
is StringLiteral -> transform(expr)
@@ -225,11 +221,7 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
val (target, _) = targetOf(srcCall.target)
val type = srcCall.inferType(program).getOrElse {
if((srcCall.parent as? Pipe)?.segments?.last() === srcCall)
// for a pipe, the last segment is allowed to be a call to a function not returning anything.
DataType.UNDEFINED
else
throw FatalAstException("unknown dt $srcCall")
throw FatalAstException("unknown dt $srcCall")
}
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
for (arg in srcCall.args)
@@ -287,14 +279,6 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(label: Label): PtLabel =
PtLabel(label.name, label.position)
private fun transform(srcPipe: Pipe): PtPipe {
val pipe = PtPipe(DataType.UNDEFINED, true, srcPipe.position)
pipe.add(transformExpression(srcPipe.source))
for (segment in srcPipe.segments)
pipe.add(transformExpression(segment))
return pipe
}
private fun transform(src: PostIncrDecr): PtPostIncrDecr {
val post = PtPostIncrDecr(src.operator, src.position)
post.add(transform(src.target))
@@ -419,7 +403,8 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
val actualType = if(type==DataType.BOOL) DataType.UBYTE else type
val expr = PtBinaryExpression(srcExpr.operator, actualType, srcExpr.position)
expr.add(transformExpression(srcExpr.left))
expr.add(transformExpression(srcExpr.right))
return expr
@@ -459,15 +444,6 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(number: NumericLiteral): PtNumber =
PtNumber(number.type, number.number, number.position)
private fun transform(srcPipe: PipeExpression): PtPipe {
val type = srcPipe.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val pipe = PtPipe(type, false, srcPipe.position)
pipe.add(transformExpression(srcPipe.source))
for (segment in srcPipe.segments)
pipe.add(transformExpression(segment))
return pipe
}
private fun transform(srcPrefix: PrefixExpression): PtPrefix {
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val prefix = PtPrefix(srcPrefix.operator, type, srcPrefix.position)
@@ -9,6 +9,7 @@ import prog8.ast.walk.IAstVisitor
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.compiler.BuiltinFunctions
import prog8.compiler.InplaceModifyingBuiltinFunctions
import prog8.compiler.builtinFunctionReturnType
import java.io.CharConversionException
import java.io.File
@@ -77,16 +78,30 @@ internal class AstChecker(private val program: Program,
if(!valueDt.isKnown) {
errors.err("return value type mismatch or unknown symbol", returnStmt.value!!.position)
} else {
if (expectedReturnValues[0] != valueDt.getOr(DataType.UNDEFINED))
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}", returnStmt.value!!.position)
if (expectedReturnValues[0] != valueDt.getOr(DataType.UNDEFINED)) {
if(valueDt istype DataType.BOOL && expectedReturnValues[0] == DataType.UBYTE) {
// if the return value is a bool and the return type is ubyte, allow this. But give a warning.
errors.warn("return type of the subroutine should probably be bool instead of ubyte", returnStmt.position)
} else if(valueDt istype DataType.UBYTE && expectedReturnValues[0] == DataType.BOOL) {
// if the return value is ubyte and the return type is bool, allow this only if value is 0 or 1
val returnValue = returnStmt.value as? NumericLiteral
if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
}
}
else {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
}
}
}
}
super.visit(returnStmt)
}
override fun visit(ifElse: IfElse) {
if(!ifElse.condition.inferType(program).isInteger)
errors.err("condition value should be an integer type", ifElse.condition.position)
val dt = ifElse.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL))
errors.err("condition value should be an integer type or bool", ifElse.condition.position)
super.visit(ifElse)
}
@@ -118,6 +133,10 @@ internal class AstChecker(private val program: Program,
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
}
DataType.BOOL -> {
if(iterableDt != DataType.ARRAY_BOOL)
errors.err("bool loop variable can only loop over boolean array", forLoop.position)
}
DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
@@ -298,11 +317,6 @@ internal class AstChecker(private val program: Program,
}
}
// Most code generation targets only support subroutine inlining on asmsub subroutines
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
if(!subroutine.isAsmSubroutine && compilerOptions.compTarget.name!=VMTarget.NAME)
subroutine.inline = false
if(subroutine.parent !is Block && subroutine.parent !is Subroutine)
err("subroutines can only be defined in the scope of a block or within another subroutine")
@@ -422,14 +436,16 @@ internal class AstChecker(private val program: Program,
}
override fun visit(untilLoop: UntilLoop) {
if(!untilLoop.condition.inferType(program).isInteger)
errors.err("condition value should be an integer type", untilLoop.condition.position)
val dt = untilLoop.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL))
errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
super.visit(untilLoop)
}
override fun visit(whileLoop: WhileLoop) {
if(!whileLoop.condition.inferType(program).isInteger)
errors.err("condition value should be an integer type", whileLoop.condition.position)
val dt = whileLoop.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL))
errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
super.visit(whileLoop)
}
@@ -466,6 +482,13 @@ internal class AstChecker(private val program: Program,
if(numvalue!=null && targetDt.isKnown)
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
// for now, don't enforce bool type with only logical operators...
// if(assignment.isAugmentable && targetDt istype DataType.BOOL) {
// val operator = (assignment.value as? BinaryExpression)?.operator
// if(operator in InvalidOperatorsForBoolean)
// errors.err("can't use boolean operand with this operator $operator", assignment.position)
// }
super.visit(assignment)
}
@@ -507,7 +530,7 @@ internal class AstChecker(private val program: Program,
if(constVal==null) {
val sourceDatatype = assignment.value.inferType(program)
if (sourceDatatype.isUnknown) {
if (assignment.value !is FunctionCallExpression && assignment.value !is PipeExpression)
if (assignment.value !is FunctionCallExpression)
errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position)
} else {
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
@@ -827,10 +850,6 @@ internal class AstChecker(private val program: Program,
errors.err("can only take negative of a signed number type", expr.position)
}
}
else if(expr.operator == "not") {
if(dt !in IntegerDatatypes)
errors.err("can only use boolean not on integer types", expr.position)
}
else if(expr.operator == "~") {
if(dt !in IntegerDatatypes)
errors.err("can only use bitwise invert on integer types", expr.position)
@@ -867,15 +886,6 @@ internal class AstChecker(private val program: Program,
errors.err("remainder can only be used on unsigned integer operands", expr.right.position)
}
}
"and", "or", "xor" -> {
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
errors.err("logical operator can only be used on boolean operands", expr.right.position)
val constLeft = expr.left.constValue(program)
val constRight = expr.right.constValue(program)
if(constLeft!=null && constLeft.number.toInt() !in 0..1 || constRight!=null && constRight.number.toInt() !in 0..1)
errors.err("const literal argument of logical operator must be boolean (0 or 1)", expr.position)
}
"&", "|", "^" -> {
// only integer numeric operands accepted
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
@@ -884,15 +894,17 @@ internal class AstChecker(private val program: Program,
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
}
if(leftDt !in NumericDatatypes && leftDt != DataType.STR)
if(leftDt !in NumericDatatypes && leftDt != DataType.STR && leftDt != DataType.BOOL)
errors.err("left operand is not numeric or str", expr.left.position)
if(rightDt!in NumericDatatypes && rightDt != DataType.STR)
if(rightDt!in NumericDatatypes && rightDt != DataType.STR && rightDt != DataType.BOOL)
errors.err("right operand is not numeric or str", expr.right.position)
if(leftDt!=rightDt) {
if(leftDt==DataType.STR && rightDt in IntegerDatatypes && expr.operator=="*") {
// only exception allowed: str * constvalue
if(expr.right.constValue(program)==null)
errors.err("can only use string repeat with a constant number value", expr.left.position)
} else if(leftDt==DataType.BOOL && rightDt in ByteDatatypes || leftDt in ByteDatatypes && rightDt==DataType.BOOL) {
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} else {
errors.err("left and right operands aren't the same type", expr.left.position)
}
@@ -901,10 +913,31 @@ internal class AstChecker(private val program: Program,
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 expressions with strings or arrays are possible", expr.position)
errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
}
}
if(leftDt==DataType.BOOL || rightDt==DataType.BOOL ||
(expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true ||
(expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true) {
if(expr.operator in setOf("<", "<=", ">", ">=")) {
errors.err("can't use boolean operand with this comparison operator", expr.position)
}
// for now, don't enforce bool type with only logical operators...
// if(expr.operator in InvalidOperatorsForBoolean && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
// errors.err("can't use boolean operand with this operator ${expr.operator}", expr.left.position)
// }
if(expr.operator == "==" || expr.operator == "!=") {
val leftNum = expr.left.constValue(program)?.number ?: 0.0
val rightNum = expr.right.constValue(program)?.number ?: 0.0
if(leftNum>1.0 || rightNum>1.0 || leftNum<0.0 || rightNum<0.0) {
errors.warn("expression is always false", expr.position)
}
}
if((expr.operator == "/" || expr.operator == "%") && ( rightDt==DataType.BOOL || (expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
errors.err("can't use boolean operand with this operator ${expr.operator}", expr.right.position)
}
}
}
override fun visit(typecast: TypecastExpression) {
@@ -1013,7 +1046,7 @@ internal class AstChecker(private val program: Program,
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
if(targetStatement!=null) {
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
checkUnusedReturnValues(functionCallStatement, targetStatement, program, errors)
checkUnusedReturnValues(functionCallStatement, targetStatement, errors)
}
val funcName = functionCallStatement.target.nameInSource
@@ -1041,7 +1074,7 @@ internal class AstChecker(private val program: Program,
}
}
if(funcName[0] in arrayOf("rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
// in-place modification, can't be done on literals
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
@@ -1062,20 +1095,7 @@ internal class AstChecker(private val program: Program,
errors.err("cannot use arguments when calling a label", position)
if(target is BuiltinFunctionPlaceholder) {
if(target.name=="swap") {
// swap() is a bit weird because this one is translated into an operations directly, instead of being a function call
val dt1 = args[0].inferType(program)
val dt2 = args[1].inferType(program)
if (dt1 != dt2)
errors.err("swap requires 2 args of identical type", position)
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
errors.err("swap requires 2 variables, not constant value(s)", position)
else if(args[0] isSameAs args[1])
errors.err("swap should have 2 different args", position)
else if(!dt1.isNumeric)
errors.err("swap requires args of numerical type", position)
}
else if(target.name=="all" || target.name=="any") {
if(target.name=="all" || target.name=="any") {
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
@@ -1102,8 +1122,14 @@ internal class AstChecker(private val program: Program,
}
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
var regname = ident.nameInSource[1].uppercase()
if(regname.endsWith('L') || regname.endsWith('H') || regname.endsWith('s'))
regname=regname.substring(0, regname.length-1)
val lastLetter = regname.last().lowercaseChar()
if(lastLetter in setOf('l', 'h', 's')) {
regname = regname.substring(0, regname.length - 1)
val lastLetter2 = regname.last().lowercaseChar()
if(lastLetter2 in setOf('l', 'h', 's')) {
regname = regname.substring(0, regname.length - 1)
}
}
val reg = RegisterOrPair.valueOf(regname)
val same = params.filter { it.value.registerOrPair==reg }
for(s in same) {
@@ -1117,33 +1143,6 @@ internal class AstChecker(private val program: Program,
}
}
override fun visit(pipe: PipeExpression) {
process(pipe)
super.visit(pipe)
}
override fun visit(pipe: Pipe) {
process(pipe)
super.visit(pipe)
}
private fun process(pipe: IPipe) {
if(pipe.source in pipe.segments)
throw InternalCompilerException("pipe source and segments should all be different nodes")
if (pipe.segments.isEmpty())
throw FatalAstException("pipe is missing one or more expressions")
if(pipe.segments.any { it !is IFunctionCall })
throw FatalAstException("pipe segments can only be function calls")
if(compilerOptions.compTarget !is VMTarget) {
pipe.segments.forEach {
it as IFunctionCall
if (it.args.size > 0)
errors.err("only unary functions supported in pipe expressions for now", it.position)
}
}
}
override fun visit(postIncrDecr: PostIncrDecr) {
if(postIncrDecr.target.identifier != null) {
val targetName = postIncrDecr.target.identifier!!.nameInSource
@@ -1170,14 +1169,19 @@ internal class AstChecker(private val program: Program,
}
}
// else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/--
if(postIncrDecr.target.inferType(program) istype DataType.BOOL) {
errors.err("can't use boolean operand with this operator ${postIncrDecr.operator}", postIncrDecr.position)
}
super.visit(postIncrDecr)
}
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
val target = arrayIndexedExpression.arrayvar.targetStatement(program)
if(target is VarDecl) {
if(target.datatype !in IterableDatatypes)
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
if(target.datatype !in IterableDatatypes && target.datatype!=DataType.UWORD)
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
val arraysize = target.arraysize?.constIndex()
if(arraysize!=null) {
// check out of bounds
@@ -1427,6 +1431,9 @@ internal class AstChecker(private val program: Program,
if (number < -32768 || number > 32767)
return err("value '$number' out of range for word")
}
DataType.BOOL -> {
return true
}
else -> return err("value of type ${value.type} not compatible with $targetDt")
}
return true
@@ -1486,12 +1493,13 @@ internal class AstChecker(private val program: Program,
}
val result = when(targetDatatype) {
DataType.BYTE -> sourceDatatype== DataType.BYTE
DataType.UBYTE -> sourceDatatype== DataType.UBYTE
DataType.WORD -> sourceDatatype== DataType.BYTE || sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.WORD
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
DataType.BOOL -> sourceDatatype in NumericDatatypes
DataType.BYTE -> sourceDatatype == DataType.BYTE || sourceDatatype == DataType.BOOL
DataType.UBYTE -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.BOOL
DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.BOOL)
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD || sourceDatatype == DataType.BOOL
DataType.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype== DataType.STR
DataType.STR -> sourceDatatype == DataType.STR
else -> {
errors.err("cannot assign new value to variable of type $targetDatatype", position)
false
@@ -1515,7 +1523,7 @@ internal class AstChecker(private val program: Program,
}
}
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, program: Program, errors: IErrorReporter) {
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
if (!call.void) {
// check for unused return values
if (target is Subroutine && target.returntypes.isNotEmpty()) {
@@ -10,6 +10,7 @@ import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.compiler.printProgram
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
@@ -21,14 +22,17 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
}
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
val boolRemover = BoolRemover(this)
boolRemover.visit(this)
boolRemover.applyModifications()
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
fixer.visit(this)
while(errors.noErrors() && fixer.applyModifications()>0) {
while (errors.noErrors() && fixer.applyModifications() > 0) {
fixer.visit(this)
}
val cleaner = BeforeAsmTypecastCleaner(this, errors)
cleaner.visit(this)
while(errors.noErrors() && cleaner.applyModifications()>0) {
while (errors.noErrors() && cleaner.applyModifications() > 0) {
cleaner.visit(this)
}
}
@@ -44,6 +48,14 @@ internal fun Program.reorderStatements(errors: IErrorReporter, options: Compilat
}
}
internal fun Program.changeNotExpression(errors: IErrorReporter) {
val changer = NotExpressionChanger(this, errors)
changer.visit(this)
while(errors.noErrors() && changer.applyModifications()>0) {
changer.visit(this)
}
}
internal fun Program.charLiteralsToUByteLiterals(target: ICompilationTarget, errors: IErrorReporter) {
val walker = object : AstWalker() {
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
@@ -94,7 +106,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
checker2.visit(this)
if(errors.noErrors()) {
val transforms = AstVariousTransforms(this)
val transforms = AstOnetimeTransforms(this, options)
transforms.visit(this)
transforms.applyModifications()
val lit2decl = LiteralsToAutoVars(this, options.compTarget, errors)
@@ -1,7 +1,6 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.IPipe
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.FunctionCallExpression
@@ -54,12 +53,16 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
if(decl.name in compTarget.machine.opcodeNames)
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
val existing = decl.parent.definingScope.lookup(listOf(decl.name))
if (existing != null && existing !== decl && existing is VarDecl) {
if(existing.parent!==decl.parent)
nameShadowWarning(decl.name, decl.position, existing)
val existingInSameScope = decl.definingScope.lookup(listOf(decl.name))
if(existingInSameScope!=null && existingInSameScope!==decl)
nameError(decl.name, decl.position, existingInSameScope)
val existingOuter = decl.parent.definingScope.lookup(listOf(decl.name))
if (existingOuter != null && existingOuter !== decl && existingOuter is VarDecl) {
if(existingOuter.parent!==decl.parent)
nameShadowWarning(decl.name, decl.position, existingOuter)
else
nameError(decl.name, decl.position, existing)
nameError(decl.name, decl.position, existingOuter)
}
if(decl.definingBlock.name==decl.name)
@@ -84,9 +87,9 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
val existing = subroutine.lookup(listOf(subroutine.name))
if (existing != null && existing !== subroutine) {
if(existing.parent!==existing.parent)
nameShadowWarning(subroutine.name, subroutine.position, existing)
nameShadowWarning(subroutine.name, existing.position, subroutine)
else
nameError(subroutine.name, subroutine.position, existing)
nameError(subroutine.name, existing.position, subroutine)
}
// check that there are no local symbols (variables, labels, subs) that redefine the subroutine's parameters.
@@ -147,32 +150,20 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
private fun visitFunctionCall(call: IFunctionCall) {
val isPartOfPipeSegments = (call.parent as? IPipe)?.segments?.contains(call as Node) == true
val errormessageAboutArgs = if(isPartOfPipeSegments) "invalid number of arguments in piped call" else "invalid number of arguments"
when (val target = call.target.targetStatement(program)) {
is Subroutine -> {
// if the call is part of a Pipe, the number of arguments in the call should be 1 less than the number of parameters
val expectedNumberOfArgs: Int = if(isPartOfPipeSegments) {
target.parameters.size - 1
} else {
target.parameters.size
}
val expectedNumberOfArgs: Int = target.parameters.size
if(call.args.size != expectedNumberOfArgs) {
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
errors.err(errormessageAboutArgs, pos)
errors.err("invalid number of arguments", pos)
}
}
is BuiltinFunctionPlaceholder -> {
val func = BuiltinFunctions.getValue(target.name)
// if the call is part of a Pipe, the number of arguments in the call should be 1 less than the number of parameters
val expectedNumberOfArgs: Int = if(isPartOfPipeSegments) {
func.parameters.size-1
} else {
func.parameters.size
}
val expectedNumberOfArgs: Int = func.parameters.size
if(call.args.size != expectedNumberOfArgs) {
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
errors.err(errormessageAboutArgs, pos)
errors.err("invalid number of arguments", pos)
}
if(func.name=="memory") {
val name = call.args[0] as? StringLiteral
@@ -0,0 +1,62 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.target.VMTarget
internal class AstOnetimeTransforms(private val program: Program, private val options: CompilationOptions) : AstWalker() {
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(parent !is VarDecl) {
// TODO move this / remove this, and make the codegen better instead.
// If the expression is pointervar[idx] where pointervar is uword and not a real array,
// replace it by a @(pointervar+idx) expression.
// Don't replace the initializer value in a vardecl - this will be moved to a separate
// assignment statement soon in after(VarDecl)
return replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression, parent)
}
return noModifications
}
private fun replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(options.compTarget.name== VMTarget.NAME)
return noModifications // vm codegen deals correctly with all cases
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
if(parent is AssignTarget) {
val assignment = parent.parent as? Assignment
if(assignment?.value is NumericLiteral || assignment?.value is IdentifierReference) {
// ONLY for a constant assignment, or direct variable assignment, the codegen contains correct optimized code.
return noModifications
}
// Other cases aren't covered correctly by the 6502 codegen, and there are a LOT of cases.
// So rewrite assignment target pointervar[index] into @(pointervar+index)
// (the 6502 codegen covers all cases correctly for a direct memory assignment).
val indexer = arrayIndexedExpression.indexer
val add: Expression =
if(indexer.indexExpr.constValue(program)?.number==0.0)
arrayIndexedExpression.arrayvar.copy()
else
BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexer.indexExpr, arrayIndexedExpression.position)
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
}
}
return noModifications
}
}
@@ -1,6 +1,5 @@
package prog8.compiler.astprocessing
import prog8.ast.IPipe
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.SyntaxError
@@ -9,10 +8,44 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.target.Cx16Target
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
override fun before(program: Program): Iterable<IAstModification> {
if(compTarget.name!=Cx16Target.NAME) {
// reset the address of the virtual registers to be inside the evaluation stack.
// (we don't do this on CommanderX16 itself as the registers have a fixed location in Zeropage there)
val cx16block = program.allBlocks.single { it.name=="cx16" }
val memVars = cx16block.statements
.filterIsInstance<VarDecl>()
.associateBy { it.name }
val estack = compTarget.machine.ESTACK_HI
for(regnum in 0u..15u) {
val rX = memVars.getValue("r$regnum")
val rXL = memVars.getValue("r${regnum}L")
val rXH = memVars.getValue("r${regnum}H")
val rXs = memVars.getValue("r${regnum}s")
val rXsL = memVars.getValue("r${regnum}sL")
val rXsH = memVars.getValue("r${regnum}sH")
setAddress(rX, estack + 2u*regnum)
setAddress(rXL, estack + 2u*regnum)
setAddress(rXH, estack + 2u*regnum +1u)
setAddress(rXs, estack + 2u*regnum)
setAddress(rXsL, estack + 2u*regnum)
setAddress(rXsH, estack + 2u*regnum + 1u)
}
}
return noModifications
}
private fun setAddress(vardecl: VarDecl, address: UInt) {
val oldAddr = vardecl.value as NumericLiteral
vardecl.value = NumericLiteral(oldAddr.type, address.toDouble(), oldAddr.position)
}
override fun before(char: CharLiteral, parent: Node): Iterable<IAstModification> {
if(char.encoding== Encoding.DEFAULT)
char.encoding = compTarget.defaultEncoding
@@ -96,6 +129,7 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
}
return noModifications
}
@@ -111,29 +145,25 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
return noModifications
}
override fun before(pipe: Pipe, parent: Node): Iterable<IAstModification> {
if(pipe.source is PipeExpression) {
// correct Antlr parse tree quirk: turn nested pipe into single flat pipe
val psrc = pipe.source as PipeExpression
val newSource = psrc.source
val newSegments = psrc.segments
newSegments += pipe.segments.single()
return listOf(IAstModification.ReplaceNode(pipe as Node, Pipe(newSource, newSegments, pipe.position), parent))
} else if(pipe.source is IPipe)
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
return noModifications
}
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).
val symbolsInSub = subroutine.allDefinedSymbols
val namesInSub = symbolsInSub.map{ it.first }.toSet()
if(subroutine.asmAddress==null) {
if(!subroutine.isAsmSubroutine && subroutine.parameters.isNotEmpty()) {
val vars = subroutine.statements.asSequence().filterIsInstance<VarDecl>().map { it.name }.toSet()
if(!vars.containsAll(subroutine.parameters.map{it.name})) {
return subroutine.parameters
.filter { it.name !in namesInSub }
.map {
val vardecl = VarDecl.fromParameter(it)
IAstModification.InsertFirst(vardecl, subroutine)
}
}
}
}
override fun before(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
if(pipeExpr.source is PipeExpression) {
// correct Antlr parse tree quirk; turn nested pipe into single flat pipe
val psrc = pipeExpr.source as PipeExpression
val newSource = psrc.source
val newSegments = psrc.segments
newSegments += pipeExpr.segments.single()
return listOf(IAstModification.ReplaceNode(pipeExpr as Node, PipeExpression(newSource, newSegments, pipeExpr.position), parent))
} else if(pipeExpr.source is IPipe)
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
return noModifications
}
}
@@ -1,66 +0,0 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.DataType
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
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).
val symbolsInSub = subroutine.allDefinedSymbols
val namesInSub = symbolsInSub.map{ it.first }.toSet()
if(subroutine.asmAddress==null) {
if(!subroutine.isAsmSubroutine && subroutine.parameters.isNotEmpty()) {
val vars = subroutine.statements.asSequence().filterIsInstance<VarDecl>().map { it.name }.toSet()
if(!vars.containsAll(subroutine.parameters.map{it.name})) {
return subroutine.parameters
.filter { it.name !in namesInSub }
.map {
val vardecl = VarDecl.fromParameter(it)
IAstModification.InsertFirst(vardecl, subroutine)
}
}
}
}
return noModifications
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent)
}
}
internal fun replacePointerVarIndexWithMemreadOrMemwrite(program: Program, arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
// rewrite pointervar[index] into @(pointervar+index)
val indexer = arrayIndexedExpression.indexer
val add = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexer.indexExpr, arrayIndexedExpression.position)
return if(parent is AssignTarget) {
// we're part of the target of an assignment, we have to actually change the assign target itself
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
} else {
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
}
}
return emptyList()
}
@@ -67,6 +67,10 @@ internal class BeforeAsmAstChanger(val program: Program,
// Try to replace A = B <operator> Something by A= B, A = A <operator> Something
// this triggers the more efficent augmented assignment code generation more often.
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimization for Vm target
return noModifications
if(!assignment.isAugmentable
&& assignment.target.identifier != null
&& !assignment.target.isIOAddress(options.compTarget.machine)) {
@@ -120,6 +124,11 @@ internal class BeforeAsmAstChanger(val program: Program,
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
// Most code generation targets only support subroutine inlining on asmsub subroutines
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
if(!subroutine.isAsmSubroutine && options.compTarget.name!=VMTarget.NAME)
subroutine.inline = false
val mods = mutableListOf<IAstModification>()
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
@@ -163,21 +172,36 @@ internal class BeforeAsmAstChanger(val program: Program,
}
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
val prefixExpr = ifElse.condition as? PrefixExpression
if(prefixExpr!=null && prefixExpr.operator=="not") {
// if not x -> if x==0
val binExpr = ifElse.condition as? BinaryExpression
if(binExpr==null) {
// if x -> if x!=0
val booleanExpr = BinaryExpression(
prefixExpr.expression,
"==",
ifElse.condition,
"!=",
NumericLiteral.optimalInteger(0, ifElse.condition.position),
ifElse.condition.position
)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
}
val binExpr = ifElse.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in ComparisonOperators) {
// if x -> if x!=0, if x+5 -> if x+5 != 0
if(binExpr.operator !in ComparisonOperators) {
val constRight = binExpr.right.constValue(program)
if(constRight!=null) {
if (binExpr.operator == "+") {
// if x+5 -> if x != -5
val number = NumericLiteral(constRight.type, -constRight.number, constRight.position)
val booleanExpr = BinaryExpression(binExpr.left,"!=", number, ifElse.condition.position)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
}
else if (binExpr.operator == "-") {
// if x-5 -> if x != 5
val number = NumericLiteral(constRight.type, constRight.number, constRight.position)
val booleanExpr = BinaryExpression(binExpr.left,"!=", number, ifElse.condition.position)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
}
}
// if x*5 -> if x*5 != 0
val booleanExpr = BinaryExpression(
ifElse.condition,
"!=",
@@ -229,7 +253,7 @@ internal class BeforeAsmAstChanger(val program: Program,
// in that case: do *not* split it off but just keep it as it is (otherwise code size increases)
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimizer for Vm target
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimization for Vm target
return CondExprSimplificationResult(null, null, null, null)
var leftAssignment: Assignment? = null
@@ -237,8 +261,12 @@ internal class BeforeAsmAstChanger(val program: Program,
var rightAssignment: Assignment? = null
var rightOperandReplacement: Expression? = null
val separateLeftExpr = !expr.left.isSimple && expr.left !is IFunctionCall && expr.left !is ContainmentCheck
val separateRightExpr = !expr.right.isSimple && expr.right !is IFunctionCall && expr.right !is ContainmentCheck
val separateLeftExpr = !expr.left.isSimple
&& expr.left !is IFunctionCall
&& expr.left !is ContainmentCheck
val separateRightExpr = !expr.right.isSimple
&& expr.right !is IFunctionCall
&& expr.right !is ContainmentCheck
val leftDt = expr.left.inferType(program)
val rightDt = expr.right.inferType(program)
@@ -279,7 +307,7 @@ internal class BeforeAsmAstChanger(val program: Program,
return noModifications
}
if(options.compTarget.name!=VMTarget.NAME) {
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
@@ -343,5 +371,4 @@ internal class BeforeAsmAstChanger(val program: Program,
)
return modifications
}
}
@@ -15,10 +15,18 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
private val errors: IErrorReporter
) : AstWalker() {
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
if(typecast.type == DataType.BOOL) {
val notZero = BinaryExpression(typecast.expression, "!=", NumericLiteral(DataType.UBYTE, 0.0, typecast.position), typecast.position)
return listOf(IAstModification.ReplaceNode(typecast, notZero, parent))
}
return noModifications
}
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
// see if we can remove redundant typecasts (outside of expressions)
// such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type).
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of,
// the special typecast of a reference type (str, array) to an UWORD will be changed into address-of,
// UNLESS it's a str parameter in the containing subroutine - then we remove the typecast altogether
val sourceDt = typecast.expression.inferType(program).getOr(DataType.UNDEFINED)
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
@@ -70,7 +78,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
return noModifications
}
// also convert calls to builtin functions to BuiltinFunctionCall nodes to make things easier for codegen
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
@@ -86,7 +93,6 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
return noModifications
}
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource.size==1
&& functionCallExpr.target.nameInSource[0] in program.builtinFunctions.names) {
@@ -123,5 +129,4 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
}
return noModifications
}
}
@@ -0,0 +1,121 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.Subroutine
import prog8.ast.statements.SubroutineParameter
import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
internal class BoolRemover(val program: Program) : AstWalker() {
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
if(typecast.type == DataType.BOOL) {
val notZero = BinaryExpression(typecast.expression, "!=", NumericLiteral(DataType.UBYTE, 0.0, typecast.position), typecast.position)
return listOf(IAstModification.ReplaceNode(typecast, notZero, parent))
}
return noModifications
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.datatype==DataType.BOOL) {
var newvalue = decl.value
if(newvalue is NumericLiteral) {
if(newvalue.number!=0.0)
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.subroutineParameter, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
}
if(decl.datatype==DataType.ARRAY_BOOL) {
var newarray = decl.value
if(decl.value is ArrayLiteral) {
val oldArray = (decl.value as ArrayLiteral).value
val convertedArray = oldArray.map {
var number: Expression = it
if (it is NumericLiteral && it.type == DataType.BOOL)
number = NumericLiteral(DataType.UBYTE, if (it.number == 0.0) 0.0 else 1.0, number.position)
number
}.toTypedArray()
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.subroutineParameter, decl.position)
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
}
return noModifications
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
// replace BOOL return type and parameters by UBYTE
if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) {
val newReturnTypes = subroutine.returntypes.map {
if(it==DataType.BOOL) DataType.UBYTE else it
}
val newParams = subroutine.parameters.map {
if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it
}.toMutableList()
val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes,
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers,
subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements,
subroutine.position)
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
}
return noModifications
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// convert boolean and/or/xor/not operators to bitwise equivalents.
// so that codegen only has to work with bitwise boolean operations from now on.
// note: this has to be done here and not in BeforeAsmTypecastCleaner! (code size will increase if done there...)
if(expr.operator in setOf("and", "or", "xor")) {
expr.operator = when(expr.operator) {
"and" -> "&"
"or" -> "|"
"xor" -> "^"
else -> "invalid"
}
val mods = mutableListOf<IAstModification>()
val newLeft = wrapWithBooleanCastIfNeeded(expr.left, program)
val newRight = wrapWithBooleanCastIfNeeded(expr.right, program)
if(newLeft!=null)
mods += IAstModification.ReplaceNodeSafe(expr.left, newLeft, expr)
if(newRight!=null)
mods += IAstModification.ReplaceNodeSafe(expr.right, newRight, expr)
return mods
}
return noModifications
}
}
internal fun wrapWithBooleanCastIfNeeded(expr: Expression, program: Program): Expression? {
fun isBoolean(expr: Expression): Boolean {
return if(expr.inferType(program) istype DataType.BOOL)
true
else if(expr is NumericLiteral && expr.type in IntegerDatatypes && (expr.number==0.0 || expr.number==1.0))
true
else if(expr is BinaryExpression && expr.operator in ComparisonOperators + LogicalOperators)
true
else if(expr is PrefixExpression && expr.operator == "not")
true
else if(expr is BinaryExpression && expr.operator in BitwiseOperators) {
if(isBoolean(expr.left) && isBoolean(expr.right))
true
else expr.operator=="&" && expr.right.constValue(program)?.number==1.0 // x & 1 is also a boolean result
}
else
false
}
return if(isBoolean(expr))
null
else
TypecastExpression(expr, DataType.BOOL, true, expr.position)
}
@@ -1,20 +1,17 @@
package prog8.compiler.astprocessing
import prog8.ast.*
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.PrefixExpression
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.Position
private var generatedLabelSequenceNumber: Int = 0
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
internal class CodeDesugarer(val program: Program,
private val errors: IErrorReporter) : AstWalker() {
// Some more code shuffling to simplify the Ast that the codegenerator has to process.
// Several changes have already been done by the StatementReorderer !
@@ -28,23 +25,11 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
// - repeat-forever loops replaced by label+jump.
private val generatedLabelPrefix = "prog8_label_"
private fun makeLabel(postfix: String, position: Position): Label {
generatedLabelSequenceNumber++
return Label("${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix", position)
}
private fun jumpLabel(label: Label): Jump {
val ident = IdentifierReference(listOf(label.name), label.position)
return Jump(null, ident, null, label.position)
}
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
fun jumpAfter(stmt: Statement): Iterable<IAstModification> {
val label = makeLabel("after", breakStmt.position)
val label = program.makeLabel("after", breakStmt.position)
return listOf(
IAstModification.ReplaceNode(breakStmt, jumpLabel(label), parent),
IAstModification.ReplaceNode(breakStmt, program.jumpLabel(label), parent),
IAstModification.InsertAfter(stmt, label, stmt.parent as IStatementContainer)
)
}
@@ -71,17 +56,18 @@ do { STUFF } until CONDITION
===>
_loop:
STUFF
if not CONDITION
if CONDITION==0
goto _loop
*/
val pos = untilLoop.position
val loopLabel = makeLabel("untilloop", pos)
val notCondition = PrefixExpression("not", untilLoop.condition, pos)
val loopLabel = program.makeLabel("untilloop", pos)
val ifCondition = invertCondition(untilLoop.condition)
?: BinaryExpression(untilLoop.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, pos), pos)
val replacement = AnonymousScope(mutableListOf(
loopLabel,
untilLoop.body,
IfElse(notCondition,
AnonymousScope(mutableListOf(jumpLabel(loopLabel)), pos),
IfElse(ifCondition,
AnonymousScope(mutableListOf(program.jumpLabel(loopLabel)), pos),
AnonymousScope(mutableListOf(), pos),
pos)
), pos)
@@ -93,23 +79,24 @@ if not CONDITION
while CONDITION { STUFF }
==>
_whileloop:
if NOT CONDITION goto _after
if CONDITION==0 goto _after
STUFF
goto _whileloop
_after:
*/
val pos = whileLoop.position
val loopLabel = makeLabel("whileloop", pos)
val afterLabel = makeLabel("afterwhile", pos)
val notCondition = PrefixExpression("not", whileLoop.condition, pos)
val loopLabel = program.makeLabel("whileloop", pos)
val afterLabel = program.makeLabel("afterwhile", pos)
val ifCondition = invertCondition(whileLoop.condition)
?: BinaryExpression(whileLoop.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, pos), pos)
val replacement = AnonymousScope(mutableListOf(
loopLabel,
IfElse(notCondition,
AnonymousScope(mutableListOf(jumpLabel(afterLabel)), pos),
IfElse(ifCondition,
AnonymousScope(mutableListOf(program.jumpLabel(afterLabel)), pos),
AnonymousScope(mutableListOf(), pos),
pos),
whileLoop.body,
jumpLabel(loopLabel),
program.jumpLabel(loopLabel),
afterLabel
), pos)
return listOf(IAstModification.ReplaceNode(whileLoop, replacement, parent))
@@ -138,8 +125,8 @@ _after:
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
if(repeatLoop.iterations==null) {
val label = makeLabel("repeat", repeatLoop.position)
val jump = jumpLabel(label)
val label = program.makeLabel("repeat", repeatLoop.position)
val jump = program.jumpLabel(label)
return listOf(
IAstModification.InsertFirst(label, repeatLoop.body),
IAstModification.InsertLast(jump, repeatLoop.body),
@@ -0,0 +1,80 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.IntegerDatatypes
internal class NotExpressionChanger(val program: Program, val errors: IErrorReporter) : AstWalker() {
override fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator=="==" || expr.operator=="!=") {
val left = expr.left as? BinaryExpression
if (left != null) {
val rightValue = expr.right.constValue(program)
if (rightValue?.number == 0.0 && rightValue.type in IntegerDatatypes) {
if (left.operator == "==" && expr.operator == "==") {
// (x==something)==0 --> x!=something
left.operator = "!="
return listOf(IAstModification.ReplaceNode(expr, left, parent))
} else if (left.operator == "!=" && expr.operator == "==") {
// (x!=something)==0 --> x==something
left.operator = "=="
return listOf(IAstModification.ReplaceNode(expr, left, parent))
} else if (left.operator == "==" && expr.operator == "!=") {
// (x==something)!=0 --> x==something
left.operator = "=="
return listOf(IAstModification.ReplaceNode(expr, left, parent))
} else if (left.operator == "!=" && expr.operator == "!=") {
// (x!=something)!=0 --> x!=something
left.operator = "!="
return listOf(IAstModification.ReplaceNode(expr, left, parent))
}
}
}
}
return noModifications
}
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator == "not") {
// not(not(x)) -> x
if((expr.expression as? PrefixExpression)?.operator=="not")
return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent))
// not(~x) -> x!=0
if((expr.expression as? PrefixExpression)?.operator=="~") {
val x = (expr.expression as PrefixExpression).expression
val dt = x.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
val notZero = BinaryExpression(x, "!=", NumericLiteral(dt, 0.0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, notZero, parent))
}
val subBinExpr = expr.expression as? BinaryExpression
if(subBinExpr?.operator=="==") {
if(subBinExpr.right.constValue(program)?.number==0.0) {
// not(x==0) -> x!=0
subBinExpr.operator = "!="
return listOf(IAstModification.ReplaceNode(expr, subBinExpr, parent))
}
} else if(subBinExpr?.operator=="!=") {
if(subBinExpr.right.constValue(program)?.number==0.0) {
// not(x!=0) -> x==0
subBinExpr.operator = "=="
return listOf(IAstModification.ReplaceNode(expr, subBinExpr, parent))
}
}
// all other not(x) --> x==0
// this means that "not" will never occur anywhere again in the ast after this
val replacement = BinaryExpression(expr.expression, "==", NumericLiteral(DataType.UBYTE,0.0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent))
}
return noModifications
}
}
Loaded 100 of 194 files, more files were not shown because too many files have changed in this diff. Show more