Compare commits

..

144 Commits
v8.0 ... v8.2

Author SHA1 Message Date
53a3c59a91 language for sphinx 2022-06-15 22:38:00 +02:00
df36983049 version 8.2 2022-06-15 22:31:29 +02:00
bda016bb3b optimized 6502 codegen for logical expressions 2022-06-15 22:17:15 +02:00
cc174b7b85 added boolean() builtin function and use it to get rid of !=0 comparisons 2022-06-14 23:34:45 +02:00
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
775c85fc18 don't swap operands that would change function evaluation order + vm: fix label casing error 2022-06-13 00:25:45 +02:00
5a756aaed9 Pipe expression "|>" removed from the language 2022-06-12 18:41:42 +02:00
dca092fd7c fix pipe expression when start term is constant number 2022-06-12 16:59:28 +02:00
c6e92ecac4 some code cleanup 2022-06-12 16:15:08 +02:00
93008ff605 tweak zsound examples 2022-06-12 14:51:24 +02:00
43c7b935df fixed zsound pcm player example 2022-06-11 03:31:42 +02:00
8f9a0a244a trying to add zsound pcm player example as well 2022-06-10 23:35:37 +02:00
fd13bd864e some notes added to zsound demo player 2022-06-09 23:36:07 +02:00
710f27afa9 bump library versions 2022-06-09 22:44:17 +02:00
f537793b0b added zsound demo player example (cx16) 2022-06-08 23:57:01 +02:00
f7183e38ee tweak trivial subroutine inlining 2022-06-08 21:05:03 +02:00
0a65dfdd10 optimized codegen for some more simple expressions with +/- 2022-06-07 22:30:08 +02:00
3075578245 optimized codegen for assigning value or variable to indexed pointer. (6502) 2022-06-06 18:30:19 +02:00
b042b7705e fix invalid removal of repeated assignments. 2022-06-06 17:27:06 +02:00
d56eb397f9 fix codegen for rol/ror on pointer indexed 2022-06-06 16:07:45 +02:00
3054a1d32d api change: removed swap() builtin function (too complex in codegen for little used function) 2022-06-06 16:01:11 +02:00
0a3cd652b0 vm: fix codegen for storing to pointer indexed 2022-06-06 14:18:12 +02:00
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
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
031f647952 allow casting negative numbers to unsigned, result = 2's complement 2022-06-05 14:21:10 +02:00
8f1c86f550 fixed several old test files 2022-06-05 14:20:08 +02:00
926fdecd13 fix problematic path handling on windows in error messages 2022-06-05 11:54:19 +02:00
af2ca7a67e fix problematic characters that cause path errors on Windows 2022-06-05 11:46:37 +02:00
9e3e2ff81a fix assembly generation error when pipe character is part of string literal 2022-06-04 22:25:51 +02:00
a9fe6472d9 remove old screencode syntax from docs 2022-06-04 22:07:31 +02:00
a862a81480 added unit test for name shadowing warning 2022-06-04 21:35:48 +02:00
dbb92881a1 fixed X register corruption in some cases of rol() and ror() 2022-06-04 21:10:48 +02:00
10bf7f5d07 fix: again gives proper name redefinition errors in same scope 2022-06-04 20:15:46 +02:00
1e61d84fd1 vm: fix expression codegen for pointer indexing 2022-06-04 19:32:35 +02:00
8618ba1b60 fix 6502 expression codegen for pointer indexing 2022-06-04 18:46:16 +02:00
3c8c44155d vm: loadix instruction added for indirect addressing via pointer 2022-06-04 18:07:57 +02:00
2002412026 optimized codegen for pointer indexing (read expressions) 2022-06-04 17:20:17 +02:00
7f69517fd4 preparing optimizing pointer indexing 2022-06-04 16:18:27 +02:00
851f8645b5 Merge remote-tracking branch 'origin/master' 2022-06-04 14:23:41 +02:00
c40cfaa388 preparing optimizing pointer indexing 2022-06-04 14:23:02 +02:00
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
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
73a3a61729 swap() checks for unsupported code gen 2022-06-03 23:41:24 +02:00
5fe6aa2800 fix swap() code for pointervars 2022-06-03 23:13:35 +02:00
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
10b5fb5d72 fix for total size returnvalue of diskio.f_read_all() 2022-06-01 01:13:19 +02:00
c4eaa944e2 thoughts 2022-05-30 23:37:41 +02:00
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
6ed5f04970 version 8.1 2022-05-25 20:00:26 +02:00
b459b09b2f vm: fix comparison datatype error; primes.p8 works again 2022-05-24 18:26:07 +02:00
3f5877dbcc vm: fix array iteration 2022-05-23 21:24:36 +02:00
e659b91c4d vm: fix storezm/storezx instructions 2022-05-23 21:01:02 +02:00
e09f054058 vm: implemented in-place bit rotate instructions 2022-05-23 20:30:25 +02:00
b646f50265 vm: implemented in-memory bit shift instructions 2022-05-23 20:15:20 +02:00
0a48ef3030 vm: just use new register instead of trying to (ab)use reg 0 2022-05-22 23:38:46 +02:00
ba614801ee cleanup 2022-05-22 23:11:22 +02:00
fd6eb47e68 added inlining certain trivial non-asm subroutine calls 2022-05-22 20:22:09 +02:00
e69aeb8b98 added warning about shadowing variables 2022-05-22 17:34:08 +02:00
26ea1da146 vm: add in-place bitwise or,and,xor 2022-05-20 20:50:27 +02:00
c9e8c7a290 vm: add in-place division 2022-05-19 23:38:16 +02:00
5e4eb92443 vm: add in-place multiply 2022-05-19 23:18:54 +02:00
461b6499ef vm: add in-place add/sub 2022-05-19 22:54:50 +02:00
c769920b6e vm: fix signed divide 2022-05-19 22:24:57 +02:00
181b98ef9e vm: implemented some self-assign instructions 2022-05-18 22:15:42 +02:00
4e1184a400 vm: added some of the sin cos tables in math.p8 2022-05-17 22:56:00 +02:00
e52d9e3210 vm: split off assignment codegen to its own file 2022-05-17 22:38:31 +02:00
dc6475c91b vm: fixed non-byte array indexing 2022-05-17 18:53:33 +02:00
52f9956e92 clarify use of direct-memory in functions that modify in place such as rol/swap 2022-05-16 22:41:31 +02:00
0bf00d1ca4 c64/c128 targets: perform cleanup at program exit such as re-enabling run-stop key and character set switching. 2022-05-15 16:44:26 +02:00
d1a707df57 fix assigning a pointer (uword) to string not copying the correct memory 2022-05-15 16:10:58 +02:00
4dc9b45297 vm: fixed string comparisons, added missing vm string module 2022-05-13 23:10:13 +02:00
6e31eebfb5 vm: ifElse codegen uses proper branching instructions now 2022-05-12 21:26:17 +02:00
a7df828932 vm: codegen uses INCM/DECM if possible 2022-05-12 19:40:31 +02:00
517cf61d11 vm: limit int instructions to just 2 register args 2022-05-11 22:36:47 +02:00
4be7bc8323 vm: limit float instructions to just 2 register args 2022-05-11 22:09:46 +02:00
74c05d00a9 vm: fix comparison operator codegen for floats 2022-05-11 17:07:21 +02:00
677613d30a vm: expressiongen: use resultRegister arg instead of allocating new leftResultReg 2022-05-11 15:58:55 +02:00
bacba629a5 vm: use shift-one instructions in codegen 2022-05-11 15:50:51 +02:00
14e36f1362 vm: fix assignment to array 2022-05-11 15:26:54 +02:00
d43ad849d1 vm: actually use the store-zero instructions in codegen 2022-05-11 15:18:36 +02:00
627aa61184 clean up subroutine inlining, basis for new try 2022-05-09 15:42:58 +02:00
dad5b17ac8 fix regression compiler crash in string comparison 2022-05-08 13:47:24 +02:00
fef52c0112 automatically convert multi-compare expression (if X==1 or X==2..) to contaiment check if X in [1,2,..] 2022-05-08 13:21:34 +02:00
8c4765b386 vm: support non-unary functions in pipe expressions 2022-05-07 20:42:05 +02:00
7c121bfc01 first steps to support multiple args in pipe expressions 2022-05-07 19:00:47 +02:00
942c5cc04b fix crash when optimizing pipe expression too aggressively 2022-05-07 17:29:36 +02:00
348b3036ff now correctly accepts "xxx" * constexpr (where constexpr is not just a single const number) 2022-05-05 23:21:20 +02:00
09d3451d9d vm: accept %asmbinary (but it is eventually ignored in code execution) 2022-05-05 21:43:31 +02:00
b1a49e5f29 vm: implement rest of float instructions 2022-05-04 22:31:45 +02:00
da01a5b4dc vm: implement float to integer cast, any, all, reverse 2022-05-04 22:08:21 +02:00
3f9cdd9b56 vm: fix mul and div instructions 2022-05-04 01:10:59 +02:00
0f9e87d7bb fixed compiler crash when casting float to integer, fixed float to int cast value error on cx16 2022-05-03 23:43:38 +02:00
0869789214 vm: implement float type casts to integer types 2022-05-02 23:38:32 +02:00
10c8cc35c5 vm: implement float divide multiply sub add 2022-05-02 21:53:43 +02:00
30c2e3e8ff vm: fix comparisons codegen 2022-05-02 21:32:45 +02:00
86cc2f1075 vm: implementing more fp instructions 2022-05-02 21:06:14 +02:00
fa357a450b clarify license 2022-05-02 19:46:08 +02:00
b32641db87 remove syscall() builtin functions
vm code can do this via inline assembly
2022-05-01 00:41:30 +02:00
0ee790969d vm: allow inline "assembly" 2022-04-30 23:24:25 +02:00
7844ace934 vm: implementing floating-point 2022-04-29 22:27:02 +02:00
f4993d6e5d vm: fix instruction type checks 2022-04-28 22:19:46 +02:00
0fab806f36 vm: some preparations for floating point 2022-04-27 17:45:58 +02:00
be2113d291 vm: starting to implement floating point instructions 2022-04-26 21:25:59 +02:00
625d5b2313 vm: some preparations for floating point 2022-04-26 21:08:32 +02:00
6471c0c536 upgrade antlr to 4.10.1 2022-04-24 23:29:15 +02:00
47c53fa60a todo 2022-04-23 20:44:59 +02:00
cf50e4f6ec vm: printing of numbers now via conv module.
assigning strings now converted to strcopy function call in the compiler ast.
2022-04-23 02:15:51 +02:00
7eea97d741 - floats: remove all floating point builtin functions and move them to the floats module instead 2022-04-22 00:45:54 +02:00
88b55ab93e vm: add abs() and fix 6502 abs() code. 2022-04-18 21:20:17 +02:00
ee36d47c27 vm: added cmp() and most of the status-branch instructions 2022-04-18 19:59:48 +02:00
6f2fdbe447 added %option merge, also fixed problem with unit test building in newer IntelliJ version 2022-04-15 22:38:32 +02:00
0f36be0001 vm: simple optimizations for +/-/*/div with constants 2022-04-14 22:42:25 +02:00
0f4a197e34 improve ast check on pipe expressions 2022-04-14 00:49:06 +02:00
7dbff5b9e6 abs: remove support for floats. Use floats.fabs() instead.
this solves: can't use abs() etc in pipe expression because return type depends on argument type
2022-04-14 00:38:31 +02:00
220246278a removed sum(), max(), min(). abs() now always returns uword type.
This greatly simplifies internal handling of builtin functions by always having one fixed return type.
2022-04-14 00:21:16 +02:00
349e5a15e9 min/max give proper error for string args
als implmented more vm builtin functions/syscalls
2022-04-13 23:09:25 +02:00
bf7f4bba7b doc 2022-04-13 20:43:07 +02:00
ab1766a559 moved all *integer* builtin trig functions (sin8u, cos8u etc) as regular asmsubs in math module 2022-04-13 00:27:35 +02:00
51bf33040a vm: add many builtin functions 2022-04-11 22:39:33 +02:00
a2c7273801 vm: use memory load instruction better 2022-04-11 20:55:06 +02:00
ec6ac5bf24 vm: added swap() 2022-04-11 01:50:47 +02:00
ec7501782d vm: added 1-bit variants of lsr/lsl opcodes 2022-04-11 00:25:00 +02:00
890b1c2d52 more readable 2022-04-10 22:31:37 +02:00
c25d07259a add block directive options to PtBlock 2022-04-10 21:37:47 +02:00
c960246eee add some utility methods to PtNode to find the defining subroutine/block 2022-04-10 21:20:01 +02:00
a01aee3111 add sideEffects boolean to PtBuiltinFunctionCall 2022-04-10 21:08:54 +02:00
e2e951efdf constValue(expr) convenience function added for new Ast expression nodes 2022-04-10 18:45:33 +02:00
3f6393f732 PtNumber can now be compared 2022-04-10 17:48:03 +02:00
b6eb343234 moving string escaping out of antlr project 2022-04-10 17:31:30 +02:00
207a7e5160 move operator lists 2022-04-10 13:24:17 +02:00
a0face4a28 vm: implementing rol/ror 2022-04-09 11:13:49 +02:00
a8cf9f5cc4 vm: syscalls can now return value 2022-04-05 20:46:34 +02:00
461b38e653 add -vm option to load an existing p8virt file directly in the virtual machine 2022-04-05 18:42:31 +02:00
8e4c0f7c22 vm: add sorting and reverse functions, fix value arg out of range errors 2022-04-05 17:48:49 +02:00
d78bfcc35c vm: more optimal code when array index is constant value 2022-04-05 00:19:37 +02:00
2b7c09e6ee vm: more optimal code for loops ending on 0 2022-04-05 00:08:38 +02:00
036d9dbe59 got rid of unnecessary cast of boolean expressions by making their type dynamically adjust to byte or word 2022-04-04 23:43:55 +02:00
1d342cc6af optimize cx16 textio.setcc()/setcc2() 2022-04-04 22:23:06 +02:00
62b32b2211 todos 2022-04-03 22:56:13 +02:00
ae45ce517e cleanups 2022-04-03 17:33:50 +02:00
5b3ccab7dc vm: support noreinit option 2022-04-03 17:19:50 +02:00
95f16c38a9 removed 'aug' property in PtAssignment , it wasn't used for anything 2022-04-03 15:56:14 +02:00
d616cb283b vm: implemented Pipe expression 2022-04-03 15:25:32 +02:00
9874fe2c23 fix superfluous printing of WARN/ERROR words 2022-04-02 22:16:47 +02:00
211 changed files with 8344 additions and 5970 deletions

View File

@ -1,15 +1,15 @@
<component name="libraryTable">
<library name="antlr.antlr4" type="repository">
<properties maven-id="org.antlr:antlr4:4.9.3">
<properties maven-id="org.antlr:antlr4:4.10.1">
<exclude>
<dependency maven-id="com.ibm.icu:icu4j" />
</exclude>
</properties>
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.9.3/antlr4-4.9.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.9.3/antlr4-runtime-4.9.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.2/antlr-runtime-3.5.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.1/ST4-4.3.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.10.1/antlr4-4.10.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.10.1/antlr4-runtime-4.10.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.3/ST4-4.3.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar!/" />
</CLASSES>

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.2.3" />
<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$/io/kotest/kotest-assertions-core-jvm/5.2.3/kotest-assertions-core-jvm-5.2.3.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$/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-shared-jvm/5.2.3/kotest-assertions-shared-jvm-5.2.3.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/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.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-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.2.3/kotest-common-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.2.3/kotest-assertions-api-jvm-5.2.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -1,22 +1,22 @@
<component name="libraryTable">
<library name="io.kotest.property.jvm" type="repository">
<properties maven-id="io.kotest:kotest-property-jvm:5.1.0" />
<properties maven-id="io.kotest:kotest-property-jvm:5.2.3" />
<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$/io/kotest/kotest-property-jvm/5.2.3/kotest-property-jvm-5.2.3.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.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/kotest/kotest-common-jvm/5.2.3/kotest-common-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.2.3/kotest-assertions-shared-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.2.3/kotest-assertions-api-jvm-5.2.3.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$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.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.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 />

View File

@ -1,26 +1,26 @@
<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.2.3" />
<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.2.3/kotest-runner-junit5-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.2.3/kotest-framework-api-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.2.3/kotest-assertions-shared-jvm-5.2.3.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$/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.2.3/kotest-common-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.2.3/kotest-framework-engine-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.138/classgraph-4.8.138.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.2.3/kotest-framework-discovery-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.2.3/kotest-assertions-core-jvm-5.2.3.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.2.3/kotest-assertions-api-jvm-5.2.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.2.3/kotest-extensions-jvm-5.2.3.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!/" />
@ -32,8 +32,8 @@
<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$/io/kotest/kotest-framework-concurrency-jvm/5.2.3/kotest-framework-concurrency-jvm-5.2.3.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!/" />

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 />

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 />

View File

@ -1,6 +1,9 @@
This sofware license is for Prog8 the compiler + associated libraries.
The software generated by running the compiler is excluded from this.
This sofware license is for Prog8 the compiler + associated library files.
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.

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?

View File

@ -30,6 +30,10 @@ sealed class PtNode(val position: Position) {
children.add(index, child)
child.parent = this
}
fun definingBlock() = findParentNode<PtBlock>(this)
fun definingSub() = findParentNode<PtSub>(this)
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
}
@ -76,10 +80,18 @@ class PtProgram(
class PtBlock(name: String,
val address: UInt?,
val library: Boolean,
val forceOutput: Boolean,
val alignment: BlockAlignment,
position: Position
) : PtNamedNode(name, position) {
override fun printProperties() {
print("$name addr=$address library=$library")
print("$name addr=$address library=$library forceOutput=$forceOutput alignment=$alignment")
}
enum class BlockAlignment {
NONE,
WORD,
PAGE
}
}
@ -116,3 +128,17 @@ class PtNop(position: Position): PtNode(position) {
class PtScopeVarsDecls(position: Position): PtNode(position) {
override fun printProperties() {}
}
// find the parent node of a specific type or interface
// (useful to figure out in what namespace/block something is defined, etc.)
inline fun <reified T> findParentNode(node: PtNode): T? {
var candidate = node.parent
while(candidate !is T && candidate !is PtProgram)
candidate = candidate.parent
return if(candidate is PtProgram)
null
else
candidate as T
}

View File

@ -3,14 +3,32 @@ package prog8.code.ast
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.Position
import java.util.*
import kotlin.math.round
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
override fun printProperties() {
print(type)
}
}
infix fun isSameAs(other: PtExpression): Boolean {
return when(this) {
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
is PtIdentifier -> other is PtIdentifier && other.type==type && other.targetName==targetName
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
is PtNumber -> other is PtNumber && other.type==type && other.number==number
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
else -> false
}
}
}
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
val identifier: PtIdentifier
@ -26,10 +44,21 @@ class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, pos
}
class PtArray(type: DataType, position: Position): PtExpression(type, position)
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
override fun hashCode(): Int = Objects.hash(children, type)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtArray)
return false
return type==other.type && children == other.children
}
}
class PtBuiltinFunctionCall(val name: String, val void: Boolean, type: DataType, position: Position) : PtExpression(type, position) {
class PtBuiltinFunctionCall(val name: String,
val void: Boolean,
val hasNoSideEffects: Boolean,
type: DataType,
position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
@ -38,7 +67,7 @@ class PtBuiltinFunctionCall(val name: String, val void: Boolean, type: DataType,
val args: List<PtExpression>
get() = children.map { it as PtExpression }
override fun printProperties() {
print("$name void=$void")
print("$name void=$void noSideFx=$hasNoSideEffects")
}
}
@ -95,19 +124,28 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
init {
if(type!=DataType.FLOAT) {
val rounded = round(number)
if (rounded != number)
throw IllegalArgumentException("refused rounding of float to avoid loss of precision")
}
}
override fun printProperties() {
print("$number ($type)")
}
}
override fun hashCode(): Int = Objects.hash(type, number)
class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtNumber)
return false
return number==other.number
}
override fun printProperties() {}
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
}
@ -137,6 +175,13 @@ class PtString(val value: String, val encoding: Encoding, position: Position) :
override fun printProperties() {
print("$encoding:\"$value\"")
}
override fun hashCode(): Int = Objects.hash(value, encoding)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtString)
return false
return value==other.value && encoding == other.encoding
}
}
@ -144,3 +189,15 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
val value: PtExpression
get() = children.single() as PtExpression
}
// special node that isn't created from compiling user code, but used internally
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) {
override fun printProperties() {
print("reg=$register $type")
}
}
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null

View File

@ -38,18 +38,43 @@ class PtSubroutineParameter(val name: String, val type: DataType, position: Posi
}
class PtAssignment(val augmentable: Boolean, position: Position) : PtNode(position) {
class PtAssignment(position: Position) : PtNode(position) {
val target: PtAssignTarget
get() = children[0] as PtAssignTarget
val value: PtExpression
get() = children[1] as PtExpression
override fun printProperties() {
print("aug=$augmentable")
override fun printProperties() { }
val isInplaceAssign: Boolean by lazy {
val target = target.children.single() as PtExpression
when(val source = value) {
is PtArrayIndexer -> {
if(target is PtArrayIndexer && source.type==target.type) {
if(target.variable isSameAs source.variable) {
target.index isSameAs source.index
}
}
false
}
is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.targetName==source.targetName
is PtMachineRegister -> target is PtMachineRegister && target.register==source.register
is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address
is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number
is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier
is PtPrefix -> {
(target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value)
||
(target is PtIdentifier && (source.value as? PtIdentifier)?.targetName==target.targetName)
}
is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value
is PtBinaryExpression ->
target isSameAs source.left
else -> false
}
}
}
class PtAssignTarget(position: Position) : PtNode(position) {
val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier
@ -62,9 +87,9 @@ class PtAssignTarget(position: Position) : PtNode(position) {
get() {
return when(val tgt = children.single()) {
is PtIdentifier -> tgt.type
is PtArrayIndexer -> tgt.type // TODO array to elt type?
is PtArrayIndexer -> tgt.type
is PtMemoryByte -> tgt.type
else -> throw AssemblyError("weird dt")
else -> throw AssemblyError("weird target $tgt")
}
}
@ -97,8 +122,8 @@ class PtForLoop(position: Position) : PtNode(position) {
class PtIfElse(position: Position) : PtNode(position) {
val condition: PtExpression
get() = children[0] as PtExpression
val condition: PtBinaryExpression
get() = children[0] as PtBinaryExpression
val ifScope: PtNodeGroup
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup

View File

@ -24,9 +24,9 @@ compileTestKotlin {
}
dependencies {
implementation project(':virtualmachine')
// 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 {

View File

@ -10,6 +10,5 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="module" module-name="virtualmachine" />
</component>
</module>

View File

@ -0,0 +1,89 @@
package prog8.code.core
import kotlin.math.abs
fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
return '-' + abs(integer).toHex()
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
}
}
fun UInt.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
return when (this) {
in 0u until 16u -> this.toString()
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
}
}
fun Char.escape(): Char = this.toString().escape()[0]
fun String.escape(): String {
val es = this.map {
when(it) {
'\t' -> "\\t"
'\n' -> "\\n"
'\r' -> "\\r"
'"' -> "\\\""
in '\u8000'..'\u80ff' -> "\\x" + (it.code - 0x8000).toString(16).padStart(2, '0') // 'ugly' passthrough hack
in '\u0000'..'\u00ff' -> it.toString()
else -> "\\u" + it.code.toString(16).padStart(4, '0')
}
}
return es.joinToString("")
}
fun String.unescape(): String {
val result = mutableListOf<Char>()
val iter = this.iterator()
while(iter.hasNext()) {
val c = iter.nextChar()
if(c=='\\') {
val ec = iter.nextChar()
result.add(when(ec) {
'\\' -> '\\'
'n' -> '\n'
'r' -> '\r'
'"' -> '"'
'\'' -> '\''
'u' -> {
try {
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
} catch (sb: StringIndexOutOfBoundsException) {
throw IllegalArgumentException("invalid \\u escape sequence")
} catch (nf: NumberFormatException) {
throw IllegalArgumentException("invalid \\u escape sequence")
}
}
'x' -> {
try {
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
(0x8000 + hex).toChar() // 'ugly' pass-through hack
} catch (sb: StringIndexOutOfBoundsException) {
throw IllegalArgumentException("invalid \\x escape sequence")
} catch (nf: NumberFormatException) {
throw IllegalArgumentException("invalid \\x escape sequence")
}
}
else -> throw IllegalArgumentException("invalid escape char in string: \\$ec")
})
} else {
result.add(c)
}
}
return result.joinToString("")
}

View File

@ -53,13 +53,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 {
@ -102,16 +96,16 @@ enum class Statusflag {
enum class BranchCondition {
CS,
CC,
EQ,
EQ, // EQ == Z
Z,
NE,
NE, // NE == NZ
NZ,
MI, // MI == NEG
NEG,
PL, // PL == POS
POS,
VS,
VC,
MI,
NEG,
PL,
POS
}

View File

@ -0,0 +1,18 @@
package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val LogicalOperators = setOf("and", "or", "xor", "not")
val BitwiseOperators = setOf("&", "|", "^")
fun invertedComparisonOperator(operator: String) =
when (operator) {
"==" -> "!="
"!=" -> "=="
"<" -> ">="
">" -> "<="
"<=" -> ">"
">=" -> "<"
else -> null
}

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)
}
}

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>"
}

View File

@ -1,31 +0,0 @@
package prog8.code.core
import kotlin.math.abs
fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
return '-' + abs(integer).toHex()
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
}
}
fun UInt.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
return when (this) {
in 0u until 16u -> this.toString()
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
}
}

View File

@ -4,9 +4,6 @@ import prog8.code.core.CompilationOptions
import prog8.code.core.CpuType
import prog8.code.core.IMachineDefinition
import prog8.code.core.Zeropage
import prog8.vm.Assembler
import prog8.vm.Memory
import prog8.vm.VirtualMachine
import java.io.File
import java.nio.file.Path
@ -17,7 +14,7 @@ class VirtualMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 4
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
@ -33,14 +30,10 @@ class VirtualMachineDefinition: IMachineDefinition {
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
println("\nStarting Virtual Machine...")
// to not have external module dependencies we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val source = File("$programNameWithPath.p8virt").readText()
val (memsrc, programsrc) = source.split("------PROGRAM------".toRegex(), 2)
val memory = Memory()
val assembler = Assembler()
assembler.initializeMemory(memsrc, memory)
val program = assembler.assembleProgram(programsrc)
val vm = VirtualMachine(memory, program)
vm.run(throttle = true)
vm.runProgram(source, true)
}
override fun isIOAddress(address: UInt): Boolean = false
@ -49,3 +42,7 @@ class VirtualMachineDefinition: IMachineDefinition {
override val opcodeNames = emptySet<String>()
}
interface IVirtualMachineRunner {
fun runProgram(source: String, throttle: Boolean)
}

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"
}

View File

@ -1,7 +1,10 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import prog8.ast.*
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.ParentSentinel
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
@ -10,7 +13,6 @@ import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
import prog8.compiler.BuiltinFunctions
import prog8.compiler.builtinFunctionReturnType
import prog8.code.core.SourceCode
import java.util.*
import kotlin.io.path.Path
import kotlin.io.path.writeLines
@ -83,7 +85,7 @@ class AsmGen(internal val program: Program,
}
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
@ -332,7 +334,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")
@ -442,12 +443,6 @@ class AsmGen(internal val program: Program,
internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
private fun translateBuiltinFunctionCallExpression(name: String, singleArg:AsmAssignSource, scope: Subroutine): DataType =
builtinFunctionsAsmGen.translateUnaryFunctioncall(name, singleArg, false, scope)
private fun translateBuiltinFunctionCallStatement(name: String, singleArg: AsmAssignSource, scope: Subroutine) =
builtinFunctionsAsmGen.translateUnaryFunctioncall(name, singleArg, true, scope)
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
@ -947,7 +942,7 @@ $repeatLabel lda $counterVar
assemblyLines.add(assembly)
}
internal fun returnRegisterOfFunction(it: IdentifierReference, argumentTypesForBuiltinFunc: List<DataType>?): RegisterOrPair {
internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair {
return when (val targetRoutine = it.targetStatement(program)!!) {
is BuiltinFunctionPlaceholder -> {
val func = BuiltinFunctions.getValue(targetRoutine.name)
@ -956,16 +951,11 @@ $repeatLabel lda $counterVar
in WordDatatypes -> RegisterOrPair.AY
DataType.FLOAT -> RegisterOrPair.FAC1
else -> {
if(!func.hasReturn)
throw AssemblyError("func has no returntype")
else {
val args = argumentTypesForBuiltinFunc!!.map { defaultZero(it, Position.DUMMY) }
when(builtinFunctionReturnType(func.name, args, program).getOrElse { DataType.UNDEFINED }) {
in ByteDatatypes -> RegisterOrPair.A
in WordDatatypes -> RegisterOrPair.AY
DataType.FLOAT -> RegisterOrPair.FAC1
else -> throw AssemblyError("weird returntype")
}
when(builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED }) {
in ByteDatatypes -> RegisterOrPair.A
in WordDatatypes -> RegisterOrPair.AY
DataType.FLOAT -> RegisterOrPair.FAC1
else -> throw AssemblyError("weird returntype")
}
}
}
@ -2815,114 +2805,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, listOf(valueDt))
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): unary function calls taking a single param and producing a value.
// directly assign their argument from the previous call's returnvalue.
segments.dropLast(1).forEach {
it as IFunctionCall
valueDt = translateUnaryFunctionCallWithArgSource(it.target, valueSource, false, subroutine)
val resultReg = returnRegisterOfFunction(it.target, listOf(valueDt))
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
}
// the last segment: unary function call taking a single param and optionally producing a result value.
val lastCall = segments.last() as IFunctionCall
if(isStatement) {
translateUnaryFunctionCallWithArgSource(lastCall.target, valueSource, true, subroutine)
} else {
valueDt = translateUnaryFunctionCallWithArgSource(lastCall.target, 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 translateUnaryFunctionCallWithArgSource(target: IdentifierReference, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
when(val targetStmt = target.targetStatement(program)!!) {
is BuiltinFunctionPlaceholder -> {
return if(isStatement) {
translateBuiltinFunctionCallStatement(targetStmt.name, singleArg, scope)
DataType.UNDEFINED
} else {
translateBuiltinFunctionCallExpression(targetStmt.name, singleArg, scope)
}
}
is Subroutine -> {
val argDt = targetStmt.parameters.single().type
if(targetStmt.isAsmSubroutine) {
// argument via registers
val argRegister = targetStmt.asmParameterRegisters.single().registerOrPair!!
val assignArgument = AsmAssignment(
singleArg,
AsmAssignTarget.fromRegisters(argRegister, argDt in SignedDatatypes, scope, program, this),
false, program.memsizer, target.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(
singleArg,
AsmAssignTarget(TargetStorageKind.REGISTER, program, this, argDt, scope, register = paramReg),
false, program.memsizer, target.position
)
} else {
// arg goes via parameter variable
val argVarName = asmVariableName(targetStmt.scopedName + targetStmt.parameters.single().name)
AsmAssignment(
singleArg,
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, argDt, scope, argVarName),
false, program.memsizer, target.position
)
}
translateNormalAssignment(assignArgument)
}
if(targetStmt.shouldSaveX())
saveRegisterLocal(CpuRegister.X, scope)
out(" jsr ${asmSymbolName(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
@ -3028,6 +2910,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 {

View File

@ -4,7 +4,6 @@ import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import prog8.code.core.*
import prog8.code.core.SourceCode
import java.io.File
import java.nio.file.Path
import kotlin.io.path.Path

View File

@ -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,43 +28,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
}
internal fun translateUnaryFunctioncall(name: String, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
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, argExpressions, program).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
@ -81,19 +42,9 @@ 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)
"min", "max" -> funcMinMax(fcall, func, resultToStack, resultRegister, sscope)
"sum" -> funcSum(fcall, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sin8", "sin8u", "sin16", "sin16u",
"sinr8", "sinr8u", "sinr16", "sinr16u",
"cos8", "cos8u", "cos16", "cos16u",
"cosr8", "cosr8u", "cosr16", "cosr16u" -> funcSinCosInt(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"sin", "cos", "tan", "atan",
"ln", "log2", "sqrt", "rad",
"deg", "round", "floor", "ceil",
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
"boolean" -> funcBoolean(fcall, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall)
@ -129,7 +80,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"cmp" -> funcCmp(fcall)
"callfar" -> funcCallFar(fcall)
"callrom" -> funcCallRom(fcall)
"syscall", "syscall1", "syscall2", "syscall3" -> throw AssemblyError("6502 assembly target doesn't use syscall function interface")
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
}
}
@ -391,23 +341,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
}
private fun funcSinCosInt(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_${func.name}_stack")
else
when(func.name) {
"sin8", "sin8u", "sinr8", "sinr8u", "cos8", "cos8u", "cosr8", "cosr8u" -> {
asmgen.out(" jsr prog8_lib.func_${func.name}_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
}
"sin16", "sin16u", "sinr16", "sinr16u", "cos16", "cos16u", "cosr16", "cosr16u" -> {
asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
}
}
private fun funcReverse(fcall: IFunctionCall) {
val variable = fcall.args.single()
if (variable is IdentifierReference) {
@ -539,6 +472,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)
@ -547,6 +481,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("""
@ -640,6 +575,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)
@ -648,6 +584,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("""
@ -682,18 +619,14 @@ 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)
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
}
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr floats.func_${func.name}_stack")
else {
asmgen.out(" jsr floats.func_${func.name}_fac1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
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)
}
private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
@ -733,541 +666,60 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
}
} else {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A | ldy #0")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A | ldy #0")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A | ldy #0")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
}
}
private fun funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program)
if(resultToStack) {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_stack")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_stack")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.ARRAY_UB, DataType.STR -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
}
DataType.ARRAY_B -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
}
DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
}
else -> throw AssemblyError("weird type $dt")
}
}
}
private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program)
if(resultToStack) {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_stack")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_stack")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_stack")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_sum_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_sum_f_stack")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt.getOr(DataType.UNDEFINED)) {
DataType.ARRAY_UB, DataType.STR -> {
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_B -> {
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_sum_f_fac1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
}
else -> throw AssemblyError("weird type $dt")
}
}
}
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)
if(resultToStack) {
when (dt) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_stack")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w_stack")
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f_stack")
DataType.UBYTE -> asmgen.out(" ldy #0")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
DataType.UWORD -> {}
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
else -> throw AssemblyError("weird type")
}
} else {
when (dt) {
in ByteDatatypes -> {
asmgen.out(" jsr prog8_lib.abs_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
}
in WordDatatypes -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.FLOAT -> {
asmgen.out(" jsr floats.abs_f_fac1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
}
DataType.UBYTE -> asmgen.out(" ldy #0")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY")
DataType.UWORD -> {}
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
else -> throw AssemblyError("weird type")
}
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
}
}
private fun funcBoolean(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
}
in WordDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
}
DataType.FLOAT -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN")
}
else -> throw AssemblyError("weird type")
}
if(resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
else if(resultRegister!=null)
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, scope, program, asmgen), CpuRegister.A)
}
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) {
"rnd" -> {
@ -1432,12 +884,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)
}
@ -1596,8 +1048,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
private fun outputAddressAndLenghtOfArray(arg: Expression) {
// address in P8ZP_SCRATCH_W1, number of elements in A
arg as IdentifierReference
val arrayVar = arg.targetVarDecl(program)!!
if(!arrayVar.isArray)
throw AssemblyError("length of non-array requested")
val size = arrayVar.arraysize!!.constIndex()!!
val identifierName = asmgen.asmVariableName(arg)
val size = arg.targetVarDecl(program)!!.arraysize!!.constIndex()!!
asmgen.out("""
lda #<$identifierName
ldy #>$identifierName

View File

@ -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")
@ -692,6 +690,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)

View File

@ -3,10 +3,7 @@ package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignSource
@ -111,10 +108,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 {

View File

@ -1,7 +1,6 @@
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.antlr.escape
import prog8.ast.statements.*
import prog8.code.*
import prog8.code.core.*
@ -128,9 +127,27 @@ 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 | rts")
asmgen.out(" jsr main.start | lda #4 | sta $01")
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
}
"c64" -> {
asmgen.out(" jsr main.start | lda #31 | sta $01")
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
}
"c128" -> {
asmgen.out(" jsr main.start")
// TODO c128: how to bank basic+kernal back in?
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
}
"c64" -> asmgen.out(" jsr main.start | lda #31 | sta $01 | rts")
else -> asmgen.jmp("main.start")
}
}
@ -572,7 +589,7 @@ internal class ProgramAndVarsGen(
}
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
asmgen.out("$varname\t; $encoding:\"${escape(value).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))

View File

@ -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> {

View File

@ -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)

View File

@ -2,6 +2,7 @@ package prog8.codegen.cpu6502.assignment
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.getTempRegisterName
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen
@ -72,6 +73,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
@ -185,15 +205,7 @@ internal class AssignmentAsmGen(private val program: Program,
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
// copy the actual string result into the target string variable
asmgen.out("""
pha
lda #<${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1+1
pla
jsr prog8_lib.strcpy""")
throw AssemblyError("stringvalue assignment should have been replaced by a call to strcpy")
}
else -> throw AssemblyError("weird target dt")
}
@ -229,7 +241,7 @@ internal class AssignmentAsmGen(private val program: Program,
asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register)
if(assign.target.register==null) {
// still need to assign the result to the target variable/etc.
val returntype = builtinFunctionReturnType(value.name, value.args, program)
val returntype = builtinFunctionReturnType(value.name)
if(!returntype.isKnown)
throw AssemblyError("unknown dt")
when(returntype.getOr(DataType.UNDEFINED)) {
@ -279,35 +291,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...)
@ -318,6 +303,216 @@ internal class AssignmentAsmGen(private val program: Program,
}
}
private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean {
if(expr.operator in ComparisonOperators) {
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(expr.copy(), assignTrue, assignFalse, assign.position)
ifelse.linkParents(expr)
asmgen.translate(ifelse)
return true
}
}
if(!expr.inferType(program).isInteger)
return false
// optimized code for logical expressions
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this...
if(expr.operator=="and") {
val iDt = expr.left.inferType(program)
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
val tmpReg = getTempRegisterName(iDt).joinToString(".")
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" and $tmpReg")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for and, expected byte $expr @${expr.position}")
}
else if(expr.operator=="or") {
val iDt = expr.left.inferType(program)
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
val tmpReg = getTempRegisterName(iDt).joinToString(".")
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" ora $tmpReg")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for or, expected byte $expr @${expr.position}")
}
else if(expr.operator=="xor") {
val iDt = expr.left.inferType(program)
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
if (dt in ByteDatatypes) {
val tmpReg = getTempRegisterName(iDt).joinToString(".")
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
asmgen.out(" eor $tmpReg")
if(assign.target.datatype in ByteDatatypes)
assignRegisterByte(assign.target, CpuRegister.A)
else {
asmgen.out(" ldy #0")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return true
}
else throw AssemblyError("weird dt for xor, expected byte $expr @${expr.position}")
}
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 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.
@ -336,8 +531,6 @@ internal class AssignmentAsmGen(private val program: Program,
val variable = (containment.iterable as? IdentifierReference)?.targetVarDecl(program)
?: throw AssemblyError("invalid containment iterable type")
if(elementDt istype DataType.FLOAT)
throw AssemblyError("containment check of floats not supported")
if(variable.origin!=VarDeclOrigin.USERCODE) {
when(variable.datatype) {
DataType.STR -> {
@ -396,7 +589,9 @@ internal class AssignmentAsmGen(private val program: Program,
asmgen.out(" jsr prog8_lib.containment_bytearray")
return
}
DataType.ARRAY_F -> throw AssemblyError("containment check of floats not supported")
DataType.ARRAY_F -> {
throw AssemblyError("containment check of floats not supported")
}
DataType.ARRAY_B, DataType.ARRAY_UB -> {
val arrayVal = variable.value as ArrayLiteral
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
@ -568,7 +763,14 @@ internal class AssignmentAsmGen(private val program: Program,
}
if(target.kind==TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
if(valueDt==DataType.FLOAT && target.datatype!=DataType.FLOAT) {
// have to typecast the float number on the fly down to an integer
assignExpressionToRegister(value, RegisterOrPair.FAC1, target.datatype in SignedDatatypes)
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes)
} else {
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
}
return
}
@ -635,7 +837,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 -> {
@ -658,7 +860,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 -> {
@ -688,7 +890,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 -> {
@ -709,7 +911,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 -> {
@ -750,7 +952,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 -> {
@ -784,7 +986,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 -> {
@ -831,7 +1033,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")
@ -859,7 +1061,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")
@ -1265,7 +1467,7 @@ internal class AssignmentAsmGen(private val program: Program,
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> {
if (target.register!! != RegisterOrPair.FAC1)
throw AssemblyError("can't assign Fac1 float to another fac register")
throw AssemblyError("can't assign Fac1 float to another register")
}
TargetStorageKind.STACK -> asmgen.out(" jsr floats.push_fac1")
}
@ -1366,6 +1568,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")
@ -1898,6 +2116,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")
@ -1941,13 +2175,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!!) {
@ -2302,10 +2552,14 @@ internal class AssignmentAsmGen(private val program: Program,
}
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
translateNormalAssignment(assign)
if(expr.inferType(program) istype DataType.FLOAT && dt!=DataType.FLOAT) {
throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
} else {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
translateNormalAssignment(assign)
}
}
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {

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"
}

View File

@ -172,7 +172,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 +203,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)
@ -445,7 +436,6 @@ class AstToXmlConverter(internal val program: PtProgram,
private fun write(assign: PtAssignment) {
xml.elt("assign")
xml.attr("aug", assign.augmentable.toString())
xml.pos(assign.position)
xml.startChildren()
write(assign.target)
@ -613,7 +603,7 @@ class AstToXmlConverter(internal val program: PtProgram,
}
if(asmSub.clobbers.isNotEmpty()) {
xml.elt("clobbers")
xml.attr("registers", asmSub.clobbers.map {it.name}.joinToString(","))
xml.attr("registers", asmSub.clobbers.joinToString(",") { it.name })
xml.endElt()
}
if(asmSub.retvalRegisters.isNotEmpty()) {

View File

@ -29,7 +29,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"
}

View File

@ -1,11 +1,14 @@
package prog8.codegen.virtual
import prog8.code.core.AssemblyError
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.vm.Instruction
import prog8.vm.Opcode
import prog8.vm.OpcodesWithAddress
import prog8.vm.VmDataType
import java.io.BufferedWriter
import java.nio.file.Path
import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
@ -27,8 +30,10 @@ internal class AssemblyProgram(override val name: String,
}
out.write("------PROGRAM------\n")
out.write("; global var inits\n")
globalInits.forEach { out.writeLine(it) }
if(!options.dontReinitGlobals) {
out.write("; global var inits\n")
globalInits.forEach { out.writeLine(it) }
}
out.write("; actual program code\n")
blocks.asSequence().flatMap { it.lines }.forEach { line->out.writeLine(line) }
@ -43,6 +48,21 @@ internal class AssemblyProgram(override val name: String,
write(line.ins.toString() + "\n")
}
is VmCodeLabel -> write("_" + line.name.joinToString(".") + ":\n")
is VmCodeInlineAsm -> {
val asm = line.assembly.replace("""\{[a-zA-Z\d_\.]+\}""".toRegex()) { matchResult ->
val name = matchResult.value.substring(1, matchResult.value.length-1).split('.')
allocations.get(name).toString() }
write(asm+"\n")
}
is VmCodeInlineBinary -> {
write("incbin \"${line.file}\"")
if(line.offset!=null)
write(",${line.offset}")
if(line.length!=null)
write(",${line.length}")
write("\n")
}
else -> throw AssemblyError("invalid vm code line")
}
}
@ -57,12 +77,40 @@ internal class VmCodeInstruction(
type: VmDataType?=null,
reg1: Int?=null, // 0-$ffff
reg2: Int?=null, // 0-$ffff
reg3: Int?=null, // 0-$ffff
fpReg1: Int?=null, // 0-$ffff
fpReg2: Int?=null, // 0-$ffff
value: Int?=null, // 0-$ffff
symbol: List<String>?=null // alternative to value
): VmCodeLine() {
val ins = Instruction(opcode, type, reg1, reg2, reg3, value, symbol)
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)
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 -> {}
}
}
}
}
internal class VmCodeLabel(val name: List<String>): VmCodeLine()
internal class VmCodeComment(val comment: String): VmCodeLine()
@ -81,4 +129,10 @@ internal class VmCodeChunk(initial: VmCodeLine? = null) {
operator fun plusAssign(chunk: VmCodeChunk) {
lines.addAll(chunk.lines)
}
}
}
internal class VmCodeInlineAsm(asm: String): VmCodeLine() {
val assembly: String = asm.trimIndent()
}
internal class VmCodeInlineBinary(val file: Path, val offset: UInt?, val length: UInt?): VmCodeLine()

View File

@ -0,0 +1,245 @@
package prog8.codegen.virtual
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.SignedDatatypes
import prog8.vm.Opcode
import prog8.vm.VmDataType
internal class AssignmentGen(private val codeGen: CodeGen, private val expressionEval: ExpressionGen) {
internal fun translate(assignment: PtAssignment): VmCodeChunk {
if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
return if (assignment.isInplaceAssign)
translateInplaceAssign(assignment)
else
translateRegularAssign(assignment)
}
private fun translateInplaceAssign(assignment: PtAssignment): VmCodeChunk {
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
return if(ident!=null) {
val address = codeGen.allocations.get(ident.targetName)
assignSelfInMemory(address, assignment.value, assignment)
} else if(memory != null) {
if(memory.address is PtNumber)
assignSelfInMemory((memory.address as PtNumber).number.toInt(), assignment.value, assignment)
else
fallbackAssign(assignment)
} else if(array!=null) {
// TODO in-place array element assignment?
fallbackAssign(assignment)
} else {
fallbackAssign(assignment)
}
}
private fun assignSelfInMemory(
address: Int,
value: PtExpression,
origAssign: PtAssignment
): VmCodeChunk {
val vmDt = codeGen.vmType(value.type)
val code = VmCodeChunk()
when(value) {
is PtIdentifier -> return code // do nothing, x=x null assignment.
is PtMachineRegister -> return code // do nothing, reg=reg null assignment
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, origAssign)
is PtMemoryByte -> {
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
code // do nothing, mem=mem null assignment.
else {
// read and write a (i/o) memory location to itself.
val tempReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
code
}
}
else -> return fallbackAssign(origAssign)
}
}
private fun fallbackAssign(origAssign: PtAssignment): VmCodeChunk {
if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
return translateRegularAssign(origAssign)
}
private fun inplaceBinexpr(
operator: String,
operand: PtExpression,
vmDt: VmDataType,
signed: Boolean,
address: Int,
origAssign: PtAssignment
): VmCodeChunk {
when(operator) {
"+" -> return expressionEval.operatorPlusInplace(address, vmDt, operand)
"-" -> return expressionEval.operatorMinusInplace(address, vmDt, operand)
"*" -> return expressionEval.operatorMultiplyInplace(address, vmDt, operand)
"/" -> return expressionEval.operatorDivideInplace(address, vmDt, signed, operand)
"|" -> return expressionEval.operatorOrInplace(address, vmDt, operand)
"&" -> return expressionEval.operatorAndInplace(address, vmDt, operand)
"^" -> return expressionEval.operatorXorInplace(address, vmDt, operand)
"<<" -> return expressionEval.operatorShiftLeftInplace(address, vmDt, operand)
">>" -> return expressionEval.operatorShiftRightInplace(address, vmDt, signed, operand)
else -> {}
}
return fallbackAssign(origAssign)
}
private fun inplacePrefix(operator: String, vmDt: VmDataType, address: Int): VmCodeChunk {
val code= VmCodeChunk()
when(operator) {
"+" -> { }
"-" -> {
code += VmCodeInstruction(Opcode.NEGM, vmDt, value = address)
}
"~" -> {
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.XORM, vmDt, reg1=regMask, value = address)
}
"not" -> {
code += VmCodeInstruction(Opcode.NOTM, vmDt, value = address)
}
else -> throw AssemblyError("weird prefix operator")
}
return code
}
private fun translateRegularAssign(assignment: PtAssignment): VmCodeChunk {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
val vmDt = codeGen.vmType(assignment.value.type)
val code = VmCodeChunk()
var resultRegister = -1
var resultFpRegister = -1
val zero = codeGen.isZero(assignment.value)
if(!zero) {
// calculate the assignment value
if (vmDt == VmDataType.FLOAT) {
resultFpRegister = codeGen.vmRegisters.nextFreeFloat()
code += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
} else {
resultRegister = if (assignment.value is PtMachineRegister) {
(assignment.value as PtMachineRegister).register
} else {
val reg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(assignment.value, reg, -1)
reg
}
}
}
if(ident!=null) {
val address = codeGen.allocations.get(ident.targetName)
code += if(zero) {
VmCodeInstruction(Opcode.STOREZM, vmDt, value = address)
} else {
if (vmDt == VmDataType.FLOAT)
VmCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value = address)
else
VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value = address)
}
}
else if(array!=null) {
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) {
variableAddr += fixedIndex*itemsize
code += VmCodeInstruction(Opcode.STOREZM, vmDt, value=variableAddr)
} else {
val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg)
code += VmCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, value=variableAddr)
}
} else {
if(vmDt== VmDataType.FLOAT) {
if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize
code += VmCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value=variableAddr)
} else {
val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg)
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
}
} else {
if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value=variableAddr)
} else {
val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg)
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
}
}
}
}
else if(memory!=null) {
require(vmDt== VmDataType.BYTE)
if(zero) {
if(memory.address is PtNumber) {
code += VmCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += VmCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
}
} else {
if(memory.address is PtNumber) {
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
}
}
}
else
throw AssemblyError("weird assigntarget")
return code
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): VmCodeChunk {
val code = VmCodeChunk()
if(itemsize==1) {
code += expressionEval.translateExpression(array.index, indexReg, -1)
}
else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
code += expressionEval.translateExpression(mult, indexReg, -1)
}
return code
}
}

View File

@ -1,8 +1,9 @@
package prog8.codegen.virtual
import prog8.code.ast.PtBuiltinFunctionCall
import prog8.code.ast.PtNumber
import prog8.code.ast.PtString
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.vm.Opcode
import prog8.vm.Syscall
import prog8.vm.VmDataType
@ -10,117 +11,391 @@ import prog8.vm.VmDataType
internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: ExpressionGen) {
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
return when(call.name) {
"any" -> funcAny(call, resultRegister)
"all" -> funcAll(call, resultRegister)
"abs" -> funcAbs(call, resultRegister)
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call, resultRegister)
"sqrt16" -> funcSqrt16(call, resultRegister)
"pop" -> funcPop(call)
"popw" -> funcPopw(call)
"push" -> funcPush(call)
"pushw" -> funcPushw(call)
"rsave",
"rsavex",
"rrestore",
"rrestorex" -> VmCodeChunk() // vm doesn't have registers to save/restore
"rnd" -> funcRnd(resultRegister)
"rndw" -> funcRndw(resultRegister)
"callfar" -> throw AssemblyError("callfar() is for cx16 target only")
"callrom" -> throw AssemblyError("callrom() is for cx16 target only")
"msb" -> funcMsb(call, resultRegister)
"lsb" -> funcLsb(call, resultRegister)
"boolean" -> funcBoolean(call, resultRegister)
"memory" -> funcMemory(call, resultRegister)
"peek" -> funcPeek(call, resultRegister)
"peekw" -> funcPeekW(call, resultRegister)
"poke" -> funcPoke(call)
"pokew" -> funcPokeW(call)
"pokemon" -> VmCodeChunk()
"mkword" -> funcMkword(call, resultRegister)
"sort" -> funcSort(call)
"reverse" -> funcReverse(call)
"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}")
}
}
private fun funcCmp(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
when(call.name) {
"syscall" -> {
val vExpr = call.args.single() as PtNumber
code += VmCodeInstruction(Opcode.SYSCALL, value=vExpr.number.toInt())
val leftRegister = codeGen.vmRegisters.nextFree()
val rightRegister = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], leftRegister, -1)
code += exprGen.translateExpression(call.args[1], rightRegister, -1)
code += VmCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
return code
}
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val code = VmCodeChunk()
val syscall =
when (array.dt) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ANY_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ANY_WORD
DataType.ARRAY_F -> Syscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type")
}
"syscall1" -> {
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
val callNr = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], 0)
code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value = syscall.ordinal)
if (resultRegister != 0)
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0)
return code
}
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall =
when(array.dt) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ALL_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ALL_WORD
DataType.ARRAY_F -> Syscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type")
}
"syscall2" -> {
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)
while(codeGen.vmRegisters.peekNext()<2) {
codeGen.vmRegisters.nextFree()
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
return code
}
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val sourceDt = call.args.single().type
if(sourceDt!=DataType.UWORD) {
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
when (sourceDt) {
DataType.UBYTE -> {
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
}
val callNr = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], 0)
code += exprGen.translateExpression(call.args[2], 1)
code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1)
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
}
"syscall3" -> {
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 0)
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2)
while(codeGen.vmRegisters.peekNext()<3) {
codeGen.vmRegisters.nextFree()
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)
code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
code += VmCodeLabel(notNegativeLabel)
}
val callNr = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], 0)
code += exprGen.translateExpression(call.args[2], 1)
code += exprGen.translateExpression(call.args[3], 2)
code += VmCodeInstruction(Opcode.SYSCALL, value=callNr)
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 2)
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 1)
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1 = 0)
}
"msb" -> {
code += exprGen.translateExpression(call.args.single(), resultRegister)
code += VmCodeInstruction(Opcode.SWAP, VmDataType.BYTE, reg1 = resultRegister)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
}
"lsb" -> {
code += exprGen.translateExpression(call.args.single(), resultRegister)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
}
"memory" -> {
val name = (call.args[0] as PtString).value
val size = (call.args[1] as PtNumber).number.toUInt()
val align = (call.args[2] as PtNumber).number.toUInt()
val existing = codeGen.allocations.getMemorySlab(name)
val address = if(existing==null)
codeGen.allocations.allocateMemorySlab(name, size, align)
else if(existing.second!=size || existing.third!=align) {
codeGen.errors.err("memory slab '$name' already exists with a different size or alignment", call.position)
return VmCodeChunk()
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)
code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
code += VmCodeLabel(notNegativeLabel)
}
else
existing.first
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt())
}
"rnd" -> {
code += VmCodeInstruction(Opcode.SYSCALL, value= Syscall.RND.ordinal)
if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
}
"peek" -> {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg)
}
"peekw" -> {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg)
}
"mkword" -> {
val msbReg = codeGen.vmRegisters.nextFree()
val lsbReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], msbReg)
code += exprGen.translateExpression(call.args[1], lsbReg)
code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg)
}
else -> {
TODO("builtinfunc ${call.name}")
// code += VmCodeInstruction(Opcode.NOP))
// for (arg in call.args) {
// code += translateExpression(arg, resultRegister)
// code += when(arg.type) {
// in ByteDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister))
// in WordDatatypes -> VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister))
// else -> throw AssemblyError("weird arg dt")
// }
// }
// code += VmCodeInstruction(Opcode.CALL), labelArg = listOf("_prog8_builtin", call.name))
// for (arg in call.args) {
// code += when(arg.type) {
// in ByteDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=resultRegister))
// in WordDatatypes -> VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=resultRegister))
// else -> throw AssemblyError("weird arg dt")
// }
// }
// code += VmCodeInstruction(Opcode.NOP))
else -> throw AssemblyError("weird type")
}
}
return code
}
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg)
return code
}
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg)
return code
}
private fun funcPop(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
val reg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg)
code += assignRegisterTo(call.args.single(), reg)
return code
}
private fun funcPopw(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
val reg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=reg)
code += assignRegisterTo(call.args.single(), reg)
return code
}
private fun funcPush(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg)
return code
}
private fun funcPushw(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg)
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
val sortSyscall =
when(array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> Syscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> Syscall.REVERSE_WORDS
DataType.ARRAY_F -> Syscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("weird type to reverse")
}
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
return code
}
private fun funcSort(call: PtBuiltinFunctionCall): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val sortSyscall =
when(array.dt) {
DataType.ARRAY_UB -> Syscall.SORT_UBYTE
DataType.ARRAY_B -> Syscall.SORT_BYTE
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")
else -> throw IllegalArgumentException("weird type to sort")
}
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
return code
}
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val msbReg = codeGen.vmRegisters.nextFree()
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args[0], msbReg, -1)
code += exprGen.translateExpression(call.args[1], resultRegister, -1)
code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
return code
}
private fun funcPokeW(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.WORD, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += VmCodeInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg)
}
} else {
val valueReg = codeGen.vmRegisters.nextFree()
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg)
}
}
return code
}
private fun funcPoke(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += VmCodeInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg)
}
} else {
val valueReg = codeGen.vmRegisters.nextFree()
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
}
}
return code
}
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
}
return code
}
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
}
return code
}
private fun funcRnd(resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
code += VmCodeInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
return code
}
private fun funcRndw(resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
code += VmCodeInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
return code
}
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val name = (call.args[0] as PtString).value
val size = (call.args[1] as PtNumber).number.toUInt()
val align = (call.args[2] as PtNumber).number.toUInt()
val existing = codeGen.allocations.getMemorySlab(name)
val address = if(existing==null)
codeGen.allocations.allocateMemorySlab(name, size, align)
else if(existing.second!=size || existing.third!=align) {
codeGen.errors.err("memory slab '$name' already exists with a different size or alignment", call.position)
return VmCodeChunk()
}
else
existing.first
val code = VmCodeChunk()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, value=address.toInt())
return code
}
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return code
}
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return code
}
private fun funcBoolean(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val vmDt = codeGen.vmType(call.args[0].type)
when (vmDt) {
VmDataType.FLOAT -> {
val fpValueReg = codeGen.vmRegisters.nextFreeFloat()
val fpSignReg = codeGen.vmRegisters.nextFreeFloat()
code += exprGen.translateExpression(call.args.single(), -1, fpValueReg)
code += VmCodeInstruction(Opcode.SGN, VmDataType.FLOAT, fpReg1 = fpSignReg, fpReg2 = fpValueReg)
code += VmCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1 = resultRegister, fpReg1 = fpSignReg)
}
VmDataType.WORD -> {
val msbReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
}
else -> {
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
}
}
return code
}
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)
code += VmCodeInstruction(opcode, vmDt, reg1=resultRegister)
code += assignRegisterTo(call.args[0], resultRegister)
return code
}
private fun assignRegisterTo(target: PtExpression, register: Int): VmCodeChunk {
val code = VmCodeChunk()
val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position)
assignTarget.children.add(target)
assignment.children.add(assignTarget)
assignment.children.add(PtMachineRegister(register, target.type, target.position))
code += codeGen.translateNode(assignment)
return code
}
}

View File

@ -10,15 +10,25 @@ import kotlin.math.pow
internal class VmRegisterPool {
private var firstFree: Int=3 // registers 0,1,2 are reserved
private var firstFree: Int=3 // integer registers 0,1,2 are reserved
private var firstFreeFloat: Int=0
fun peekNext() = firstFree
fun peekNextFloat() = firstFreeFloat
fun nextFree(): Int {
val result = firstFree
firstFree++
if(firstFree>65535)
throw AssemblyError("out of virtual registers")
throw AssemblyError("out of virtual registers (int)")
return result
}
fun nextFreeFloat(): Int {
val result = firstFreeFloat
firstFreeFloat++
if(firstFreeFloat>65535)
throw AssemblyError("out of virtual registers (fp)")
return result
}
}
@ -33,21 +43,19 @@ class CodeGen(internal val program: PtProgram,
internal val allocations = VariableAllocator(symbolTable, program, errors)
private val expressionEval = ExpressionGen(this)
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
private val assignmentGen = AssignmentGen(this, expressionEval)
internal val vmRegisters = VmRegisterPool()
init {
if(options.dontReinitGlobals)
TODO("support no globals re-init in vm")
}
override fun compileToAssembly(): IAssemblyProgram? {
val vmprog = AssemblyProgram(program.name, allocations)
// collect global variables initializers
program.allBlocks().forEach {
val code = VmCodeChunk()
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += translate(assign) }
vmprog.addGlobalInits(code)
if(!options.dontReinitGlobals) {
// collect global variables initializers
program.allBlocks().forEach {
val code = VmCodeChunk()
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += assignmentGen.translate(assign) }
vmprog.addGlobalInits(code)
}
}
for (block in program.allBlocks()) {
@ -60,7 +68,7 @@ class CodeGen(internal val program: PtProgram,
}
private fun translateNode(node: PtNode): VmCodeChunk {
internal fun translateNode(node: PtNode): VmCodeChunk {
val code = when(node) {
is PtBlock -> translate(node)
is PtSub -> translate(node)
@ -68,21 +76,24 @@ class CodeGen(internal val program: PtProgram,
is PtVariable -> VmCodeChunk() // var should be looked up via symbol table
is PtMemMapped -> VmCodeChunk() // memmapped var should be looked up via symbol table
is PtConstant -> VmCodeChunk() // constants have all been folded into the code
is PtAssignment -> translate(node)
is PtAssignment -> assignmentGen.translate(node)
is PtNodeGroup -> translateGroup(node.children)
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
is PtFunctionCall -> expressionEval.translate(node, 0)
is PtFunctionCall -> expressionEval.translate(node, 0, 0)
is PtNop -> VmCodeChunk()
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)
is PtRepeatLoop -> translate(node)
is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName))
is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Opcode.BREAKPOINT))
is PtConditionalBranch -> translate(node)
is PtInlineAssembly -> VmCodeChunk(VmCodeInlineAsm(node.assembly))
is PtIncludeBinary -> VmCodeChunk(VmCodeInlineBinary(node.file, node.offset, node.length))
is PtAsmSub -> TODO("asmsub not yet supported on virtual machine target ${node.position}")
is PtAddressOf,
is PtContainmentCheck,
is PtMemoryByte,
@ -98,11 +109,7 @@ class CodeGen(internal val program: PtProgram,
is PtSubroutineParameter,
is PtNumber,
is PtArray,
is PtString -> throw AssemblyError("strings should not occur as separate statement node ${node.position}")
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target ${node.position}")
is PtInlineAssembly -> throw AssemblyError("inline assembly not supported on virtual machine target ${node.position}")
is PtIncludeBinary -> throw AssemblyError("inline binary data not supported on virtual machine target ${node.position}")
is PtConditionalBranch -> throw AssemblyError("conditional branches not supported in vm target due to lack of cpu flags ${node.position}")
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
else -> TODO("missing codegen for $node")
}
if(code.lines.isNotEmpty() && node.position.line!=0)
@ -110,6 +117,33 @@ class CodeGen(internal val program: PtProgram,
return code
}
private fun translate(branch: PtConditionalBranch): VmCodeChunk {
val code = VmCodeChunk()
val elseLabel = createLabelName()
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
code += when(branch.condition) {
BranchCondition.CS -> VmCodeInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
BranchCondition.CC -> VmCodeInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
BranchCondition.EQ, BranchCondition.Z -> VmCodeInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
BranchCondition.NE, BranchCondition.NZ -> VmCodeInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
BranchCondition.MI, BranchCondition.NEG -> VmCodeInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
BranchCondition.PL, BranchCondition.POS -> VmCodeInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
BranchCondition.VC,
BranchCondition.VS -> throw AssemblyError("conditional branch ${branch.condition} not supported in vm target due to lack of cpu V flag ${branch.position}")
}
code += translateNode(branch.trueScope)
if(branch.falseScope.children.isNotEmpty()) {
val endLabel = createLabelName()
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
code += VmCodeLabel(elseLabel)
code += translateNode(branch.falseScope)
code += VmCodeLabel(endLabel)
} else {
code += VmCodeLabel(elseLabel)
}
return code
}
private fun translate(whenStmt: PtWhen): VmCodeChunk {
if(whenStmt.choices.children.isEmpty())
return VmCodeChunk()
@ -117,7 +151,7 @@ class CodeGen(internal val program: PtProgram,
val valueReg = vmRegisters.nextFree()
val choiceReg = vmRegisters.nextFree()
val valueDt = vmType(whenStmt.value.type)
code += expressionEval.translateExpression(whenStmt.value, valueReg)
code += expressionEval.translateExpression(whenStmt.value, valueReg, -1)
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
val endLabel = createLabelName()
for (choice in choices) {
@ -128,21 +162,21 @@ class CodeGen(internal val program: PtProgram,
val values = choice.values.children.map {it as PtNumber}
if(values.size==1) {
code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
code += VmCodeInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, symbol = skipLabel)
code += VmCodeInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel)
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
} else {
val matchLabel = createLabelName()
for (value in values) {
code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
code += VmCodeInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, symbol = matchLabel)
code += VmCodeInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
}
code += VmCodeInstruction(Opcode.JUMP, symbol = skipLabel)
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = skipLabel)
code += VmCodeLabel(matchLabel)
code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel)
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
}
code += VmCodeLabel(skipLabel)
}
@ -167,35 +201,46 @@ class CodeGen(internal val program: PtProgram,
val iterableVar = symbolTable.lookup(iterable.targetName) as StStaticVariable
val loopvarAddress = allocations.get(loopvar.scopedName)
val indexReg = vmRegisters.nextFree()
val tmpReg = vmRegisters.nextFree()
val loopLabel = createLabelName()
val endLabel = createLabelName()
if(iterableVar.dt==DataType.STR) {
// iterate over a zero-terminated string
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress)
code += VmCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=tmpReg, reg2=indexReg, value = arrayAddress)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=tmpReg, value = loopvarAddress)
code += translateNode(forLoop.statements)
code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = loopLabel)
code += VmCodeLabel(endLabel)
} else {
// iterate over array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
val lengthBytes = iterableVar.length!! * elementSize
val lengthReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, symbol = endLabel)
code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)
code += translateNode(forLoop.statements)
code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
code += VmCodeLabel(endLabel)
if(lengthBytes<256) {
val lengthReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, value=arrayAddress)
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, value = loopvarAddress)
code += translateNode(forLoop.statements)
code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
code += VmCodeInstruction(Opcode.BNE, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel)
} else if(lengthBytes==256) {
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, value=arrayAddress)
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, value = loopvarAddress)
code += translateNode(forLoop.statements)
code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
code += VmCodeInstruction(Opcode.BNZ, VmDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel)
} else {
throw AssemblyError("iterator length should never exceed 256")
}
}
}
else -> throw AssemblyError("weird for iterable")
@ -215,8 +260,8 @@ class CodeGen(internal val program: PtProgram,
val loopLabel = createLabelName()
val code = VmCodeChunk()
code += expressionEval.translateExpression(iterable.to, endvalueReg)
code += expressionEval.translateExpression(iterable.from, indexReg)
code += expressionEval.translateExpression(iterable.to, endvalueReg, -1)
code += expressionEval.translateExpression(iterable.from, indexReg, -1)
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
code += VmCodeLabel(loopLabel)
code += translateNode(forLoop.statements)
@ -229,41 +274,49 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
}
val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE
code += VmCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, symbol=loopLabel)
code += VmCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
return code
}
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
val iterable = forLoop.iterable as PtRange
val step = iterable.step.number.toInt()
val range = IntProgression.fromClosedRange(
(iterable.from as PtNumber).number.toInt(),
(iterable.to as PtNumber).number.toInt() + step,
step)
if (range.isEmpty() || range.step==0)
throw AssemblyError("empty range or step 0")
val loopLabel = createLabelName()
val loopvarAddress = allocations.get(loopvar.scopedName)
val indexReg = vmRegisters.nextFree()
val endvalueReg = vmRegisters.nextFree()
val loopvarDt = vmType(loopvar.dt)
val iterable = forLoop.iterable as PtRange
val step = iterable.step.number.toInt()
val rangeStart = (iterable.from as PtNumber).number.toInt()
val rangeEndUntyped = (iterable.to as PtNumber).number.toInt() + step
if(step==0)
throw AssemblyError("step 0")
if(step>0 && rangeEndUntyped<rangeStart || step<0 && rangeEndUntyped>rangeStart)
throw AssemblyError("empty range")
val rangeEndWrapped = if(loopvarDt==VmDataType.BYTE) rangeEndUntyped and 255 else rangeEndUntyped and 65535
val code = VmCodeChunk()
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1=endvalueReg, value=range.last)
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=range.first)
val endvalueReg: Int
if(rangeEndWrapped!=0) {
endvalueReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
} else {
endvalueReg = -1 // not used
}
code += VmCodeInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, value=loopvarAddress)
code += VmCodeLabel(loopLabel)
code += translateNode(forLoop.statements)
if(range.step<3) {
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), range.step)
if(step<3) {
code += addConstMem(loopvarDt, loopvarAddress.toUInt(), step)
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
} else {
code += VmCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
code += addConstReg(loopvarDt, indexReg, range.step)
code += addConstReg(loopvarDt, indexReg, step)
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
}
// TODO more optimal loop instruction for loops ending on 0 (BNZ?)
code += VmCodeInstruction(Opcode.BNE, loopvarDt, reg1=indexReg, reg2=endvalueReg, symbol=loopLabel)
code += if(rangeEndWrapped==0) {
VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
} else {
VmCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
}
return code
}
@ -289,11 +342,11 @@ class CodeGen(internal val program: PtProgram,
val valueReg = vmRegisters.nextFree()
if(value>0) {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= value)
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, reg2 = reg, reg3 = valueReg)
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 = reg, reg3 = valueReg)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = valueReg)
}
}
}
@ -324,13 +377,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 = valueReg, reg3 = operandReg)
code += VmCodeInstruction(Opcode.ADD, 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 = valueReg, reg3 = operandReg)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = valueReg, reg2 = operandReg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
}
}
@ -338,113 +391,333 @@ class CodeGen(internal val program: PtProgram,
return code
}
private val powersOfTwo = (0..16).map { 2.0.pow(it.toDouble()).toInt() }
private fun multiplyByConst(dt: VmDataType, reg: Int, factor: UInt): VmCodeChunk {
internal fun multiplyByConstFloat(fpReg: Int, factor: Float): VmCodeChunk {
val code = VmCodeChunk()
val pow2 = powersOfTwo.indexOf(factor.toInt())
if(pow2>=1) {
// just shift bits
code += VmCodeInstruction(Opcode.LSL, dt, reg1=reg, reg2=reg, reg3=pow2)
if(factor==1f)
return code
if(factor==0f) {
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
} else {
when(factor) {
0u -> {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
}
1u -> { /* do nothing */ }
else -> {
val factorReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value=factor.toInt())
code += VmCodeInstruction(Opcode.MUL, dt, reg1=reg, reg2=reg, reg3=factorReg)
}
val factorReg = vmRegisters.nextFreeFloat()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += VmCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
}
return code
}
internal fun multiplyByConstFloatInplace(address: Int, factor: Float): VmCodeChunk {
val code = VmCodeChunk()
if(factor==1f)
return code
if(factor==0f) {
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.FLOAT, value = address)
} else {
val factorReg = vmRegisters.nextFreeFloat()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += VmCodeInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, value = address)
}
return code
}
internal val powersOfTwo = (0..16).map { 2.0.pow(it.toDouble()).toInt() }
internal fun multiplyByConst(dt: VmDataType, reg: Int, factor: Int): VmCodeChunk {
val code = VmCodeChunk()
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1) {
// just shift 1 bit
code += VmCodeInstruction(Opcode.LSL, dt, reg1=reg)
}
else if(pow2>=1) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
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)
}
}
return code
}
internal fun multiplyByConstInplace(dt: VmDataType, address: Int, factor: Int): VmCodeChunk {
val code = VmCodeChunk()
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1) {
// just shift 1 bit
code += VmCodeInstruction(Opcode.LSLM, dt, value = address)
}
else if(pow2>=1) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += VmCodeInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=address)
} else {
if (factor == 0) {
code += VmCodeInstruction(Opcode.STOREZM, dt, value=address)
}
else {
val factorReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value = factor)
code += VmCodeInstruction(Opcode.MULM, dt, reg1=factorReg, value = address)
}
}
return code
}
internal fun divideByConstFloat(fpReg: Int, factor: Float): VmCodeChunk {
val code = VmCodeChunk()
if(factor==1f)
return code
if(factor==0f) {
code += 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)
}
return code
}
internal fun divideByConstFloatInplace(address: Int, factor: Float): VmCodeChunk {
val code = VmCodeChunk()
if(factor==1f)
return code
if(factor==0f) {
val maxvalueReg = vmRegisters.nextFreeFloat()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, value=address)
} else {
val factorReg = vmRegisters.nextFreeFloat()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += VmCodeInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, value=address)
}
return code
}
internal fun divideByConst(dt: VmDataType, reg: Int, factor: Int, signed: Boolean): VmCodeChunk {
val code = VmCodeChunk()
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1) {
// just shift 1 bit
code += if(signed)
VmCodeInstruction(Opcode.ASR, dt, reg1=reg)
else
VmCodeInstruction(Opcode.LSR, dt, reg1=reg)
}
else if(pow2>=1) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += if(signed)
VmCodeInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
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)
else
VmCodeInstruction(Opcode.DIV, dt, reg1=reg, reg2=factorReg)
}
}
return code
}
internal fun divideByConstInplace(dt: VmDataType, address: Int, factor: Int, signed: Boolean): VmCodeChunk {
val code = VmCodeChunk()
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1) {
// just shift 1 bit
code += if(signed)
VmCodeInstruction(Opcode.ASRM, dt, value=address)
else
VmCodeInstruction(Opcode.LSRM, dt, value=address)
}
else if(pow2>=1) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += if(signed)
VmCodeInstruction(Opcode.ASRNM, dt, reg1=pow2reg, value=address)
else
VmCodeInstruction(Opcode.LSRNM, dt, reg1=pow2reg, value=address)
} else {
if (factor == 0) {
val reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address)
}
else {
val factorReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
code += if(signed)
VmCodeInstruction(Opcode.DIVSM, dt, reg1=factorReg, value=address)
else
VmCodeInstruction(Opcode.DIVM, dt, reg1=factorReg, value=address)
}
}
return code
}
private fun translate(ifElse: PtIfElse): VmCodeChunk {
var branch = Opcode.BZ
var condition = ifElse.condition
if(ifElse.condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
val cond = ifElse.condition as? PtBinaryExpression
if((cond?.right as? PtNumber)?.number==0.0) {
if(cond.operator == "==") {
// if X==0 ... so we branch on Not-zero instead.
branch = Opcode.BNZ
condition = cond.left
}
else if(cond.operator == "!=") {
// if X!=0 ... so we keep branching on Zero.
condition = cond.left
}
}
val conditionReg = vmRegisters.nextFree()
val vmDt = vmType(condition.type)
val signed = ifElse.condition.left.type in arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val vmDt = vmType(ifElse.condition.left.type)
val code = VmCodeChunk()
code += expressionEval.translateExpression(condition, conditionReg)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += VmCodeInstruction(branch, vmDt, reg1=conditionReg, symbol = elseLabel)
code += translateNode(ifElse.ifScope)
code += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel)
code += VmCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope)
code += VmCodeLabel(afterIfLabel)
} else {
// only if part
val afterIfLabel = createLabelName()
code += VmCodeInstruction(branch, vmDt, reg1=conditionReg, symbol = afterIfLabel)
code += translateNode(ifElse.ifScope)
code += VmCodeLabel(afterIfLabel)
fun translateNonZeroComparison(): VmCodeChunk {
val elseBranch = when(ifElse.condition.operator) {
"==" -> Opcode.BNE
"!=" -> Opcode.BEQ
"<" -> if(signed) Opcode.BGES else Opcode.BGE
">" -> if(signed) Opcode.BLES else Opcode.BLE
"<=" -> if(signed) Opcode.BGTS else Opcode.BGT
">=" -> if(signed) Opcode.BLTS else Opcode.BLT
else -> throw AssemblyError("invalid comparison operator")
}
val leftReg = vmRegisters.nextFree()
val rightReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
code += expressionEval.translateExpression(ifElse.condition.right, rightReg, -1)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope)
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += VmCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope)
code += VmCodeLabel(afterIfLabel)
} else {
// only if part
val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope)
code += VmCodeLabel(afterIfLabel)
}
return code
}
return code
fun translateZeroComparison(): VmCodeChunk {
fun equalOrNotEqualZero(elseBranch: Opcode): VmCodeChunk {
val leftReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope)
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += VmCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope)
code += VmCodeLabel(afterIfLabel)
} else {
// only if part
val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope)
code += VmCodeLabel(afterIfLabel)
}
return code
}
return when (ifElse.condition.operator) {
"==" -> {
// if X==0 ... so we just branch on left expr is Not-zero.
equalOrNotEqualZero(Opcode.BNZ)
}
"!=" -> {
// if X!=0 ... so we just branch on left expr is Zero.
equalOrNotEqualZero(Opcode.BZ)
}
else -> {
// another comparison against 0, just use regular codegen for this.
translateNonZeroComparison()
}
}
}
return if(constValue(ifElse.condition.right)==0.0)
translateZeroComparison()
else
translateNonZeroComparison()
}
private fun translate(postIncrDecr: PtPostIncrDecr): VmCodeChunk {
val code = VmCodeChunk()
val operation = when(postIncrDecr.operator) {
"++" -> Opcode.INC
"--" -> Opcode.DEC
val operationMem: Opcode
val operationRegister: Opcode
when(postIncrDecr.operator) {
"++" -> {
operationMem = Opcode.INCM
operationRegister = Opcode.INC
}
"--" -> {
operationMem = Opcode.DECM
operationRegister = Opcode.DEC
}
else -> throw AssemblyError("weird operator")
}
val ident = postIncrDecr.target.identifier
val memory = postIncrDecr.target.memory
val array = postIncrDecr.target.array
val vmDt = vmType(postIncrDecr.target.type)
val resultReg = vmRegisters.nextFree()
if(ident!=null) {
val address = allocations.get(ident.targetName)
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address)
code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address)
code += VmCodeInstruction(operationMem, vmDt, value = address)
} else if(memory!=null) {
val addressReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg)
code += VmCodeInstruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg)
code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg)
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
code += VmCodeInstruction(operationMem, vmDt, value = address)
} else {
val incReg = vmRegisters.nextFree()
val addressReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += VmCodeInstruction(Opcode.LOADI, vmDt, reg1 = incReg, reg2 = addressReg)
code += VmCodeInstruction(operationRegister, vmDt, reg1 = incReg)
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1 = incReg, reg2 = addressReg)
}
} else if (array!=null) {
val variable = array.variable.targetName
var variableAddr = allocations.get(variable)
val itemsize = program.memsizer.memorySize(array.type)
val fixedIndex = (array.index as? PtNumber)?.number?.toInt()
val memOp = when(postIncrDecr.operator) {
"++" -> Opcode.INCM
"--" -> Opcode.DECM
else -> throw AssemblyError("weird operator")
}
val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize
code += VmCodeInstruction(memOp, vmDt, value=variableAddr)
code += VmCodeInstruction(operationMem, vmDt, value=variableAddr)
} else {
val incReg = vmRegisters.nextFree()
val indexReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(array.index, indexReg)
code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=resultReg, reg2=indexReg, value=variableAddr)
code += VmCodeInstruction(operation, vmDt, reg1=resultReg)
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1=resultReg, reg2=indexReg, value=variableAddr)
code += expressionEval.translateExpression(array.index, indexReg, -1)
code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=incReg, reg2=indexReg, value=variableAddr)
code += VmCodeInstruction(operationRegister, vmDt, reg1=incReg)
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1=incReg, reg2=indexReg, value=variableAddr)
}
} else
throw AssemblyError("weird assigntarget")
@ -453,24 +726,24 @@ class CodeGen(internal val program: PtProgram,
}
private fun translate(repeat: PtRepeatLoop): VmCodeChunk {
if((repeat.count as? PtNumber)?.number==0.0)
return VmCodeChunk()
if((repeat.count as? PtNumber)?.number==1.0)
return translateGroup(repeat.children)
if((repeat.count as? PtNumber)?.number==256.0) {
// 256 iterations can still be done with just a byte counter if you set it to zero as starting value.
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
when (constIntValue(repeat.count)) {
0 -> return VmCodeChunk()
1 -> return translateGroup(repeat.children)
256 -> {
// 256 iterations can still be done with just a byte counter if you set it to zero as starting value.
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
}
}
val code = VmCodeChunk()
val counterReg = vmRegisters.nextFree()
val vmDt = vmType(repeat.count.type)
code += expressionEval.translateExpression(repeat.count, counterReg)
code += expressionEval.translateExpression(repeat.count, counterReg, -1)
val repeatLabel = createLabelName()
code += VmCodeLabel(repeatLabel)
code += translateNode(repeat.statements)
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg)
code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel)
code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, labelSymbol = repeatLabel)
return code
}
@ -479,9 +752,9 @@ class CodeGen(internal val program: PtProgram,
if(jump.address!=null)
throw AssemblyError("cannot jump to memory location in the vm target")
code += if(jump.generatedLabel!=null)
VmCodeInstruction(Opcode.JUMP, symbol = listOf(jump.generatedLabel!!))
VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf(jump.generatedLabel!!))
else if(jump.identifier!=null)
VmCodeInstruction(Opcode.JUMP, symbol = jump.identifier!!.targetName)
VmCodeInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName)
else
throw AssemblyError("weird jump")
return code
@ -493,62 +766,21 @@ class CodeGen(internal val program: PtProgram,
return code
}
private fun translate(assignment: PtAssignment): VmCodeChunk {
// TODO can in-place assignments (assignment.augmentable = true) be optimized more?
val code = VmCodeChunk()
val resultRegister = vmRegisters.nextFree()
code += expressionEval.translateExpression(assignment.value, resultRegister)
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
val vmDt = vmType(assignment.value.type)
if(ident!=null) {
val address = allocations.get(ident.targetName)
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address)
}
else if(array!=null) {
val variable = array.variable.targetName
var variableAddr = allocations.get(variable)
val itemsize = program.memsizer.memorySize(array.type)
val fixedIndex = (array.index as? PtNumber)?.number?.toInt()
val vmDtArrayIdx = vmType(array.type)
if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize
code += VmCodeInstruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr)
} else {
val indexReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(array.index, indexReg)
code += VmCodeInstruction(Opcode.STOREX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr)
}
}
else if(memory!=null) {
if(memory.address is PtNumber) {
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
} else {
val addressRegister = vmRegisters.nextFree()
code += expressionEval.translateExpression(assignment.value, addressRegister)
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister)
}
}
else
throw AssemblyError("weird assigntarget")
return code
}
private fun translate(ret: PtReturn): VmCodeChunk {
val code = VmCodeChunk()
val value = ret.value
if(value!=null) {
// Call Convention: return value is always returned in r0
code += expressionEval.translateExpression(value, 0)
// Call Convention: return value is always returned in r0 (or fr0 if float)
code += if(value.type==DataType.FLOAT)
expressionEval.translateExpression(value, -1, 0)
else
expressionEval.translateExpression(value, 0, -1)
}
code += VmCodeInstruction(Opcode.RETURN)
return code
}
private fun translate(sub: PtSub): VmCodeChunk {
// TODO actually inline subroutines marked as inline
val code = VmCodeChunk()
code += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
code += VmCodeLabel(sub.scopedName)
@ -577,6 +809,7 @@ class CodeGen(internal val program: PtProgram,
DataType.BYTE -> VmDataType.BYTE
DataType.UWORD,
DataType.WORD -> VmDataType.WORD
DataType.FLOAT -> VmDataType.FLOAT
in PassByReferenceDatatypes -> VmDataType.WORD
else -> throw AssemblyError("no vm datatype for $type")
}
@ -590,4 +823,9 @@ class CodeGen(internal val program: PtProgram,
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk =
builtinFuncGen.translate(call, resultRegister)
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
}

View File

@ -3,51 +3,66 @@ package prog8.codegen.virtual
import prog8.code.StStaticVariable
import prog8.code.StSub
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.PassByValueDatatypes
import prog8.code.core.SignedDatatypes
import prog8.code.core.*
import prog8.vm.Opcode
import prog8.vm.VmDataType
internal class ExpressionGen(private val codeGen: CodeGen) {
fun translateExpression(expr: PtExpression, resultRegister: Int): VmCodeChunk {
fun translateExpression(expr: PtExpression, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
require(codeGen.vmRegisters.peekNext() > resultRegister)
val code = VmCodeChunk()
val vmDt = codeGen.vmType(expr.type)
when (expr) {
is PtMachineRegister -> {
if(resultRegister!=expr.register) {
val vmDt = codeGen.vmType(expr.type)
code += VmCodeInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=expr.register)
}
}
is PtNumber -> {
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
val vmDt = codeGen.vmType(expr.type)
code += if(vmDt==VmDataType.FLOAT)
VmCodeInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
else
VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
}
is PtIdentifier -> {
val vmDt = codeGen.vmType(expr.type)
val mem = codeGen.allocations.get(expr.targetName)
code += if(expr.type in PassByValueDatatypes) {
VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem)
code += if (expr.type in PassByValueDatatypes) {
if(vmDt==VmDataType.FLOAT)
VmCodeInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, value = mem)
else
VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, value = mem)
} else {
// for strings and arrays etc., load the *address* of the value instead
VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)
VmCodeInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, value = mem)
}
}
is PtAddressOf -> {
val vmDt = codeGen.vmType(expr.type)
val mem = codeGen.allocations.get(expr.identifier.targetName)
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)
}
is PtMemoryByte -> {
val addressRegister = codeGen.vmRegisters.nextFree()
val addressExprCode = translateExpression(expr.address, addressRegister)
code += addressExprCode
if(expr.address is PtNumber) {
val address = (expr.address as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1=resultRegister, value = address)
} else {
val addressRegister = codeGen.vmRegisters.nextFree()
code += translateExpression(expr.address, addressRegister, -1)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1=resultRegister, reg2=addressRegister)
}
}
is PtTypeCast -> code += translate(expr, resultRegister)
is PtTypeCast -> code += translate(expr, resultRegister, resultFpRegister)
is PtPrefix -> code += translate(expr, resultRegister)
is PtArrayIndexer -> code += translate(expr, resultRegister)
is PtBinaryExpression -> code += translate(expr, resultRegister)
is PtArrayIndexer -> code += translate(expr, resultRegister, resultFpRegister)
is PtBinaryExpression -> code += translate(expr, resultRegister, resultFpRegister)
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
is PtFunctionCall -> code += translate(expr, resultRegister)
is PtContainmentCheck -> code += translate(expr, resultRegister)
is PtPipe -> code += translate(expr, resultRegister)
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
is PtRange,
is PtArray,
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
@ -56,61 +71,77 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
return code
}
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
TODO("Not yet implemented: pipe expression")
}
private fun translate(check: PtContainmentCheck, resultRegister: Int): VmCodeChunk {
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
code += translateExpression(check.element, resultRegister) // load the element to check in resultRegister
code += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.targetName) as StStaticVariable
when(iterable.dt) {
DataType.STR -> {
val call = PtFunctionCall(listOf("prog8_lib", "string_contains"), false, DataType.UBYTE, check.position)
call.children.add(check.element)
call.children.add(check.iterable)
code += translate(call, resultRegister)
code += translate(call, resultRegister, resultFpRegister)
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
val call = PtFunctionCall(listOf("prog8_lib", "bytearray_contains"), false, DataType.UBYTE, check.position)
call.children.add(check.element)
call.children.add(check.iterable)
call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position))
code += translate(call, resultRegister)
code += translate(call, resultRegister, resultFpRegister)
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
val call = PtFunctionCall(listOf("prog8_lib", "wordarray_contains"), false, DataType.UBYTE, check.position)
call.children.add(check.element)
call.children.add(check.iterable)
call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position))
code += translate(call, resultRegister)
code += translate(call, resultRegister, resultFpRegister)
}
DataType.ARRAY_F -> TODO("containment check in float-array")
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.targetName}")
}
return code
}
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int): VmCodeChunk {
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val vmDt = codeGen.vmType(arrayIx.type)
val code = VmCodeChunk()
val idxReg = codeGen.vmRegisters.nextFree()
// TODO: optimized code when the index is a constant value
code += translateExpression(arrayIx.index, idxReg)
if(eltSize>1) {
val factorReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize)
code += VmCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=idxReg, reg3=factorReg)
}
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation)
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
if(vmDt==VmDataType.FLOAT)
code += VmCodeInstruction(Opcode.LOADM, VmDataType.FLOAT, fpReg1=resultFpRegister, value=arrayLocation+memOffset)
else
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=arrayLocation+memOffset)
} else {
code += translateExpression(arrayIx.index, idxReg, -1)
if(eltSize>1)
code += codeGen.multiplyByConst(VmDataType.BYTE, idxReg, eltSize)
if(vmDt==VmDataType.FLOAT)
code += VmCodeInstruction(Opcode.LOADX, VmDataType.FLOAT, fpReg1 = resultFpRegister, reg1=idxReg, value = arrayLocation)
else
code += VmCodeInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation)
}
return code
}
private fun translate(expr: PtPrefix, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
code += translateExpression(expr.value, resultRegister)
code += translateExpression(expr.value, resultRegister, -1)
val vmDt = codeGen.vmType(expr.type)
when(expr.operator) {
"+" -> { }
@ -121,43 +152,40 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
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=resultRegister, reg3=regMask)
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=regMask)
}
"not" -> {
val label = codeGen.createLabelName()
code += VmCodeInstruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label)
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1)
code += VmCodeLabel(label)
val regMask = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value=1)
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)
code += VmCodeInstruction(Opcode.NOT, vmDt, reg1=resultRegister)
}
else -> throw AssemblyError("weird prefix operator")
}
return code
}
private fun translate(cast: PtTypeCast, resultRegister: Int): VmCodeChunk {
private fun translate(cast: PtTypeCast, predefinedResultRegister: Int, predefinedResultFpRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
if(cast.type==cast.value.type)
return code
code += translateExpression(cast.value, resultRegister)
val actualResultFpReg = if(predefinedResultFpRegister>=0) predefinedResultFpRegister else codeGen.vmRegisters.nextFreeFloat()
val actualResultReg = if(predefinedResultRegister>=0) predefinedResultRegister else codeGen.vmRegisters.nextFree()
if(cast.value.type==DataType.FLOAT) {
// a cast from float to integer, so evaluate the value into a float register first
code += translateExpression(cast.value, -1, actualResultFpReg)
}
else
code += translateExpression(cast.value, actualResultReg, -1)
when(cast.type) {
DataType.UBYTE -> {
when(cast.value.type) {
DataType.BYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
DataType.FLOAT -> {
TODO("float -> ubyte") // float not yet supported
}
DataType.FLOAT -> code += VmCodeInstruction(Opcode.FTOUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
else -> throw AssemblyError("weird cast value type")
}
}
DataType.BYTE -> {
when(cast.value.type) {
DataType.UBYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
DataType.FLOAT -> {
TODO("float -> byte") // float not yet supported
}
DataType.FLOAT -> code += VmCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
else -> throw AssemblyError("weird cast value type")
}
}
@ -165,15 +193,15 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> uword: sign extend
code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister)
code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.UBYTE -> {
// ubyte -> uword: sign extend
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister)
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.WORD -> { }
DataType.FLOAT -> {
TODO("float -> uword") // float not yet supported
code += VmCodeInstruction(Opcode.FTOUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
else -> throw AssemblyError("weird cast value type")
}
@ -182,123 +210,606 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> word: sign extend
code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister)
code += VmCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.UBYTE -> {
// byte -> word: sign extend
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister)
code += VmCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.UWORD -> { }
DataType.FLOAT -> {
TODO("float -> word") // float not yet supported
code += VmCodeInstruction(Opcode.FTOSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.FLOAT -> {
TODO("floating point not yet supported")
// when(cast.value.type) {
// DataType.BYTE -> {
// }
// DataType.UBYTE -> {
// }
// DataType.WORD -> {
// }
// DataType.UWORD -> {
// }
// else -> throw AssemblyError("weird cast value type")
// }
code += when(cast.value.type) {
DataType.UBYTE -> {
VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.BYTE -> {
VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.UWORD -> {
VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.WORD -> {
VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
else -> throw AssemblyError("weird cast value type")
}
}
else -> throw AssemblyError("weird cast type")
}
return code
}
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val leftResultReg = codeGen.vmRegisters.nextFree()
val rightResultReg = codeGen.vmRegisters.nextFree()
// TODO: optimized codegen when left or right operand is known 0 or 1 or whatever. But only if this would result in a different opcode such as ADD 1 -> INC, MUL 1 -> NOP
// actually optimizing the code should not be done here but in a tailored code optimizer step.
val leftCode = translateExpression(binExpr.left, leftResultReg)
val rightCode = translateExpression(binExpr.right, rightResultReg)
code += leftCode
code += rightCode
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val vmDt = codeGen.vmType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
when(binExpr.operator) {
"+" -> {
code += VmCodeInstruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"-" -> {
code += VmCodeInstruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"*" -> {
code += VmCodeInstruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"/" -> {
code += VmCodeInstruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"%" -> {
code += VmCodeInstruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"|", "or" -> {
code += VmCodeInstruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"&", "and" -> {
code += VmCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"^", "xor" -> {
code += VmCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"<<" -> {
code += VmCodeInstruction(Opcode.LSL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
">>" -> {
val opc = if(signed) Opcode.ASR else Opcode.LSR
code += VmCodeInstruction(opc, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"==" -> {
code += VmCodeInstruction(Opcode.SEQ, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"!=" -> {
code += VmCodeInstruction(Opcode.SNE, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"<" -> {
val ins = if(signed) Opcode.SLTS else Opcode.SLT
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
">" -> {
val ins = if(signed) Opcode.SGTS else Opcode.SGT
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
"<=" -> {
val ins = if(signed) Opcode.SLES else Opcode.SLE
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
">=" -> {
val ins = if(signed) Opcode.SGES else Opcode.SGE
code += VmCodeInstruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg)
}
return when(binExpr.operator) {
"+" -> operatorPlus(binExpr, vmDt, resultRegister, resultFpRegister)
"-" -> operatorMinus(binExpr, vmDt, resultRegister, resultFpRegister)
"*" -> 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)
"<<" -> operatorShiftLeft(binExpr, vmDt, resultRegister)
">>" -> operatorShiftRight(binExpr, vmDt, resultRegister, signed)
"==" -> operatorEquals(binExpr, vmDt, resultRegister, false)
"!=" -> operatorEquals(binExpr, vmDt, resultRegister, true)
"<" -> operatorLessThan(binExpr, vmDt, resultRegister, signed, false)
">" -> operatorGreaterThan(binExpr, vmDt, resultRegister, signed, false)
"<=" -> operatorLessThan(binExpr, vmDt, resultRegister, signed, true)
">=" -> operatorGreaterThan(binExpr, vmDt, resultRegister, signed, true)
else -> throw AssemblyError("weird operator ${binExpr.operator}")
}
}
private fun operatorGreaterThan(
binExpr: PtBinaryExpression,
vmDt: VmDataType,
resultRegister: Int,
signed: Boolean,
greaterEquals: Boolean
): VmCodeChunk {
val code = VmCodeChunk()
if(vmDt==VmDataType.FLOAT) {
val leftFpReg = codeGen.vmRegisters.nextFreeFloat()
val rightFpReg = codeGen.vmRegisters.nextFreeFloat()
val zeroRegister = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, -1, leftFpReg)
code += translateExpression(binExpr.right, -1, rightFpReg)
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
val ins = if (signed) {
if (greaterEquals) Opcode.SGES else Opcode.SGTS
} else {
if (greaterEquals) Opcode.SGE else Opcode.SGT
}
code += VmCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
comparisonCall.children.add(binExpr.left)
comparisonCall.children.add(binExpr.right)
code += translate(comparisonCall, resultRegister, -1)
val zeroRegister = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += if(greaterEquals)
VmCodeInstruction(Opcode.SGES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
else
VmCodeInstruction(Opcode.SGTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
val ins = if (signed) {
if (greaterEquals) Opcode.SGES else Opcode.SGTS
} else {
if (greaterEquals) Opcode.SGE else Opcode.SGT
}
code += VmCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
}
fun translate(fcall: PtFunctionCall, resultRegister: Int): VmCodeChunk {
private fun operatorLessThan(
binExpr: PtBinaryExpression,
vmDt: VmDataType,
resultRegister: Int,
signed: Boolean,
lessEquals: Boolean
): VmCodeChunk {
val code = VmCodeChunk()
if(vmDt==VmDataType.FLOAT) {
val leftFpReg = codeGen.vmRegisters.nextFreeFloat()
val rightFpReg = codeGen.vmRegisters.nextFreeFloat()
val zeroRegister = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, -1, leftFpReg)
code += translateExpression(binExpr.right, -1, rightFpReg)
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
val ins = if (signed) {
if (lessEquals) Opcode.SLES else Opcode.SLTS
} else {
if (lessEquals) Opcode.SLE else Opcode.SLT
}
code += VmCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
comparisonCall.children.add(binExpr.left)
comparisonCall.children.add(binExpr.right)
code += translate(comparisonCall, resultRegister, -1)
val zeroRegister = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += if(lessEquals)
VmCodeInstruction(Opcode.SLES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
else
VmCodeInstruction(Opcode.SLTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
val ins = if (signed) {
if (lessEquals) Opcode.SLES else Opcode.SLTS
} else {
if (lessEquals) Opcode.SLE else Opcode.SLT
}
code += VmCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
}
private fun operatorEquals(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, notEquals: Boolean): VmCodeChunk {
val code = VmCodeChunk()
if(vmDt==VmDataType.FLOAT) {
val leftFpReg = codeGen.vmRegisters.nextFreeFloat()
val rightFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, leftFpReg)
code += translateExpression(binExpr.right, -1, rightFpReg)
if (notEquals) {
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
} else {
val label = codeGen.createLabelName()
val valueReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1)
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, labelSymbol = label)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0)
code += VmCodeLabel(label)
}
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
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)
}
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
code += VmCodeInstruction(opcode, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
}
private fun operatorShiftRight(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, signed: Boolean): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isOne(binExpr.right)) {
code += translateExpression(binExpr.left, resultRegister, -1)
val opc = if (signed) Opcode.ASR else Opcode.LSR
code += VmCodeInstruction(opc, vmDt, reg1 = resultRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
code += VmCodeInstruction(opc, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
internal fun operatorShiftRightInplace(address: Int, vmDt: VmDataType, signed: Boolean, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isOne(operand)) {
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
code += VmCodeInstruction(opc, vmDt, value=address)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
code += VmCodeInstruction(opc, vmDt, reg1 = operandReg, value=address)
}
return code
}
private fun operatorShiftLeft(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isOne(binExpr.right)){
code += translateExpression(binExpr.left, resultRegister, -1)
code += VmCodeInstruction(Opcode.LSL, vmDt, reg1=resultRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += VmCodeInstruction(Opcode.LSLN, vmDt, reg1=resultRegister, rightResultReg)
}
return code
}
internal fun operatorShiftLeftInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isOne(operand)){
code += VmCodeInstruction(Opcode.LSLM, vmDt, value=address)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, value=address)
}
return code
}
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)
return code
}
internal fun operatorXorInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.XORM, vmDt, reg1=operandReg, value = address)
return code
}
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)
return code
}
internal fun operatorAndInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.ANDM, vmDt, reg1=operandReg, value=address)
return code
}
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)
return code
}
internal fun operatorOrInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.ORM, vmDt, reg1=operandReg, value = address)
return code
}
private fun operatorModulo(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
if(vmDt==VmDataType.FLOAT)
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)
return code
}
private fun operatorDivide(binExpr: PtBinaryExpression,
vmDt: VmDataType,
resultRegister: Int,
resultFpRegister: Int,
signed: Boolean): VmCodeChunk {
val code = VmCodeChunk()
val constFactorRight = binExpr.right as? PtNumber
if(vmDt==VmDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
val factor = constFactorRight.number.toFloat()
code += codeGen.divideByConstFloat(resultFpRegister, factor)
} else {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += if(signed)
VmCodeInstruction(Opcode.DIVS, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
else
VmCodeInstruction(Opcode.DIV, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
code += translateExpression(binExpr.left, resultRegister, -1)
val factor = constFactorRight.number.toInt()
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)
}
}
return code
}
internal fun operatorDivideInplace(address: Int, vmDt: VmDataType, signed: Boolean, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
val constFactorRight = operand as? PtNumber
if(vmDt==VmDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toFloat()
code += codeGen.divideByConstFloatInplace(address, factor)
} else {
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(operand, -1, operandFpReg)
code += if(signed)
VmCodeInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, value=address)
else
VmCodeInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, value=address)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toInt()
code += codeGen.divideByConstInplace(vmDt, address, factor, signed)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(signed)
VmCodeInstruction(Opcode.DIVSM, vmDt, reg1=operandReg, value = address)
else
VmCodeInstruction(Opcode.DIVM, vmDt, reg1=operandReg, value = address)
}
}
return code
}
private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val constFactorLeft = binExpr.left as? PtNumber
val constFactorRight = binExpr.right as? PtNumber
if(vmDt==VmDataType.FLOAT) {
if(constFactorLeft!=null) {
code += translateExpression(binExpr.right, -1, resultFpRegister)
val factor = constFactorLeft.number.toFloat()
code += codeGen.multiplyByConstFloat(resultFpRegister, factor)
} else if(constFactorRight!=null) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
val factor = constFactorRight.number.toFloat()
code += codeGen.multiplyByConstFloat(resultFpRegister, factor)
} else {
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)
}
} else {
if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
code += translateExpression(binExpr.right, resultRegister, -1)
val factor = constFactorLeft.number.toInt()
code += codeGen.multiplyByConst(vmDt, resultRegister, factor)
} else if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
code += translateExpression(binExpr.left, resultRegister, -1)
val factor = constFactorRight.number.toInt()
code += codeGen.multiplyByConst(vmDt, resultRegister, factor)
} else {
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)
}
}
return code
}
internal fun operatorMultiplyInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
val constFactorRight = operand as? PtNumber
if(vmDt==VmDataType.FLOAT) {
if(constFactorRight!=null) {
val factor = constFactorRight.number.toFloat()
code += codeGen.multiplyByConstFloatInplace(address, factor)
} else {
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(operand, -1, operandFpReg)
code += VmCodeInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, value = address)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
val factor = constFactorRight.number.toInt()
code += codeGen.multiplyByConstInplace(vmDt, address, factor)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.MULM, vmDt, reg1=operandReg, value = address)
}
}
return code
}
private fun operatorMinus(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
if(vmDt==VmDataType.FLOAT) {
if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
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)
}
} else {
if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, resultRegister, -1)
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)
}
}
return code
}
internal fun operatorMinusInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
if(vmDt==VmDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
code += VmCodeInstruction(Opcode.DECM, vmDt, value=address)
}
else {
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(operand, -1, operandFpReg)
code += VmCodeInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, value=address)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
code += VmCodeInstruction(Opcode.DECM, vmDt, value=address)
}
else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.SUBM, vmDt, reg1=operandReg, value = address)
}
}
return code
}
private fun operatorPlus(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
if(vmDt==VmDataType.FLOAT) {
if((binExpr.left as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.right, -1, resultFpRegister)
code += VmCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
}
else if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
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)
}
} else {
if((binExpr.left as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.right, resultRegister, -1)
code += VmCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
}
else if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, resultRegister, -1)
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)
}
}
return code
}
internal fun operatorPlusInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
if(vmDt==VmDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
code += VmCodeInstruction(Opcode.INCM, vmDt, value = address)
}
else {
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(operand, -1, operandFpReg)
code += VmCodeInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, value=address)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
code += VmCodeInstruction(Opcode.INCM, vmDt, value = address)
}
else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.ADDM, vmDt, reg1=operandReg, value=address)
}
}
return code
}
fun translate(fcall: PtFunctionCall, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
val subroutine = codeGen.symbolTable.flat.getValue(fcall.functionName) as StSub
val code = VmCodeChunk()
for ((arg, parameter) in fcall.args.zip(subroutine.parameters)) {
val argReg = codeGen.vmRegisters.nextFree()
code += translateExpression(arg, argReg)
val vmDt = codeGen.vmType(parameter.type)
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem)
val paramDt = codeGen.vmType(parameter.type)
if(codeGen.isZero(arg)) {
if (paramDt == VmDataType.FLOAT) {
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
code += VmCodeInstruction(Opcode.STOREZM, paramDt, value = mem)
} else {
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
code += VmCodeInstruction(Opcode.STOREZM, paramDt, value = mem)
}
} else {
if (paramDt == VmDataType.FLOAT) {
val argFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(arg, -1, argFpReg)
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
code += VmCodeInstruction(Opcode.STOREM, paramDt, fpReg1 = argFpReg, value = mem)
} else {
val argReg = codeGen.vmRegisters.nextFree()
code += translateExpression(arg, argReg, -1)
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
code += VmCodeInstruction(Opcode.STOREM, paramDt, reg1 = argReg, value = mem)
}
}
}
code += VmCodeInstruction(Opcode.CALL, symbol=fcall.functionName)
if(!fcall.void && resultRegister!=0) {
// Call convention: result value is in r0, so put it in the required register instead.
code += VmCodeInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0)
code += VmCodeInstruction(Opcode.CALL, labelSymbol=fcall.functionName)
if(fcall.type==DataType.FLOAT) {
if (!fcall.void && resultFpRegister != 0) {
// Call convention: result value is in fr0, so put it in the required register instead.
code += VmCodeInstruction(Opcode.LOADR, VmDataType.FLOAT, fpReg1 = resultFpRegister, fpReg2 = 0)
}
} else {
if (!fcall.void && resultRegister != 0) {
// Call convention: result value is in r0, so put it in the required register instead.
code += VmCodeInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1 = resultRegister, reg2 = 0)
}
}
return code
}

View File

@ -67,7 +67,7 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg
}
else -> throw InternalCompilerException("weird dt")
}
mm.add(Pair(variable.scopedName, "${location.toHex()} $typeStr $value"))
mm.add(Pair(variable.scopedName, "$location $typeStr $value"))
}
return mm
}

View File

@ -3,7 +3,6 @@ package prog8.optimizer
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.AugmentAssignmentOperators
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.TypecastExpression
@ -13,6 +12,7 @@ import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AugmentAssignmentOperators
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.target.VMTarget
@ -97,7 +97,8 @@ X = BinExpr X = LeftExpr
// we can see if we can unwrap the binary expression by working on a new temporary variable
// (that has the type of the expression), and then finally doing the typecast.
// Once it's outside the typecast, the regular splitting can commence.
val (tempVarName, _) = program.getTempVar(origExpr.inferType(program).getOr(DataType.UNDEFINED))
val tempvarDt = origExpr.inferType(program).getOr(DataType.UNDEFINED)
val (tempVarName, _) = program.getTempVar(tempvarDt)
val assignTempVar = Assignment(
AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position),
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position

View File

@ -6,11 +6,13 @@ import prog8.ast.base.ExpressionError
import prog8.ast.base.FatalAstException
import prog8.ast.base.UndefinedSymbolError
import prog8.ast.expressions.*
import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.ForLoop
import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType
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
@ -107,9 +109,32 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
* (X + c1) - c2 -> X + (c1-c2)
*/
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val modifications = mutableListOf<IAstModification>()
val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program)
val modifications = mutableListOf<IAstModification>()
if(expr.left.inferType(program) istype DataType.STR) {
if(expr.operator=="+" && expr.left is StringLiteral && expr.right is StringLiteral) {
// concatenate two strings.
val leftString = expr.left as StringLiteral
val rightString = expr.right as StringLiteral
val concatenated = if(leftString.encoding==rightString.encoding) {
leftString.value + rightString.value
} else {
program.encoding.decodeString(
program.encoding.encodeString(leftString.value, leftString.encoding) + program.encoding.encodeString(rightString.value, rightString.encoding),
leftString.encoding)
}
val concatStr = StringLiteral(concatenated, leftString.encoding, expr.position)
return listOf(IAstModification.ReplaceNode(expr, concatStr, parent))
}
else if(expr.operator=="*" && rightconst!=null && expr.left is StringLiteral) {
// mutiply a string.
val part = expr.left as StringLiteral
val newStr = StringLiteral(part.value.repeat(rightconst.number.toInt()), part.encoding, expr.position)
return listOf(IAstModification.ReplaceNode(expr, newStr, parent))
}
}
if(expr.operator=="==" && rightconst!=null) {
val leftExpr = expr.left as? BinaryExpression
@ -168,6 +193,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
@ -399,7 +439,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)

View File

@ -1,13 +1,19 @@
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.AssociativeOperators
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.IntegerDatatypes
import prog8.code.core.NumericDatatypes
import kotlin.math.abs
@ -16,7 +22,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()
@ -83,12 +89,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))
}
@ -270,6 +276,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource == listOf("lsb")) {
if(functionCallExpr.args.isEmpty())
return noModifications
val arg = functionCallExpr.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
@ -286,6 +294,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
else if(functionCallExpr.target.nameInSource == listOf("msb")) {
if(functionCallExpr.args.isEmpty())
return noModifications
val arg = functionCallExpr.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
@ -330,30 +340,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
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
@ -645,7 +631,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

View File

@ -54,8 +54,14 @@ fun Program.optimizeStatements(errors: IErrorReporter,
return optimizationCount
}
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
val opti = ExpressionSimplifier(this, errors)
fun Program.inlineSubroutines(): Int {
val inliner = Inliner(this)
inliner.visit(this)
return inliner.applyModifications()
}
fun Program.simplifyExpressions() : Int {
val opti = ExpressionSimplifier(this)
opti.visit(this)
return opti.applyModifications()
}

View File

@ -0,0 +1,241 @@
package prog8.optimizer
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
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 {
private val modifications = mutableListOf<IAstModification>()
init {
visit(program)
modifications.forEach { it.perform() }
modifications.clear()
}
override fun visit(subroutine: Subroutine) {
if(!subroutine.isAsmSubroutine && !subroutine.inline && subroutine.parameters.isEmpty()) {
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine}
if(!containsSubsOrVariables) {
if(subroutine.statements.size==1 || (subroutine.statements.size==2 && isEmptyReturn(subroutine.statements[1]))) {
// subroutine is possible candidate to be inlined
subroutine.inline =
when(val stmt=subroutine.statements[0]) {
is Return -> {
if(stmt.value is NumericLiteral)
true
else if (stmt.value is IdentifierReference) {
makeFullyScoped(stmt.value as IdentifierReference)
true
} else if(stmt.value!! is IFunctionCall && (stmt.value as IFunctionCall).args.size<=1 && (stmt.value as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
when (stmt.value) {
is BuiltinFunctionCall -> {
makeFullyScoped(stmt.value as BuiltinFunctionCall)
true
}
is FunctionCallExpression -> {
makeFullyScoped(stmt.value as FunctionCallExpression)
true
}
else -> false
}
} else
false
}
is Assignment -> {
if(stmt.value.isSimple) {
val targetInline =
if(stmt.target.identifier!=null) {
makeFullyScoped(stmt.target.identifier!!)
true
} else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
true
} else
false
val valueInline =
if(stmt.value is IdentifierReference) {
makeFullyScoped(stmt.value as IdentifierReference)
true
} else if((stmt.value as? DirectMemoryRead)?.addressExpression is NumericLiteral || (stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference) {
if((stmt.value as? DirectMemoryRead)?.addressExpression is IdentifierReference)
makeFullyScoped((stmt.value as? DirectMemoryRead)?.addressExpression as IdentifierReference)
true
} else
false
targetInline || valueInline
} else
false
}
is BuiltinFunctionCallStatement -> {
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
if(inline)
makeFullyScoped(stmt)
inline
}
is FunctionCallStatement -> {
val inline = stmt.args.size<=1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
if(inline)
makeFullyScoped(stmt)
inline
}
is PostIncrDecr -> {
if(stmt.target.identifier!=null) {
makeFullyScoped(stmt.target.identifier!!)
true
}
else if(stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
if(stmt.target.memoryAddress?.addressExpression is IdentifierReference)
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
true
} else
false
}
is Jump, is GoSub -> true
else -> false
}
}
}
}
super.visit(subroutine)
}
private fun makeFullyScoped(identifier: IdentifierReference) {
val scoped = (identifier.targetStatement(program)!! as INamedStatement).scopedName
val scopedIdent = IdentifierReference(scoped, identifier.position)
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
}
private fun makeFullyScoped(call: BuiltinFunctionCallStatement) {
val scopedArgs = makeScopedArgs(call.args)
val scopedCall = BuiltinFunctionCallStatement(call.target.copy(), scopedArgs.toMutableList(), call.position)
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
}
private fun makeFullyScoped(call: FunctionCallStatement) {
val sub = call.target.targetSubroutine(program)!!
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)
val scopedCall = FunctionCallStatement(scopedName, scopedArgs.toMutableList(), call.void, call.position)
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
}
private fun makeFullyScoped(call: BuiltinFunctionCall) {
val sub = call.target.targetSubroutine(program)!!
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
}
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(scopedName, scopedArgs.toMutableList(), call.position)
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
}
private fun makeScopedArgs(args: List<Expression>): List<Expression> {
return args.map {
when (it) {
is NumericLiteral -> it.copy()
is IdentifierReference -> {
val scoped = (it.targetStatement(program)!! as INamedStatement).scopedName
IdentifierReference(scoped, it.position)
}
else -> throw InternalCompilerException("expected only number or identifier arg, otherwise too complex")
}
}
}
}
override fun before(program: Program): Iterable<IAstModification> {
DetermineInlineSubs(program)
return super.before(program)
}
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
val sub = gosub.identifier.targetStatement(program) as? Subroutine
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(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 -> {
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) {
// cannot inline assembly directly in the Ast here as an Asm node is not an expression....
noModifications
} else {
when (val toInline = sub.statements.first()) {
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
}
}

View File

@ -7,7 +7,6 @@ import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.code.target.virtual.VirtualMachineDefinition
import kotlin.math.floor
@ -17,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.
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) {
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]
@ -314,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))
}
@ -353,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))
}

View File

@ -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)

View File

@ -34,13 +34,13 @@ dependencies {
implementation project(':codeGenCpu6502')
implementation project(':codeGenVirtual')
implementation project(':codeGenExperimental')
implementation 'org.antlr:antlr4-runtime:4.9.3'
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.2.3'
}
configurations.all {

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 {
@ -67,6 +64,17 @@ sys {
}}
}
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
ldy cx16.r0+1
jmp prog8_lib.strcpy
}}
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: only works for NON-OVERLAPPING memory regions!
; note: can't be inlined because is called from asm as well

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

View File

@ -116,8 +116,8 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
; -- cast fac1 to word into A/Y
stx P8ZP_SCRATCH_REG
jsr AYINT
ldy $66
lda $67
ldy floats.AYINT_facmo
lda floats.AYINT_facmo+1
ldx P8ZP_SCRATCH_REG
rts
.pend
@ -168,9 +168,9 @@ stack_float2w .proc ; also used for float2b
stx P8ZP_SCRATCH_REG
jsr AYINT
ldx P8ZP_SCRATCH_REG
lda $66
lda floats.AYINT_facmo
sta P8ESTACK_HI,x
lda $67
lda floats.AYINT_facmo+1
sta P8ESTACK_LO,x
dex
rts

View File

@ -1,8 +1,7 @@
; 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
floats {
; ---- this block contains C-128 compatible floating point related functions ----
@ -110,7 +109,6 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
}}
}
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
%asm {{
@ -151,43 +149,7 @@ asmsub FREADUY (ubyte value @Y) {
}}
}
sub print_f (float value) {
; ---- prints the floating point value (without a newline).
%asm {{
stx P8ZP_SCRATCH_REG
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
jsr FOUT ; fac1 to string in A/Y
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq +
jsr c64.CHROUT
iny
bne -
+ ldx P8ZP_SCRATCH_REG
rts
}}
}
sub pow(float value, float power) -> float {
%asm {{
phx
phy
lda #<value
ldy #>value
jsr floats.CONUPK
lda #<power
ldy #>power
jsr floats.FPWR
ply
plx
rts
}}
}
&uword AYINT_facmo = $66 ; $66/$67 contain result of AYINT
%asminclude "library:c128/floats.asm"
%asminclude "library:c64/floats_funcs.asm"

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 {
@ -274,6 +271,16 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
}}
}
asmsub enable_runstop_and_charsetswitch() clobbers(A) {
%asm {{
lda #0
sta 247 ; enable charset switching
lda #110
sta 808 ; enable run/stop key
rts
}}
}
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
@ -481,6 +488,13 @@ asmsub init_system_phase2() {
}}
}
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp c64.enable_runstop_and_charsetswitch
}}
}
asmsub disable_basic() clobbers(A) {
%asm {{
lda $0a04 ; disable BASIC shadow registers
@ -554,6 +568,17 @@ sys {
}}
}
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
ldy cx16.r0+1
jmp prog8_lib.strcpy
}}
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: only works for NON-OVERLAPPING memory regions!
; note: can't be inlined because is called from asm as well
@ -670,7 +695,10 @@ _longcopy
inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
;lda #14
;sta $01 ; bank the kernal in TODO c128 how to do this?
jsr c64.CLRCHN ; reset i/o channels
jsr c64.enable_runstop_and_charsetswitch
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller

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

View File

@ -113,8 +113,8 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
; -- cast fac1 to word into A/Y
stx P8ZP_SCRATCH_REG
jsr AYINT
ldy $64
lda $65
ldy floats.AYINT_facmo
lda floats.AYINT_facmo+1
ldx P8ZP_SCRATCH_REG
rts
.pend
@ -165,9 +165,9 @@ stack_float2w .proc ; also used for float2b
stx P8ZP_SCRATCH_REG
jsr AYINT
ldx P8ZP_SCRATCH_REG
lda $64
lda floats.AYINT_facmo
sta P8ESTACK_HI,x
lda $65
lda floats.AYINT_facmo+1
sta P8ESTACK_LO,x
dex
rts

View File

@ -1,10 +1,7 @@
; 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
floats {
; ---- this block contains C-64 floating point related functions ----
@ -12,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 ----
@ -91,7 +87,6 @@ romsub $e2b4 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
romsub $e30e = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
asmsub FREADS32() clobbers(A,X,Y) {
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
%asm {{
@ -174,42 +169,7 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
}}
}
sub print_f (float value) {
; ---- prints the floating point value (without a newline).
%asm {{
stx floats_store_reg
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
jsr FOUT ; fac1 to string in A/Y
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq +
jsr c64.CHROUT
iny
bne -
+ ldx floats_store_reg
rts
}}
}
sub pow(float value, float power) -> float {
%asm {{
phx
phy
lda #<value
ldy #>value
jsr floats.CONUPK
lda #<power
ldy #>power
jsr floats.FPWR
ply
plx
rts
}}
}
&uword AYINT_facmo = $64 ; $64/$65 contain result of AYINT
%asminclude "library:c64/floats.asm"
%asminclude "library:c64/floats_funcs.asm"

View File

@ -1,188 +1,6 @@
; --- floating point builtin functions
abs_f_stack .proc
; -- push abs(AY) on stack
jsr floats.MOVFM
jsr floats.ABS
jmp push_fac1
.pend
abs_f_fac1 .proc
; -- FAC1 = abs(AY)
jsr floats.MOVFM
jmp floats.ABS
.pend
func_atan_stack .proc
jsr func_atan_fac1
jmp push_fac1
.pend
func_atan_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr ATN
ldx P8ZP_SCRATCH_REG
rts
.pend
func_ceil_stack .proc
jsr func_ceil_fac1
jmp push_fac1
.pend
func_ceil_fac1 .proc
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
jsr MOVFM
stx P8ZP_SCRATCH_REG
ldx #<fmath_float1
ldy #>fmath_float1
jsr MOVMF
jsr INT
lda #<fmath_float1
ldy #>fmath_float1
jsr FCOMP
cmp #0
beq +
lda #<FL_ONE_const
ldy #>FL_ONE_const
jsr FADD
+ ldx P8ZP_SCRATCH_REG
rts
.pend
func_floor_stack .proc
jsr func_floor_fac1
jmp push_fac1
.pend
func_floor_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr INT
ldx P8ZP_SCRATCH_REG
rts
.pend
func_round_stack .proc
jsr func_round_fac1
jmp push_fac1
.pend
func_round_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr FADDH
jsr INT
ldx P8ZP_SCRATCH_REG
rts
.pend
func_sin_stack .proc
jsr func_sin_fac1
jmp push_fac1
.pend
func_sin_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SIN
ldx P8ZP_SCRATCH_REG
rts
.pend
func_cos_stack .proc
jsr func_cos_fac1
jmp push_fac1
.pend
func_cos_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr COS
ldx P8ZP_SCRATCH_REG
rts
.pend
func_tan_stack .proc
jsr func_tan_fac1
jmp push_fac1
.pend
func_tan_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr TAN
ldx P8ZP_SCRATCH_REG
rts
.pend
func_rad_stack .proc
jsr func_rad_fac1
jmp push_fac1
.pend
func_rad_fac1 .proc
; -- convert degrees to radians (d * pi / 180)
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_pi_div_180
ldy #>_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
rts
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
.pend
func_deg_stack .proc
jsr func_deg_fac1
jmp push_fac1
.pend
func_deg_fac1 .proc
; -- convert radians to degrees (d * (1/ pi * 180))
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_one_over_pi_div_180
ldy #>_one_over_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
rts
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
.pend
func_ln_stack .proc
jsr func_ln_fac1
jmp push_fac1
.pend
func_ln_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
ldx P8ZP_SCRATCH_REG
rts
.pend
func_log2_stack .proc
jsr func_log2_fac1
jmp push_fac1
.pend
func_log2_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
jsr MOVEF
lda #<FL_LOG2_const
ldy #>FL_LOG2_const
jsr MOVFM
jsr FDIVT
ldx P8ZP_SCRATCH_REG
rts
.pend
func_sign_f_stack .proc
jsr func_sign_f_into_A
sta P8ESTACK_LO,x
@ -195,33 +13,6 @@ func_sign_f_into_A .proc
jmp SIGN
.pend
func_sqrt_stack .proc
jsr func_sqrt_fac1
jmp push_fac1
.pend
func_sqrt_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
.pend
func_rndf_stack .proc
jsr func_rndf_fac1
jmp push_fac1
.pend
func_rndf_fac1 .proc
stx P8ZP_SCRATCH_REG
lda #1
jsr FREADSA
jsr RND ; rng into fac1
ldx P8ZP_SCRATCH_REG
rts
.pend
func_swap_f .proc
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
ldy #4
@ -353,85 +144,3 @@ func_all_f_stack .proc
jsr a_times_5
jmp prog8_lib.func_all_b_stack
.pend
func_max_f_stack .proc
jsr func_max_f_fac1
jmp push_fac1
.pend
func_max_f_fac1 .proc
; -- max(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
_loop_count = P8ZP_SCRATCH_REG
stx floats_store_reg
sta _loop_count
lda #255
sta _minmax_cmp+1 ; modifying
lda #<_largest_neg_float
ldy #>_largest_neg_float
_minmax_entry jsr MOVFM
- lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr FCOMP
_minmax_cmp cmp #255 ; modified
bne +
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr MOVFM
+ lda P8ZP_SCRATCH_W1
clc
adc #5
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ dec _loop_count
bne -
ldx floats_store_reg
rts
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
.pend
func_min_f_stack .proc
jsr func_min_f_fac1
jmp push_fac1
.pend
func_min_f_fac1 .proc
; -- min(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
sta func_max_f_fac1._loop_count
lda #1
sta func_max_f_fac1._minmax_cmp+1
lda #<_largest_pos_float
ldy #>_largest_pos_float
jmp func_max_f_fac1._minmax_entry
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
rts
.pend
func_sum_f_stack .proc
jsr func_sum_f_fac1
jmp push_fac1
.pend
func_sum_f_fac1 .proc
; -- sum(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
_loop_count = P8ZP_SCRATCH_REG
stx floats_store_reg
sta _loop_count
lda #<FL_ZERO_const
ldy #>FL_ZERO_const
jsr MOVFM
- lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr FADD
lda P8ZP_SCRATCH_W1
clc
adc #5
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ dec _loop_count
bne -
ldx floats_store_reg
rts
.pend

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

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
@ -300,6 +296,13 @@ asmsub init_system_phase2() {
}}
}
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp c64.enable_runstop_and_charsetswitch
}}
}
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
%asm {{
lda #$80
@ -310,6 +313,16 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
}}
}
asmsub enable_runstop_and_charsetswitch() clobbers(A) {
%asm {{
lda #0
sta 657 ; enable charset switching
lda #237
sta 808 ; enable run/stop key
rts
}}
}
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
@ -519,6 +532,17 @@ sys {
}}
}
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
ldy cx16.r0+1
jmp prog8_lib.strcpy
}}
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: only works for NON-OVERLAPPING memory regions!
; note: can't be inlined because is called from asm as well
@ -635,7 +659,10 @@ _longcopy
inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
lda #31
sta $01 ; bank the kernal in
jsr c64.CLRCHN ; reset i/o channels
jsr c64.enable_runstop_and_charsetswitch
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller

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

View File

@ -1,7 +1,4 @@
; Number conversions routines.
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
conv {
@ -267,7 +264,7 @@ inline asmsub str2ubyte(str string @AY) clobbers(Y) -> ubyte @A {
}}
}
inline asmsub str2byte(str string @AY) clobbers(Y) -> ubyte @A {
inline asmsub str2byte(str string @AY) clobbers(Y) -> byte @A {
; -- returns in A the signed byte value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)

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,100 @@ 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
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
}
}

View File

@ -1,10 +1,7 @@
; 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
floats {
; ---- this block contains C-64 compatible floating point related functions ----
@ -110,7 +107,6 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
}}
}
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
%asm {{
@ -151,7 +147,6 @@ asmsub FREADUY (ubyte value @Y) {
}}
}
asmsub RND() clobbers(A,X,Y) {
%asm {{
lda #0
@ -162,43 +157,7 @@ asmsub RND() clobbers(A,X,Y) {
}}
}
sub print_f (float value) {
; ---- prints the floating point value (without a newline).
%asm {{
phx
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
jsr FOUT ; fac1 to string in A/Y
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq +
jsr c64.CHROUT
iny
bne -
+ plx
rts
}}
}
sub pow(float value, float power) -> float {
%asm {{
phx
phy
lda #<value
ldy #>value
jsr floats.CONUPK
lda #<power
ldy #>power
jsr floats.FPWR
ply
plx
rts
}}
}
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT
%asminclude "library:c64/floats.asm"
%asminclude "library:c64/floats_funcs.asm"

View File

@ -15,14 +15,12 @@
; mode 1 = bitmap 320 x 240 monochrome
; mode 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
; mode 4 = bitmap 320 x 240 x 256c
; mode 4 = bitmap 320 x 240 x 256c (like SCREEN $80 but using this api instead of kernal)
; mode 5 = bitmap 640 x 480 monochrome
; mode 6 = bitmap 640 x 480 x 4c
; higher color dephts in highres are not supported due to lack of VRAM
; TODO can we make a FB vector table and emulation routines for the Cx16s' GRAPH_init() call? to replace the builtin 320x200 fb driver?
gfx2 {
; read-only control variables:
@ -133,24 +131,24 @@ gfx2 {
monochrome_dont_stipple_flag = not enable
}
sub rect(uword x, uword y, uword width, uword height, ubyte color) {
if width==0 or height==0
sub rect(uword x, uword y, uword rwidth, uword rheight, ubyte color) {
if rwidth==0 or rheight==0
return
horizontal_line(x, y, width, color)
if height==1
horizontal_line(x, y, rwidth, color)
if rheight==1
return
horizontal_line(x, y+height-1, width, color)
vertical_line(x, y+1, height-2, color)
if width==1
horizontal_line(x, y+rheight-1, rwidth, color)
vertical_line(x, y+1, rheight-2, color)
if rwidth==1
return
vertical_line(x+width-1, y+1, height-2, color)
vertical_line(x+rwidth-1, y+1, rheight-2, color)
}
sub fillrect(uword x, uword y, uword width, uword height, ubyte color) {
if width==0
sub fillrect(uword x, uword y, uword rwidth, uword rheight, ubyte color) {
if rwidth==0
return
repeat height {
horizontal_line(x, y, width, color)
repeat rheight {
horizontal_line(x, y, rwidth, color)
y++
}
}
@ -290,7 +288,7 @@ _done
}
}
sub vertical_line(uword x, uword y, uword height, ubyte color) {
sub vertical_line(uword x, uword y, uword lheight, ubyte color) {
when active_mode {
1, 5 -> {
; monochrome, lo-res
@ -303,7 +301,7 @@ _done
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
else
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
repeat height {
repeat lheight {
%asm {{
lda cx16.VERA_DATA0
ora cx16.r15L
@ -314,14 +312,14 @@ _done
; draw stippled line.
if x&1 {
y++
height--
lheight--
}
position2(x,y,true)
if active_mode==1
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
else
set_both_strides(13) ; 160 increment = 2 line in 640 px monochrome
repeat height/2 {
repeat lheight/2 {
%asm {{
lda cx16.VERA_DATA0
ora cx16.r15L
@ -336,7 +334,7 @@ _done
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
else
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
repeat height {
repeat lheight {
%asm {{
lda cx16.VERA_DATA0
and cx16.r15L
@ -351,7 +349,7 @@ _done
position(x,y)
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
%asm {{
ldy height
ldy lheight
beq +
lda color
- sta cx16.VERA_DATA0
@ -363,14 +361,14 @@ _done
6 -> {
; highres 4c
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
if height==0
if lheight==0
return
position2(x,y,true)
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3]
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
repeat height {
repeat lheight {
%asm {{
lda cx16.VERA_DATA0
and mask
@ -396,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

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))

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
}}
}
@ -641,6 +637,14 @@ 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 {{
rts
}}
}
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
@ -840,6 +844,17 @@ sys {
}}
}
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
ldy cx16.r0+1
jmp prog8_lib.strcpy
}}
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: only works for NON-OVERLAPPING memory regions!
; If you have to copy overlapping memory regions, consider using

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
@ -195,7 +191,7 @@ sub iso_off() {
asmsub scroll_left() clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left TODO optimize this more?
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
%asm {{
phx
@ -241,7 +237,7 @@ _lx ldx #0 ; modified
}
asmsub scroll_right() clobbers(A) {
; ---- scroll the whole screen 1 character to the right TODO optimize this more?
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
%asm {{
phx
@ -295,7 +291,7 @@ _lx ldx #0 ; modified
}
asmsub scroll_up() clobbers(A, Y) {
; ---- scroll the whole screen 1 character up TODO optimize this more?
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
%asm {{
phx
@ -345,7 +341,7 @@ _nextline
}
asmsub scroll_down() clobbers(A, Y) {
; ---- scroll the whole screen 1 character down TODO optimize this more?
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
%asm {{
phx
@ -635,7 +631,7 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A) {
asl a
sta cx16.VERA_ADDR_L
tya
clc
; clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
pla
@ -655,7 +651,7 @@ asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
pla
sta cx16.VERA_ADDR_L
tya
clc
; clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
lda cx16.VERA_DATA0
@ -677,7 +673,7 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
ina
sta cx16.VERA_ADDR_L
tya
clc
; clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
pla
@ -698,7 +694,7 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
pla
sta cx16.VERA_ADDR_L
tya
clc
; clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
lda cx16.VERA_DATA0
@ -707,36 +703,29 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
}
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
; ---- set char+color at the given position on the screen TODO optimize this better
; note: color handling is the same as on the C64: it only sets the foreground color.
; use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors.
; ---- set char+color at the given position on the screen
; note: color handling is the same as on the C64: it only sets the foreground color and leaves the background color as is.
; Use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors (is faster as well).
%asm {{
phx
lda column
asl a
tax
ldy row
lda charcolor
and #$0f
sta P8ZP_SCRATCH_B1
stz cx16.VERA_CTRL
lda #VERA_TEXTMATRIX_BANK
sta cx16.VERA_ADDR_H
stx cx16.VERA_ADDR_L
tya
clc
;clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
lda char
sta cx16.VERA_DATA0
inx
lda #VERA_TEXTMATRIX_BANK
sta cx16.VERA_ADDR_H
stx cx16.VERA_ADDR_L
tya
clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
inc cx16.VERA_ADDR_L
lda charcolor
and #$0f
sta P8ZP_SCRATCH_B1
lda cx16.VERA_DATA0
and #$f0
ora P8ZP_SCRATCH_B1
@ -747,9 +736,9 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
}
sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
; ---- set char+color at the given position on the screen TODO optimize this
; ---- set char+color at the given position on the screen
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
; use the high nybble in A to set the Bg color!
; use the high nybble in A to set the Bg color! Is a bit faster than setcc() too.
%asm {{
phx
lda column
@ -761,19 +750,12 @@ sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
sta cx16.VERA_ADDR_H
stx cx16.VERA_ADDR_L
tya
clc
; clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
lda char
sta cx16.VERA_DATA0
inx
lda #VERA_TEXTMATRIX_BANK
sta cx16.VERA_ADDR_H
stx cx16.VERA_ADDR_L
tya
clc
adc #>VERA_TEXTMATRIX_ADDR
sta cx16.VERA_ADDR_M
inc cx16.VERA_ADDR_L
lda colors
sta cx16.VERA_DATA0
plx

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
@ -241,6 +239,7 @@ close_end:
}
void c64.CHKIN(11) ; use #11 as input channel again
%asm {{
lda bufferpointer
sta m_in_buffer+1
@ -263,13 +262,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 +278,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 {

View File

@ -0,0 +1,235 @@
floats {
; the floating point functions shared across compiler targets
%option merge
sub print_f(float value) {
; ---- prints the floating point value (without a newline).
%asm {{
stx floats_store_reg
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
jsr FOUT ; fac1 to string in A/Y
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq +
jsr c64.CHROUT
iny
bne -
+ ldx floats_store_reg
rts
}}
}
sub pow(float value, float power) -> float {
%asm {{
stx P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<value
ldy #>value
jsr floats.CONUPK
lda #<power
ldy #>power
jsr floats.FPWR
ldx P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
rts
}}
}
sub fabs(float value) -> float {
%asm {{
stx P8ZP_SCRATCH_REG
lda #<value
ldy #>value
jsr MOVFM
jsr ABS
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub sin(float angle) -> float {
%asm {{
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SIN
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub cos(float angle) -> float {
%asm {{
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr COS
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub tan(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr TAN
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub atan(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr ATN
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub ln(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub log2(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
jsr MOVEF
lda #<FL_LOG2_const
ldy #>FL_LOG2_const
jsr MOVFM
jsr FDIVT
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub sqrt(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub rad(float angle) -> float {
; -- convert degrees to radians (d * pi / 180)
%asm {{
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_pi_div_180
ldy #>_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
rts
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
}}
}
sub deg(float angle) -> float {
; -- convert radians to degrees (d * (1/ pi * 180))
%asm {{
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_one_over_pi_div_180
ldy #>_one_over_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
rts
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
}}
}
sub round(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr FADDH
jsr INT
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub floor(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr INT
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub ceil(float value) -> float {
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
ldx #<fmath_float1
ldy #>fmath_float1
jsr MOVMF
jsr INT
lda #<fmath_float1
ldy #>fmath_float1
jsr FCOMP
cmp #0
beq +
lda #<FL_ONE_const
ldy #>FL_ONE_const
jsr FADD
+ ldx P8ZP_SCRATCH_REG
rts
}}
}
sub rndf() -> float {
%asm {{
stx P8ZP_SCRATCH_REG
lda #1
jsr FREADSA
jsr RND ; rng into fac1
ldx P8ZP_SCRATCH_REG
rts
}}
}
}

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

View File

@ -1,7 +1,181 @@
; 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"
asmsub sin8u(ubyte angle @A) clobbers(Y) -> ubyte @A {
%asm {{
tay
lda _sinecos8u,y
rts
_sinecos8u .byte trunc(128.0 + 127.5 * sin(range(256+64) * rad(360.0/256.0)))
}}
}
asmsub cos8u(ubyte angle @A) clobbers(Y) -> ubyte @A {
%asm {{
tay
lda sin8u._sinecos8u+64,y
rts
}}
}
asmsub sin8(ubyte angle @A) clobbers(Y) -> byte @A {
%asm {{
tay
lda _sinecos8,y
rts
_sinecos8 .char trunc(127.0 * sin(range(256+64) * rad(360.0/256.0)))
}}
}
asmsub cos8(ubyte angle @A) clobbers(Y) -> byte @A {
%asm {{
tay
lda sin8._sinecos8+64,y
rts
}}
}
asmsub sin16(ubyte angle @A) -> word @AY {
%asm {{
tay
lda _sinecos8lo,y
pha
lda _sinecos8hi,y
tay
pla
rts
_ := trunc(32767.0 * sin(range(256+64) * rad(360.0/256.0)))
_sinecos8lo .byte <_
_sinecos8hi .byte >_
}}
}
asmsub cos16(ubyte angle @A) -> word @AY {
%asm {{
tay
lda sin16._sinecos8lo+64,y
pha
lda sin16._sinecos8hi+64,y
tay
pla
rts
}}
}
asmsub sin16u(ubyte angle @A) -> uword @AY {
%asm {{
tay
lda _sinecos8ulo,y
pha
lda _sinecos8uhi,y
tay
pla
rts
_ := trunc(32768.0 + 32767.5 * sin(range(256+64) * rad(360.0/256.0)))
_sinecos8ulo .byte <_
_sinecos8uhi .byte >_
}}
}
asmsub cos16u(ubyte angle @A) -> uword @AY {
%asm {{
tay
lda sin16u._sinecos8ulo+64,y
pha
lda sin16u._sinecos8uhi+64,y
tay
pla
rts
}}
}
asmsub sinr8u(ubyte radians @A) clobbers(Y) -> ubyte @A {
%asm {{
tay
lda _sinecosR8u,y
rts
_sinecosR8u .byte trunc(128.0 + 127.5 * sin(range(180+45) * rad(360.0/180.0)))
}}
}
asmsub cosr8u(ubyte radians @A) clobbers(Y) -> ubyte @A {
%asm {{
tay
lda sinr8u._sinecosR8u+45,y
rts
}}
}
asmsub sinr8(ubyte radians @A) clobbers(Y) -> byte @A {
%asm {{
tay
lda _sinecosR8,y
rts
_sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
}}
}
asmsub cosr8(ubyte radians @A) clobbers(Y) -> byte @A {
%asm {{
tay
lda sinr8._sinecosR8+45,y
rts
}}
}
asmsub sinr16(ubyte radians @A) -> word @AY {
%asm {{
tay
lda _sinecosR8lo,y
pha
lda _sinecosR8hi,y
tay
pla
rts
_ := trunc(32767.0 * sin(range(180+45) * rad(360.0/180.0)))
_sinecosR8lo .byte <_
_sinecosR8hi .byte >_
}}
}
asmsub cosr16(ubyte radians @A) -> word @AY {
%asm {{
tay
lda sinr16._sinecosR8lo+45,y
pha
lda sinr16._sinecosR8hi+45,y
tay
pla
rts
}}
}
asmsub sinr16u(ubyte radians @A) -> uword @AY {
%asm {{
tay
lda _sinecosR8ulo,y
pha
lda _sinecosR8uhi,y
tay
pla
rts
_ := trunc(32768.0 + 32767.5 * sin(range(180+45) * rad(360.0/180.0)))
_sinecosR8ulo .byte <_
_sinecosR8uhi .byte >_
}}
}
asmsub cosr16u(ubyte radians @A) -> uword @AY {
%asm {{
tay
lda sinr16u._sinecosR8ulo+45,y
pha
lda sinr16u._sinecosR8uhi+45,y
tay
pla
rts
}}
}
}

View File

@ -84,304 +84,18 @@ func_all_w_stack .proc
rts
.pend
func_sin8_into_A .proc
tay
lda _sinecos8,y
rts
_sinecos8 .char trunc(127.0 * sin(range(256+64) * rad(360.0/256.0)))
.pend
func_sinr8_into_A .proc
tay
lda _sinecosR8,y
rts
_sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
.pend
func_sin8u_into_A .proc
tay
lda _sinecos8u,y
rts
_sinecos8u .byte trunc(128.0 + 127.5 * sin(range(256+64) * rad(360.0/256.0)))
.pend
func_sinr8u_into_A .proc
tay
lda _sinecosR8u,y
rts
_sinecosR8u .byte trunc(128.0 + 127.5 * sin(range(180+45) * rad(360.0/180.0)))
.pend
func_sin8_stack .proc
tay
lda func_sin8_into_A._sinecos8,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_sinr8_stack .proc
tay
lda func_sinr8_into_A._sinecosR8,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_sin8u_stack .proc
tay
lda func_sin8u_into_A._sinecos8u,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_sinr8u_stack .proc
tay
lda func_sinr8u_into_A._sinecosR8u,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_cos8_into_A .proc
tay
lda func_sin8_into_A._sinecos8+64,y
rts
.pend
func_cosr8_into_A .proc
tay
lda func_sinr8_into_A._sinecosR8+45,y
rts
.pend
func_cos8u_into_A .proc
tay
lda func_sin8u_into_A._sinecos8u+64,y
rts
.pend
func_cosr8u_into_A .proc
tay
lda func_sinr8u_into_A._sinecosR8u+45,y
rts
.pend
func_cos8_stack .proc
tay
lda func_sin8_into_A._sinecos8+64,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_cosr8_stack .proc
tay
lda func_sinr8_into_A._sinecosR8+45,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_cos8u_stack .proc
tay
lda func_sin8u_into_A._sinecos8u+64,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_cosr8u_stack .proc
tay
lda func_sinr8u_into_A._sinecosR8u+45,y
sta P8ESTACK_LO,x
dex
rts
.pend
func_sin16_into_AY .proc
tay
lda _sinecos8lo,y
pha
lda _sinecos8hi,y
tay
pla
rts
_ := trunc(32767.0 * sin(range(256+64) * rad(360.0/256.0)))
_sinecos8lo .byte <_
_sinecos8hi .byte >_
.pend
func_sinr16_into_AY .proc
tay
lda _sinecosR8lo,y
pha
lda _sinecosR8hi,y
tay
pla
rts
_ := trunc(32767.0 * sin(range(180+45) * rad(360.0/180.0)))
_sinecosR8lo .byte <_
_sinecosR8hi .byte >_
.pend
func_sin16u_into_AY .proc
tay
lda _sinecos8ulo,y
pha
lda _sinecos8uhi,y
tay
pla
rts
_ := trunc(32768.0 + 32767.5 * sin(range(256+64) * rad(360.0/256.0)))
_sinecos8ulo .byte <_
_sinecos8uhi .byte >_
.pend
func_sinr16u_into_AY .proc
tay
lda _sinecosR8ulo,y
pha
lda _sinecosR8uhi,y
tay
pla
rts
_ := trunc(32768.0 + 32767.5 * sin(range(180+45) * rad(360.0/180.0)))
_sinecosR8ulo .byte <_
_sinecosR8uhi .byte >_
.pend
func_sin16_stack .proc
tay
lda func_sin16_into_AY._sinecos8lo,y
sta P8ESTACK_LO,x
lda func_sin16_into_AY._sinecos8hi,y
sta P8ESTACK_HI,x
dex
rts
.pend
func_sinr16_stack .proc
tay
lda func_sinr16_into_AY._sinecosR8lo,y
sta P8ESTACK_LO,x
lda func_sinr16_into_AY._sinecosR8hi,y
sta P8ESTACK_HI,x
dex
rts
.pend
func_sin16u_stack .proc
tay
lda func_sin16u_into_AY._sinecos8ulo,y
sta P8ESTACK_LO,x
lda func_sin16u_into_AY._sinecos8uhi,y
sta P8ESTACK_HI,x
dex
rts
.pend
func_sinr16u_stack .proc
tay
lda func_sinr16u_into_AY._sinecosR8ulo,y
sta P8ESTACK_LO,x
lda func_sinr16u_into_AY._sinecosR8uhi,y
sta P8ESTACK_HI,x
dex
rts
.pend
func_cos16_into_AY .proc
tay
lda func_sin16_into_AY._sinecos8lo+64,y
pha
lda func_sin16_into_AY._sinecos8hi+64,y
tay
pla
rts
.pend
func_cosr16_into_AY .proc
tay
lda func_sinr16_into_AY._sinecosR8lo+45,y
pha
lda func_sinr16_into_AY._sinecosR8hi+45,y
tay
pla
rts
.pend
func_cos16u_into_AY .proc
tay
lda func_sin16u_into_AY._sinecos8ulo+64,y
pha
lda func_sin16u_into_AY._sinecos8uhi+64,y
tay
pla
rts
.pend
func_cosr16u_into_AY .proc
tay
lda func_sinr16u_into_AY._sinecosR8ulo+45,y
pha
lda func_sinr16u_into_AY._sinecosR8uhi+45,y
tay
pla
rts
.pend
func_cos16_stack .proc
tay
lda func_sin16_into_AY._sinecos8lo+64,y
sta P8ESTACK_LO,x
lda func_sin16_into_AY._sinecos8hi+64,y
sta P8ESTACK_HI,x
dex
rts
.pend
func_cosr16_stack .proc
tay
lda func_sinr16_into_AY._sinecosR8lo+45,y
sta P8ESTACK_LO,x
lda func_sinr16_into_AY._sinecosR8hi+45,y
sta P8ESTACK_HI,x
dex
rts
.pend
func_cos16u_stack .proc
tay
lda func_sin16u_into_AY._sinecos8ulo+64,y
sta P8ESTACK_LO,x
lda func_sin16u_into_AY._sinecos8uhi+64,y
sta P8ESTACK_HI,x
dex
rts
.pend
func_cosr16u_stack .proc
tay
lda func_sinr16u_into_AY._sinecosR8ulo+45,y
sta P8ESTACK_LO,x
lda func_sinr16u_into_AY._sinecosR8uhi+45,y
sta P8ESTACK_HI,x
dex
rts
.pend
abs_b_stack .proc
; -- push abs(A) on stack (as byte)
; -- push abs(A) on stack (as unsigned word)
jsr abs_b_into_A
sta P8ESTACK_LO,x
stz p8ESTACK_HI,x
dex
rts
.pend
abs_b_into_A .proc
; -- A = abs(A)
abs_b_into_AY .proc
; -- AY = abs(A) (abs always returns unsigned word)
ldy #0
cmp #0
bmi +
rts
@ -548,394 +262,6 @@ func_rndw_stack .proc
rts
.pend
func_min_ub_into_A .proc
; -- min(ubarray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
tay
dey
lda #255
sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y
cmp P8ZP_SCRATCH_B1
bcs +
sta P8ZP_SCRATCH_B1
+ dey
cpy #255
bne -
lda P8ZP_SCRATCH_B1
rts
.pend
func_min_ub_stack .proc
jsr func_min_ub_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_min_b_into_A .proc
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
tay
dey
lda #127
sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y
clc
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bpl +
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_B1
+ dey
cpy #255
bne -
lda P8ZP_SCRATCH_B1
rts
.pend
func_min_b_stack .proc
jsr func_min_b_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_min_uw_into_AY .proc
; -- min(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A)
asl a
tay
dey
dey
lda #$ff
sta _result_minuw
sta _result_minuw+1
_loop
iny
lda (P8ZP_SCRATCH_W1),y
dey
cmp _result_minuw+1
bcc _less
bne _gtequ
lda (P8ZP_SCRATCH_W1),y
cmp _result_minuw
bcs _gtequ
_less lda (P8ZP_SCRATCH_W1),y
sta _result_minuw
iny
lda (P8ZP_SCRATCH_W1),y
sta _result_minuw+1
dey
_gtequ dey
dey
cpy #254
bne _loop
lda _result_minuw
ldy _result_minuw+1
rts
_result_minuw .word 0
.pend
func_min_w_into_AY .proc
; -- min(warray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A)
asl a
tay
dey
dey
lda #$ff
sta _result_minw
lda #$7f
sta _result_minw+1
_loop
lda (P8ZP_SCRATCH_W1),y
cmp _result_minw
iny
lda (P8ZP_SCRATCH_W1),y
dey
sbc _result_minw+1
bvc +
eor #$80
+ bpl _gtequ
lda (P8ZP_SCRATCH_W1),y
sta _result_minw
iny
lda (P8ZP_SCRATCH_W1),y
sta _result_minw+1
dey
_gtequ dey
dey
cpy #254
bne _loop
lda _result_minw
ldy _result_minw+1
rts
_result_minw .word 0
.pend
func_min_uw_stack .proc
jsr func_min_uw_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_min_w_stack .proc
jsr func_min_w_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_max_ub_into_A .proc
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
tay
dey
lda #0
sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y
cmp P8ZP_SCRATCH_B1
bcc +
sta P8ZP_SCRATCH_B1
+ dey
cpy #255
bne -
lda P8ZP_SCRATCH_B1
rts
.pend
func_max_ub_stack .proc
jsr func_max_ub_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_max_b_into_A .proc
; -- max(barray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
tay
lda #-128
sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y
sec
sbc P8ZP_SCRATCH_B1
bvc +
eor #$80
+ bmi +
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_B1
+ dey
cpy #255
bne -
lda P8ZP_SCRATCH_B1
rts
.pend
func_max_b_stack .proc
jsr func_max_b_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_max_uw_into_AY .proc
; -- max(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A)
asl a
tay
dey
dey
lda #0
sta _result_maxuw
sta _result_maxuw+1
_loop
iny
lda (P8ZP_SCRATCH_W1),y
dey
cmp _result_maxuw+1
bcc _lesseq
bne _greater
lda (P8ZP_SCRATCH_W1),y
cmp _result_maxuw
bcc _lesseq
_greater lda (P8ZP_SCRATCH_W1),y
sta _result_maxuw
iny
lda (P8ZP_SCRATCH_W1),y
sta _result_maxuw+1
dey
_lesseq dey
dey
cpy #254
bne _loop
lda _result_maxuw
ldy _result_maxuw+1
rts
_result_maxuw .word 0
.pend
func_max_uw_stack .proc
jsr func_max_uw_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_max_w_into_AY .proc
; -- max(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A)
asl a
tay
dey
dey
lda #0
sta _result_maxw
lda #$80
sta _result_maxw+1
_loop
lda (P8ZP_SCRATCH_W1),y
cmp _result_maxw
iny
lda (P8ZP_SCRATCH_W1),y
dey
sbc _result_maxw+1
bvc +
eor #$80
+ bmi _lesseq
lda (P8ZP_SCRATCH_W1),y
sta _result_maxw
iny
lda (P8ZP_SCRATCH_W1),y
sta _result_maxw+1
dey
_lesseq dey
dey
cpy #254
bne _loop
lda _result_maxw
ldy _result_maxw+1
rts
_result_maxw .word 0
.pend
func_max_w_stack .proc
jsr func_max_w_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_sum_ub_into_AY .proc
; -- sum(ubarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A)
tay
dey
lda #0
sta P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2+1
- lda (P8ZP_SCRATCH_W1),y
clc
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
bcc +
inc P8ZP_SCRATCH_W2+1
+ dey
cpy #255
bne -
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
rts
.pend
func_sum_ub_stack .proc
jsr func_sum_ub_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_sum_b_into_AY .proc
; -- sum(barray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A)
tay
dey
lda #0
sta P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2+1
_loop lda (P8ZP_SCRATCH_W1),y
pha
clc
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
; sign extend the high byte
pla
and #$80
beq +
lda #$ff
+ adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
dey
cpy #255
bne _loop
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
rts
.pend
func_sum_b_stack .proc
jsr func_sum_b_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_sum_uw_into_AY .proc
; -- sum(uwarray) -> AY (array in P8ZP_SCRATCH_W1, num elements in A)
asl a
tay
dey
dey
lda #0
sta P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2+1
- lda (P8ZP_SCRATCH_W1),y
iny
clc
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda (P8ZP_SCRATCH_W1),y
adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
dey
dey
dey
cpy #254
bne -
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
rts
.pend
func_sum_uw_stack .proc
jsr func_sum_uw_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_sum_w_into_AY = func_sum_uw_into_AY
func_sum_w_stack = func_sum_uw_stack
func_sort_ub .proc
; 8bit unsigned sort

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

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"

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 {

View File

@ -0,0 +1,254 @@
; Number conversions routines.
conv {
; ----- number conversions to decimal strings ----
str string_out = "????????????????" ; result buffer for the string conversion routines
sub str_ub0(ubyte value) {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
ubyte hundreds = value / 100
value -= hundreds*100
ubyte tens = value / 10
value -= tens*10
string_out[0] = hundreds+'0'
string_out[1] = tens+'0'
string_out[2] = value+'0'
string_out[3] = 0
}
sub str_ub(ubyte value) {
; ---- convert the ubyte in A in decimal string form, without left padding 0s
internal_str_ub(value, string_out)
}
sub str_b(byte value) {
; ---- convert the byte in A in decimal string form, without left padding 0s
uword out_ptr = &string_out
if value<0 {
@(out_ptr) = '-'
out_ptr++
value = -value
}
internal_str_ub(value as ubyte, out_ptr)
}
sub internal_str_ub(ubyte value, uword out_ptr) {
ubyte hundreds = value / 100
value -= hundreds*100
ubyte tens = value / 10
value -= tens*10
if hundreds
goto output_hundreds
if tens
goto output_tens
goto output_ones
output_hundreds:
@(out_ptr) = hundreds+'0'
out_ptr++
output_tens:
@(out_ptr) = tens+'0'
out_ptr++
output_ones:
@(out_ptr) = value+'0'
out_ptr++
@(out_ptr) = 0
}
str hex_digits = "0123456789abcdef"
sub str_ubhex (ubyte value) {
; ---- convert the ubyte in A in hex string form
string_out[0] = hex_digits[value>>4]
string_out[1] = hex_digits[value&15]
string_out[2] = 0
}
sub str_ubbin (ubyte value) {
; ---- convert the ubyte in A in binary string form
uword out_ptr = &string_out
repeat 8 {
rol(value)
if_cc
@(out_ptr) = '0'
else
@(out_ptr) = '1'
out_ptr++
}
@(out_ptr) = 0
}
sub str_uwbin (uword value) {
; ---- convert the uword in A/Y in binary string form
uword out_ptr = &string_out
repeat 16 {
rol(value)
if_cc
@(out_ptr) = '0'
else
@(out_ptr) = '1'
out_ptr++
}
@(out_ptr) = 0
}
sub str_uwhex (uword value) {
; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
ubyte bits = msb(value)
string_out[0] = hex_digits[bits>>4]
string_out[1] = hex_digits[bits&15]
bits = lsb(value)
string_out[2] = hex_digits[bits>>4]
string_out[3] = hex_digits[bits&15]
string_out[4] = 0
}
sub str_uw0 (uword value) {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
ubyte tenthousands = (value / 10000) as ubyte
value -= 10000*tenthousands
ubyte thousands = (value / 1000) as ubyte
value -= 1000*thousands
ubyte hundreds = (value / 100) as ubyte
value -= 100 as uword * hundreds
ubyte tens = (value / 10) as ubyte
value -= 10*tens
string_out[0] = tenthousands+'0'
string_out[1] = thousands+'0'
string_out[2] = hundreds+'0'
string_out[3] = tens+'0'
string_out[4] = value as ubyte + '0'
string_out[5] = 0
}
sub str_uw (uword value) {
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
internal_str_uw(value, string_out)
}
sub str_w (word value) {
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
uword out_ptr = &string_out
if value<0 {
@(out_ptr) = '-'
out_ptr++
value = -value
}
internal_str_uw(value as uword, out_ptr)
}
sub internal_str_uw(uword value, uword out_ptr) {
ubyte tenthousands = (value / 10000) as ubyte
value -= 10000*tenthousands
ubyte thousands = (value / 1000) as ubyte
value -= 1000*thousands
ubyte hundreds = (value / 100) as ubyte
value -= 100 as uword * hundreds
ubyte tens = (value / 10) as ubyte
value -= 10*tens
if tenthousands
goto output_tenthousands
if thousands
goto output_thousands
if hundreds
goto output_hundreds
if tens
goto output_tens
goto output_ones
output_tenthousands:
@(out_ptr) = tenthousands+'0'
out_ptr++
output_thousands:
@(out_ptr) = thousands+'0'
out_ptr++
output_hundreds:
@(out_ptr) = hundreds+'0'
out_ptr++
output_tens:
@(out_ptr) = tens+'0'
out_ptr++
output_ones:
@(out_ptr) = value as ubyte + '0'
out_ptr++
@(out_ptr) = 0
}
; ---- string conversion to numbers -----
sub str2ubyte(str string) -> ubyte {
; -- returns in A the unsigned byte value of the string number argument in AY
; the number may NOT be preceded by a + sign and may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
return str2uword(string) as ubyte
}
sub str2byte(str string) -> byte {
; -- returns in A the signed byte value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
return str2word(string) as byte
}
sub str2uword(str string) -> uword {
; -- returns the unsigned word value of the string number argument in AY
; the number may NOT be preceded by a + sign and may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%asm {{
loadm.w r0, {conv.str2uword.string}
syscall 11
return
}}
}
sub str2word(str string) -> word {
; -- returns the signed word value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%asm {{
loadm.w r0, {conv.str2word.string}
syscall 12
return
}}
}
sub hex2uword(str string) -> uword {
; -- hexadecimal string (with or without '$') to uword.
; stops parsing at the first character that's not a hex digit (except leading $)
uword result
ubyte char
if @(string)=='$'
string++
repeat {
char = @(string)
if char==0
return result
result <<= 4
if char>='0' and char<='9'
result |= char-'0'
else
result |= char-'a'+10
string++
}
}
sub bin2uword(str string) -> uword {
; -- binary string (with or without '%') to uword.
; stops parsing at the first character that's not a 0 or 1. (except leading %)
uword result
ubyte char
if @(string)=='%'
string++
repeat {
char = @(string)
if char==0
return result
result <<= 1
if char=='1'
result |= 1
string++
}
}
}

View File

@ -0,0 +1,133 @@
; Prog8 definitions for floating point handling on the VirtualMachine
%option enable_floats
floats {
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586
sub print_f(float value) {
; ---- prints the floating point value (without a newline).
%asm {{
loadm.f fr0,{floats.print_f.value}
syscall 25
return
}}
}
sub pow(float value, float power) -> float {
%asm {{
loadm.f fr0,{floats.pow.value}
loadm.f fr1,{floats.pow.power}
fpow.f fr0,fr1
return
}}
}
sub fabs(float value) -> float {
%asm {{
loadm.f fr0,{floats.fabs.value}
fabs.f fr0,fr0
return
}}
}
sub sin(float angle) -> float {
%asm {{
loadm.f fr0,{floats.sin.angle}
fsin.f fr0,fr0
return
}}
}
sub cos(float angle) -> float {
%asm {{
loadm.f fr0,{floats.cos.angle}
fcos.f fr0,fr0
return
}}
}
sub tan(float value) -> float {
%asm {{
loadm.f fr0,{floats.tan.value}
ftan.f fr0,fr0
return
}}
}
sub atan(float value) -> float {
%asm {{
loadm.f fr0,{floats.atan.value}
fatan.f fr0,fr0
return
}}
}
sub ln(float value) -> float {
%asm {{
loadm.f fr0,{floats.ln.value}
fln.f fr0,fr0
return
}}
}
sub log2(float value) -> float {
%asm {{
loadm.f fr0,{floats.log2.value}
flog.f fr0,fr0
return
}}
}
sub sqrt(float value) -> float {
%asm {{
loadm.f fr0,{floats.sqrt.value}
fsqrt.f fr0,fr0
return
}}
}
sub rad(float angle) -> float {
; -- convert degrees to radians (d * pi / 180)
return angle * PI / 180.0
}
sub deg(float angle) -> float {
; -- convert radians to degrees (d * (1/ pi * 180))
return angle * 180.0 / PI
}
sub round(float value) -> float {
%asm {{
loadm.f fr0,{floats.round.value}
fround.f fr0,fr0
return
}}
}
sub floor(float value) -> float {
%asm {{
loadm.f fr0,{floats.floor.value}
ffloor.f fr0,fr0
return
}}
}
sub ceil(float value) -> float {
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
%asm {{
loadm.f fr0,{floats.ceil.value}
fceil.f fr0,fr0
return
}}
}
sub rndf() -> float {
%asm {{
rnd.f fr0
return
}}
}
}

View File

@ -1,7 +1,127 @@
; Internal Math library routines - always included by the compiler
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
math {
; TODO
sub sin8u(ubyte angle) -> ubyte {
ubyte[256] sintab = [$80, $83, $86, $89, $8c, $8f, $92, $95, $98, $9b, $9e, $a2, $a5, $a7, $aa, $ad, $b0, $b3, $b6, $b9,
$bc, $be, $c1, $c4, $c6, $c9, $cb, $ce, $d0, $d3, $d5, $d7, $da, $dc, $de, $e0,
$e2, $e4, $e6, $e8, $ea, $eb, $ed, $ee, $f0, $f1, $f3, $f4, $f5, $f6, $f8, $f9,
$fa, $fa, $fb, $fc, $fd, $fd, $fe, $fe, $fe, $ff, $ff, $ff, $ff, $ff, $ff, $ff,
$fe, $fe, $fe, $fd, $fd, $fc, $fb, $fa, $fa, $f9, $f8, $f6, $f5, $f4, $f3, $f1,
$f0, $ee, $ed, $eb, $ea, $e8, $e6, $e4, $e2, $e0, $de, $dc, $da, $d7, $d5, $d3,
$d0, $ce, $cb, $c9, $c6, $c4, $c1, $be, $bc, $b9, $b6, $b3, $b0, $ad, $aa, $a7,
$a5, $a2, $9e, $9b, $98, $95, $92, $8f, $8c, $89, $86, $83, $80, $7c, $79, $76,
$73, $70, $6d, $6a, $67, $64, $61, $5d, $5a, $58, $55, $52, $4f, $4c, $49, $46,
$43, $41, $3e, $3b, $39, $36, $34, $31, $2f, $2c, $2a, $28, $25, $23, $21, $1f,
$1d, $1b, $19, $17, $15, $14, $12, $11, $0f, $0e, $0c, $0b, $0a, $09, $07, $06,
$05, $05, $04, $03, $02, $02, $01, $01, $01, $00, $00, $00, $00, $00, $00, $00,
$01, $01, $01, $02, $02, $03, $04, $05, $05, $06, $07, $09, $0a, $0b, $0c, $0e,
$0f, $11, $12, $14, $15, $17, $19, $1b, $1d, $1f, $21, $23, $25, $28, $2a, $2c,
$2f, $31, $34, $36, $39, $3b, $3e, $41, $43, $46, $49, $4c, $4f, $52, $55, $58,
$5a, $5d, $61, $64, $67, $6a, $6d, $70, $73, $76, $79, $7c]
return sintab[angle]
}
sub cos8u(ubyte angle) -> ubyte {
ubyte[256] costab = [$ff, $ff, $ff, $ff,
$fe, $fe, $fe, $fd, $fd, $fc, $fb, $fa, $fa, $f9, $f8, $f6, $f5, $f4, $f3, $f1,
$f0, $ee, $ed, $eb, $ea, $e8, $e6, $e4, $e2, $e0, $de, $dc, $da, $d7, $d5, $d3,
$d0, $ce, $cb, $c9, $c6, $c4, $c1, $be, $bc, $b9, $b6, $b3, $b0, $ad, $aa, $a7,
$a5, $a2, $9e, $9b, $98, $95, $92, $8f, $8c, $89, $86, $83, $80, $7c, $79, $76,
$73, $70, $6d, $6a, $67, $64, $61, $5d, $5a, $58, $55, $52, $4f, $4c, $49, $46,
$43, $41, $3e, $3b, $39, $36, $34, $31, $2f, $2c, $2a, $28, $25, $23, $21, $1f,
$1d, $1b, $19, $17, $15, $14, $12, $11, $0f, $0e, $0c, $0b, $0a, $09, $07, $06,
$05, $05, $04, $03, $02, $02, $01, $01, $01, $00, $00, $00, $00, $00, $00, $00,
$01, $01, $01, $02, $02, $03, $04, $05, $05, $06, $07, $09, $0a, $0b, $0c, $0e,
$0f, $11, $12, $14, $15, $17, $19, $1b, $1d, $1f, $21, $23, $25, $28, $2a, $2c,
$2f, $31, $34, $36, $39, $3b, $3e, $41, $43, $46, $49, $4c, $4f, $52, $55, $58,
$5a, $5d, $61, $64, $67, $6a, $6d, $70, $73, $76, $79, $7c, $7f, $83, $86, $89,
$8c, $8f, $92, $95, $98, $9b, $9e, $a2, $a5, $a7, $aa, $ad, $b0, $b3, $b6, $b9,
$bc, $be, $c1, $c4, $c6, $c9, $cb, $ce, $d0, $d3, $d5, $d7, $da, $dc, $de, $e0,
$e2, $e4, $e6, $e8, $ea, $eb, $ed, $ee, $f0, $f1, $f3, $f4, $f5, $f6, $f8, $f9,
$fa, $fa, $fb, $fc, $fd, $fd, $fe, $fe, $fe, $ff, $ff, $ff ]
return costab[angle]
}
sub sin8(ubyte angle) -> byte {
return 42 ; TODO
}
sub cos8(ubyte angle) -> byte {
return 42 ; TODO
}
sub sin16(ubyte angle) -> word {
return 4242 ; TODO
}
sub cos16(ubyte angle) -> word {
return 4242 ; TODO
}
sub sin16u(ubyte angle) -> uword {
return 4242 ; TODO
}
sub cos16u(ubyte angle) -> uword {
return 4242 ; TODO
}
sub sinr8u(ubyte radians) -> ubyte {
ubyte[180] sintab = [
$80, $84, $88, $8d,
$91, $96, $9a, $9e, $a3, $a7, $ab, $af, $b3, $b7, $bb, $bf, $c3, $c7, $ca, $ce,
$d1, $d5, $d8, $db, $de, $e1, $e4, $e7, $e9, $ec, $ee, $f0, $f2, $f4, $f6, $f7,
$f9, $fa, $fb, $fc, $fd, $fe, $fe, $ff, $ff, $ff, $ff, $ff, $fe, $fe, $fd, $fc,
$fb, $fa, $f9, $f7, $f6, $f4, $f2, $f0, $ee, $ec, $e9, $e7, $e4, $e1, $de, $db,
$d8, $d5, $d1, $ce, $ca, $c7, $c3, $bf, $bb, $b7, $b3, $af, $ab, $a7, $a3, $9e,
$9a, $96, $91, $8d, $88, $84, $80, $7b, $77, $72, $6e, $69, $65, $61, $5c, $58,
$54, $50, $4c, $48, $44, $40, $3c, $38, $35, $31, $2e, $2a, $27, $24, $21, $1e,
$1b, $18, $16, $13, $11, $0f, $0d, $0b, $09, $08, $06, $05, $04, $03, $02, $01,
$01, $00, $00, $00, $00, $00, $01, $01, $02, $03, $04, $05, $06, $08, $09, $0b,
$0d, $0f, $11, $13, $16, $18, $1b, $1e, $21, $24, $27, $2a, $2e, $31, $35, $38,
$3c, $40, $44, $48, $4c, $50, $54, $58, $5c, $61, $65, $69, $6e, $72, $77, $7b]
return sintab[radians]
}
sub cosr8u(ubyte radians) -> ubyte {
ubyte[180] costab = [
$ff, $ff, $ff, $fe, $fe, $fd, $fc,
$fb, $fa, $f9, $f7, $f6, $f4, $f2, $f0, $ee, $ec, $e9, $e7, $e4, $e1, $de, $db,
$d8, $d5, $d1, $ce, $ca, $c7, $c3, $bf, $bb, $b7, $b3, $af, $ab, $a7, $a3, $9e,
$9a, $96, $91, $8d, $88, $84, $80, $7b, $77, $72, $6e, $69, $65, $61, $5c, $58,
$54, $50, $4c, $48, $44, $40, $3c, $38, $35, $31, $2e, $2a, $27, $24, $21, $1e,
$1b, $18, $16, $13, $11, $0f, $0d, $0b, $09, $08, $06, $05, $04, $03, $02, $01,
$01, $00, $00, $00, $00, $00, $01, $01, $02, $03, $04, $05, $06, $08, $09, $0b,
$0d, $0f, $11, $13, $16, $18, $1b, $1e, $21, $24, $27, $2a, $2e, $31, $35, $38,
$3c, $40, $44, $48, $4c, $50, $54, $58, $5c, $61, $65, $69, $6e, $72, $77, $7b,
$7f, $84, $88, $8d, $91, $96, $9a, $9e, $a3, $a7, $ab, $af, $b3, $b7, $bb, $bf,
$c3, $c7, $ca, $ce, $d1, $d5, $d8, $db, $de, $e1, $e4, $e7, $e9, $ec, $ee, $f0,
$f2, $f4, $f6, $f7, $f9, $fa, $fb, $fc, $fd, $fe, $fe, $ff, $ff ]
return costab[radians]
}
sub sinr8(ubyte radians) -> byte {
return 42 ; TODO
}
sub cosr8(ubyte radians) -> byte {
return 42 ; TODO
}
sub sinr16(ubyte radians) -> word {
return 4242 ; TODO
}
sub cosr16(ubyte radians) -> word {
return 4242 ; TODO
}
sub sinr16u(ubyte radians) -> uword {
return 4242 ; TODO
}
sub cosr16u(ubyte radians) -> uword {
return 4242 ; TODO
}
}

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
%import textio
@ -37,4 +35,19 @@ prog8_lib {
}
return false
}
sub string_compare(str st1, str st2) -> byte {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2.
; Note that you can also directly compare strings and string values with eachother using
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
%asm {{
loadm.w r0, {prog8_lib.string_compare.st1}
loadm.w r1, {prog8_lib.string_compare.st2}
syscall 29
return
}}
}
}

View File

@ -0,0 +1,116 @@
; 0-terminated string manipulation routines. For the Virtual Machine target.
string {
sub length(str st) -> ubyte {
; Returns the number of bytes in the string.
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
; regardless of the size of the string during compilation time. Dont confuse this with len and sizeof!
ubyte count = 0
while st[count]
count++
return count
}
sub left(str source, ubyte slen, str target) {
; Copies the left side of the source string of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result.
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
; Modifies in-place, doesnt return a value (so cant be used in an expression).
target[slen] = 0
ubyte ix
for ix in 0 to slen-1 {
target[ix] = source[ix]
}
}
sub right(str source, ubyte slen, str target) {
; Copies the right side of the source string of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result.
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
; Modifies in-place, doesnt return a value (so cant be used in an expression).
ubyte offset = length(source)-slen
ubyte ix
for ix in 0 to slen-1 {
target[ix] = source[ix+offset]
}
target[ix]=0
}
sub slice(str source, ubyte start, ubyte slen, str target) {
; Copies a segment from the source string, starting at the given index,
; and of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result.
; Also, you have to make sure yourself that start and length are within bounds of the strings.
; Modifies in-place, doesnt return a value (so cant be used in an expression).
ubyte ix
for ix in 0 to slen-1 {
target[ix] = source[ix+start]
}
target[ix]=0
}
sub find(str st, ubyte character) -> ubyte {
; Locates the first position of the given character in the string,
; returns Carry set if found + index in A, or Carry clear if not found.
ubyte ix
for ix in 0 to length(st)-1 {
if st[ix]==character {
sys.set_carry()
return ix
}
}
sys.clear_carry()
return 0
}
sub copy(str source, str target) -> ubyte {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
; Often you dont have to call this explicitly and can just write string1 = string2
; but this function is useful if youre dealing with addresses for instance.
ubyte ix
repeat {
ubyte char=source[ix]
target[ix]=char
if not char
return ix
ix++
}
}
sub compare(str st1, str st2) -> byte {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2.
; Note that you can also directly compare strings and string values with eachother using
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
return prog8_lib.string_compare(st1, st2)
}
sub lower(str st) -> ubyte {
; Lowercases the petscii string in-place. Returns length of the string.
; (for efficiency, non-letter characters > 128 will also not be left intact,
; but regular text doesn't usually contain those characters anyway.)
ubyte ix
repeat {
ubyte char=st[ix]
if not char
return ix
if char >= 'A' and char <= 'Z'
st[ix] = char | %00100000
ix++
}
}
sub upper(str st) -> ubyte {
; Uppercases the petscii string in-place. Returns length of the string.
ubyte ix
repeat {
ubyte char=st[ix]
if not char
return ix
if char >= 97 and char <= 122
st[ix] = char & %11011111
ix++
}
}
}

View File

@ -1,62 +1,47 @@
; Prog8 definitions for the Virtual Machine
;
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
;
sys {
; ------- lowlevel system routines --------
const ubyte target = 255 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL, 255 = virtual
; SYSCALLS
; 0 = reset ; resets system
; 1 = exit ; stops program and returns statuscode from r0.w
; 2 = print_c ; print single character
; 3 = print_s ; print 0-terminated string from memory
; 4 = print_u8 ; print unsigned int byte
; 5 = print_u16 ; print unsigned int word
; 6 = input ; reads a line of text entered by the user, r0.w = memory buffer, r1.b = maxlength (0-255, 0=unlimited). Zero-terminates the string. Returns length in r65535.w
; 7 = sleep ; sleep amount of milliseconds
; 8 = gfx_enable ; enable graphics window r0.b = 0 -> lores 320x240, r0.b = 1 -> hires 640x480
; 9 = gfx_clear ; clear graphics window with shade in r0.b
; 10 = gfx_plot ; plot pixel in graphics window, r0.w/r1.w contain X and Y coordinates, r2.b contains brightness
const ubyte SC_RESET = 0
const ubyte SC_EXIT = 1
const ubyte SC_PRINT_C = 2
const ubyte SC_PRINT_S = 3
const ubyte SC_PRINT_U8 = 4
const ubyte SC_PRINT_u16 = 5
const ubyte SC_INPUT = 6
const ubyte SC_SLEEP = 7
const ubyte SC_GFX_ENABLE = 8
const ubyte SC_GFX_CLEAR = 9
const ubyte SC_GFX_PLOT = 10
const ubyte SC_RND = 11
const ubyte SC_WAIT = 12
const ubyte SC_WAITVSYNC = 13
sub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt.
syscall(SC_RESET)
%asm {{
syscall 0
}}
}
sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds)
syscall1(SC_WAIT, jiffies)
%asm {{
loadm.w r0, {sys.wait.jiffies}
syscall 13
}}
}
sub waitvsync() {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
syscall(SC_WAITVSYNC)
%asm {{
syscall 14
}}
}
sub memcopy(uword source, uword target, uword count) {
repeat count {
@(target) = @(source)
sub internal_stringcopy(uword source, uword tgt) {
; Called when the compiler wants to assign a string value to another string.
while @(source) {
@(tgt) = @(source)
source++
target++
tgt++
}
@(tgt)=0
}
sub memcopy(uword source, uword tgt, uword count) {
repeat count {
@(tgt) = @(source)
source++
tgt++
}
}
@ -76,7 +61,39 @@ sys {
sub exit(ubyte returnvalue) {
; -- immediately exit the program with a return code in the A register
syscall1(SC_EXIT, returnvalue)
%asm {{
loadm.b r0,{sys.exit.returnvalue}
syscall 1
}}
}
sub set_carry() {
%asm {{
sec
}}
}
sub clear_carry() {
%asm {{
clc
}}
}
sub gfx_enable(ubyte mode) {
%asm {{
loadm.b r0, {sys.gfx_enable.mode}
syscall 8
}}
}
sub gfx_plot(uword xx, uword yy, ubyte color) {
%asm {{
loadm.w r0, {sys.gfx_plot.xx}
loadm.w r1, {sys.gfx_plot.yy}
loadm.b r2, {sys.gfx_plot.color}
syscall 10
}}
}
}

View File

@ -1,14 +1,15 @@
; 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 syslib
%import conv
txt {
sub clear_screen() {
syscall1(3, "\x1b[2J\x1B[H")
str @shared sequence = "\x1b[2J\x1B[H"
%asm {{
load.w r0, {txt.clear_screen.sequence}
syscall 3
}}
}
sub nl() {
@ -28,144 +29,95 @@ sub uppercase() {
}
sub chrout(ubyte char) {
syscall1(2, char)
%asm {{
loadm.b r0, {txt.chrout.char}
syscall 2
}}
}
sub print (str text) {
syscall1(3, text)
%asm {{
loadm.w r0, {txt.print.text}
syscall 3
}}
}
sub print_ub0 (ubyte value) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
; TODO use conv module?
ubyte hundreds = value / 100
value -= hundreds*100
ubyte tens = value / 10
value -= tens*10
chrout(hundreds+'0')
chrout(tens+'0')
chrout(value+'0')
conv.str_ub0(value)
print(conv.string_out)
}
sub print_ub (ubyte value) {
; ---- print the ubyte in decimal form, without left padding 0s
; TODO use conv module?
ubyte hundreds = value / 100
value -= hundreds*100
ubyte tens = value / 10
value -= tens*10
if hundreds
goto print_hundreds
if tens
goto print_tens
goto print_ones
print_hundreds:
chrout(hundreds+'0')
print_tens:
chrout(tens+'0')
print_ones:
chrout(value+'0')
conv.str_ub(value)
print(conv.string_out)
}
sub print_b (byte value) {
; ---- print the byte in decimal form, without left padding 0s
if value<0 {
chrout('-')
value = -value
}
print_ub(value as ubyte)
conv.str_b(value)
print(conv.string_out)
}
str hex_digits = "0123456789abcdef"
sub print_ubhex (ubyte value, ubyte prefix) {
; ---- print the ubyte in hex form
if prefix
chrout('$')
chrout(hex_digits[value>>4])
chrout(hex_digits[value&15])
conv.str_ubhex(value)
print(conv.string_out)
}
sub print_ubbin (ubyte value, ubyte prefix) {
; ---- print the ubyte in binary form
; TODO use conv module?
if prefix
chrout('%')
conv.str_ubbin(value)
print(conv.string_out)
}
sub print_uwbin (uword value, ubyte prefix) {
; ---- print the uword in binary form
; TODO use conv module?
if prefix
chrout('%')
conv.str_uwbin(value)
print(conv.string_out)
}
sub print_uwhex (uword value, ubyte prefix) {
; ---- print the uword in hexadecimal form (4 digits)
print_ubhex(msb(value), true)
print_ubhex(lsb(value), false)
if prefix
chrout('$')
conv.str_uwhex(value)
print(conv.string_out)
}
sub print_uw0 (uword value) {
; ---- print the uword value in decimal form, with left padding 0s (5 positions total)
; TODO use conv module?
ubyte tenthousands = (value / 10000) as ubyte
value -= 10000*tenthousands
ubyte thousands = (value / 1000) as ubyte
value -= 1000*thousands
ubyte hundreds = (value / 100) as ubyte
value -= 100 as uword * hundreds
ubyte tens = (value / 10) as ubyte
value -= 10*tens
chrout(tenthousands+'0')
chrout(thousands+'0')
chrout(hundreds+'0')
chrout(tens+'0')
chrout(value as ubyte + '0')
conv.str_uw0(value)
print(conv.string_out)
}
sub print_uw (uword value) {
; ---- print the uword in decimal form, without left padding 0s
ubyte tenthousands = (value / 10000) as ubyte
value -= 10000*tenthousands
ubyte thousands = (value / 1000) as ubyte
value -= 1000*thousands
ubyte hundreds = (value / 100) as ubyte
value -= 100 as uword * hundreds
ubyte tens = (value / 10) as ubyte
value -= 10*tens
if tenthousands
goto print_tenthousands
if thousands
goto print_thousands
if hundreds
goto print_hundreds
if tens
goto print_tens
goto print_ones
print_tenthousands:
chrout(tenthousands+'0')
print_thousands:
chrout(thousands+'0')
print_hundreds:
chrout(hundreds+'0')
print_tens:
chrout(tens+'0')
print_ones:
chrout(value as ubyte + '0')
conv.str_uw(value)
print(conv.string_out)
}
sub print_w (word value) {
; ---- print the (signed) word in decimal form, without left padding 0's
if value<0 {
chrout('-')
value = -value
}
print_uw(value as uword)
conv.str_w(value)
print(conv.string_out)
}
sub input_chars (uword buffer) -> ubyte {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel!
; TODO
return 0
%asm {{
loadm.w r0,{txt.input_chars.buffer}
syscall 6
return
}}
}
}

View File

@ -1 +1 @@
8.0
8.2

View File

@ -4,17 +4,20 @@ import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.code.core.CbmPrgLauncherType
import prog8.code.target.*
import prog8.code.target.virtual.VirtualMachineDefinition
import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardWatchEventKinds
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,6 +49,7 @@ private fun compileMain(args: Array<String>): Boolean {
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 moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@ -76,6 +80,10 @@ private fun compileMain(args: Array<String>): Boolean {
return false
}
if(startVm==true) {
return runVm(moduleFiles.first())
}
if(watchMode==true) {
val watchservice = FileSystems.getDefault().newWatchService()
val allImportedFiles = mutableSetOf<Path>()
@ -184,3 +192,14 @@ private fun compileMain(args: Array<String>): Boolean {
return true
}
fun runVm(listingFilename: String): Boolean {
val name =
if(listingFilename.endsWith(".p8virt"))
listingFilename.substring(0, listingFilename.length-7)
else
listingFilename
val vmdef = VirtualMachineDefinition()
vmdef.launchEmulator(0, Paths.get(name))
return true
}

View File

@ -195,8 +195,8 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
override val names = functions.keys
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteral? {
val func = BuiltinFunctions[name]
override fun constValue(funcName: String, args: List<Expression>, position: Position): NumericLiteral? {
val func = BuiltinFunctions[funcName]
if(func!=null) {
val exprfunc = func.constExpressionFunc
if(exprfunc!=null) {
@ -213,8 +213,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
}
return null
}
override fun returnType(name: String, args: MutableList<Expression>) =
builtinFunctionReturnType(name, args, program)
override fun returnType(funcName: String) = builtinFunctionReturnType(funcName)
}
fun parseImports(filepath: Path,
@ -265,7 +264,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
.flatMap { (it as Directive).args }.toSet()
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
val noSysInit = allOptions.any { it.name == "no_sysinit" }
var zpType: ZeropageType =
val zpType: ZeropageType =
if (zpoption == null)
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
else
@ -348,12 +347,13 @@ 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()
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
errors.report()
if (optsDone1 + optsDone2 + optsDone3 == 0)
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
break
}
errors.report()
@ -435,16 +435,15 @@ internal fun asmGeneratorFor(program: Program,
{
if(options.experimentalCodegen) {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
// TODO for now, use the new Intermediary Ast for this experimental codegen:
val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.experimental.AsmGen(intermediateAst, symbolTable, options, errors)
}
} else {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
// TODO rewrite 6502 codegen on new Intermediary Ast
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
if (options.compTarget.name == VMTarget.NAME) {
// TODO for now, use the new Intermediary Ast for this codegen:
val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.virtual.CodeGen(intermediateAst, symbolTable, options, errors)
}

View File

@ -29,12 +29,12 @@ internal class ErrorReporter: IErrorReporter {
MessageSeverity.WARNING -> System.out
MessageSeverity.ERROR -> System.err
}
when(it.severity) {
MessageSeverity.ERROR -> printer.print("\u001b[91mERROR\u001B[0m ") // bright red
MessageSeverity.WARNING -> printer.print("\u001b[93mWARN\u001B[0m ") // bright yellow
}
val msg = "${it.position.toClickableStr()} ${it.message}".trim()
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
}
printer.println(msg)
alreadyReportedMessages.add(msg)
when(it.severity) {

View File

@ -4,10 +4,9 @@ import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.mapError
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.determineGosubArguments
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.ast.*
@ -51,7 +50,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)
@ -81,7 +79,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)
@ -96,7 +93,7 @@ class IntermediateAstMaker(val program: Program) {
return PtNop(srcAssign.position)
}
val assign = PtAssignment(srcAssign.isAugmentable, srcAssign.position)
val assign = PtAssignment(srcAssign.position)
assign.add(transform(srcAssign.target))
assign.add(transformExpression(srcAssign.value))
return assign
@ -131,8 +128,21 @@ class IntermediateAstMaker(val program: Program) {
}
private fun transform(srcBlock: Block): PtBlock {
var alignment = PtBlock.BlockAlignment.NONE
var forceOutput = false
val directives = srcBlock.statements.filterIsInstance<Directive>()
for (directive in directives.filter { it.directive == "%option" }) {
for (arg in directive.args) {
when (arg.name) {
"align_word" -> alignment = PtBlock.BlockAlignment.WORD
"align_page" -> alignment = PtBlock.BlockAlignment.PAGE
"force_output" -> forceOutput=true
else -> throw FatalAstException("weird directive option: ${arg.name}")
}
}
}
val (vardecls, statements) = srcBlock.statements.partition { it is VarDecl }
val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, srcBlock.position)
val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, forceOutput, alignment, srcBlock.position)
if(vardecls.isNotEmpty()) block.add(makeScopeVarsDecls(vardecls, srcBlock.position))
for (stmt in statements)
block.add(transformStatement(stmt))
@ -148,8 +158,9 @@ class IntermediateAstMaker(val program: Program) {
}
private fun transform(srcNode: BuiltinFunctionCallStatement): PtBuiltinFunctionCall {
val type = builtinFunctionReturnType(srcNode.name, srcNode.args, program).getOr(DataType.UNDEFINED)
val call = PtBuiltinFunctionCall(srcNode.name, true, type, srcNode.position)
val type = builtinFunctionReturnType(srcNode.name).getOr(DataType.UNDEFINED)
val noSideFx = BuiltinFunctions.getValue(srcNode.name).pure
val call = PtBuiltinFunctionCall(srcNode.name, true, noSideFx, type, srcNode.position)
for (arg in srcNode.args)
call.add(transformExpression(arg))
return call
@ -184,7 +195,7 @@ class IntermediateAstMaker(val program: Program) {
PtInlineAssembly(assembly, directive.position)
}
else -> {
// other directives don't output any code
// other directives don't output any code (but could end up in option flags somewhere else)
PtNop(directive.position)
}
}
@ -211,8 +222,10 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
val (target, _) = targetOf(srcCall.target)
val type = srcCall.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val call = PtFunctionCall(target, false, type, srcCall.position)
val type = srcCall.inferType(program).getOrElse {
throw FatalAstException("unknown dt $srcCall")
}
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
for (arg in srcCall.args)
call.add(transformExpression(arg))
return call
@ -222,32 +235,10 @@ class IntermediateAstMaker(val program: Program) {
// Gather the Goto and any preceding parameter assignments back into a single Function call node.
// (the reason it was split up in the first place, is because the Compiler Ast optimizers
// can then work on any complex expressions that are used as arguments.)
val parent = gosub.parent as IStatementContainer
val gosubIdx = parent.statements.indexOf(gosub)
val previousNodes = parent.statements.subList(0, gosubIdx).reversed()
val paramValues = mutableMapOf<String, Expression>()
for (node in previousNodes) {
if(node !is Assignment || node.origin!=AssignmentOrigin.PARAMETERASSIGN)
break
paramValues[node.target.identifier!!.nameInSource.last()] = node.value
}
// instead of just assigning to the parameters, another way is to use push()/pop()
if(previousNodes.isNotEmpty()) {
val first = previousNodes[0] as? IFunctionCall
if(first!=null && (first.target.nameInSource.singleOrNull() in arrayOf("pop", "popw"))) {
val numPops = previousNodes.indexOfFirst { (it as? IFunctionCall)?.target?.nameInSource?.singleOrNull() !in arrayOf("pop", "popw") }
val pops = previousNodes.subList(0, numPops)
val pushes = previousNodes.subList(numPops, numPops+numPops).reversed()
for ((push, pop) in pushes.zip(pops)) {
val name = ((pop as IFunctionCall).args.single() as IdentifierReference).nameInSource.last()
val arg = (push as IFunctionCall).args.single()
paramValues[name] = arg
}
}
}
val arguments = determineGosubArguments(gosub)
val parameters = gosub.identifier.targetSubroutine(program)!!.parameters
if(paramValues.size != parameters.size)
if(arguments.size != parameters.size)
throw FatalAstException("mismatched number of parameter assignments for function call")
val target = transform(gosub.identifier)
@ -255,7 +246,7 @@ class IntermediateAstMaker(val program: Program) {
// put arguments in correct order for the parameters
parameters.forEach {
val argument = paramValues.getValue(it.name)
val argument = arguments.getValue(it.name)
call.add(transformExpression(argument))
}
@ -290,15 +281,6 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(label: Label): PtLabel =
PtLabel(label.name, label.position)
private fun transform(srcPipe: Pipe): PtPipe {
val type = srcPipe.segments.last().inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val pipe = PtPipe(type, 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))
@ -431,7 +413,8 @@ class IntermediateAstMaker(val program: Program) {
private fun transform(srcCall: BuiltinFunctionCall): PtBuiltinFunctionCall {
val type = srcCall.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val call = PtBuiltinFunctionCall(srcCall.name, false, type, srcCall.position)
val noSideFx = BuiltinFunctions.getValue(srcCall.name).pure
val call = PtBuiltinFunctionCall(srcCall.name, false, noSideFx, type, srcCall.position)
for (arg in srcCall.args)
call.add(transformExpression(arg))
return call
@ -462,15 +445,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)

View File

@ -4,12 +4,13 @@ import com.github.michaelbull.result.*
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.SyntaxError
import prog8.ast.statements.Block
import prog8.ast.statements.Directive
import prog8.ast.statements.DirectiveArg
import prog8.code.core.IErrorReporter
import prog8.code.core.Position
import prog8.parser.Prog8Parser
import prog8.code.core.SourceCode
import prog8.parser.Prog8Parser
import java.io.File
import java.nio.file.Path
import kotlin.io.path.*
@ -107,6 +108,18 @@ class ModuleImporter(private val program: Program,
)
removeDirectivesFromImportedModule(importedModule)
// modules can contain blocks with "merge" option.
// their content has to be merged into already existing block with the same name.
val blocks = importedModule.statements.filterIsInstance<Block>()
for(block in blocks) {
if("merge" in block.options()) {
val existingBlock = program.allBlocks.first { it.name==block.name}
existingBlock.statements.addAll(block.statements.filter { it !is Directive})
importedModule.statements.remove(block)
}
}
return importedModule
}

View File

@ -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
@ -244,9 +245,18 @@ internal class AstChecker(private val program: Program,
override fun visit(inlineAssembly: InlineAssembly) {
val assembly = inlineAssembly.assembly
if(" rti" in assembly || "\trti" in assembly || " rts" in assembly || "\trts" in assembly ||
" jmp" in assembly || "\tjmp" in assembly || " bra" in assembly || "\tbra" in assembly )
count++
if(compilerOptions.compTarget.name!=VMTarget.NAME) {
if (" rti" in assembly || "\trti" in assembly || " rts" in assembly || "\trts" in assembly ||
" jmp" in assembly || "\tjmp" in assembly || " bra" in assembly || "\tbra" in assembly
)
count++
} else {
if(" return" in assembly || "\treturn" in assembly
|| " jump" in assembly || "\tjump" in assembly
|| " jumpi" in assembly || "\tjumpi" in assembly
)
count++
}
}
}
@ -289,9 +299,6 @@ internal class AstChecker(private val program: Program,
}
}
if(compilerOptions.compTarget.name!=VMTarget.NAME && subroutine.inline && !subroutine.isAsmSubroutine)
err("subroutine inlining is currently only supported on asmsub routines")
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")
@ -640,6 +647,9 @@ internal class AstChecker(private val program: Program,
if(parameter==null)
err("string var must be initialized with a string literal")
}
if(decl.value !is StringLiteral)
err("string var must be initialized with a string literal")
}
if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE)
@ -727,7 +737,7 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty())
err("missing option directive argument(s)")
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it })
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge")}.any { !it })
err("invalid option directive argument(s)")
}
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
@ -854,13 +864,9 @@ internal class AstChecker(private val program: Program,
}
}
"and", "or", "xor" -> {
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
// only integer numeric operands accepted
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
@ -875,9 +881,9 @@ internal class AstChecker(private val program: Program,
if(rightDt!in NumericDatatypes && rightDt != DataType.STR)
errors.err("right operand is not numeric or str", expr.right.position)
if(leftDt!=rightDt) {
if(leftDt==DataType.STR && rightDt in IntegerDatatypes) {
if(leftDt==DataType.STR && rightDt in IntegerDatatypes && expr.operator=="*") {
// only exception allowed: str * constvalue
if(expr.right.constValue(program)!=null)
if(expr.right.constValue(program)==null)
errors.err("can only use string repeat with a constant number value", expr.left.position)
} else {
errors.err("left and right operands aren't the same type", expr.left.position)
@ -986,14 +992,7 @@ internal class AstChecker(private val program: Program,
}
}
else if(targetStatement is BuiltinFunctionPlaceholder) {
val args = if(functionCallExpr.parent is IPipe) {
// pipe segment, add implicit first argument
val firstArgDt = BuiltinFunctions.getValue(targetStatement.name).parameters.first().possibleDatatypes.first()
listOf(defaultZero(firstArgDt, functionCallExpr.position)) + functionCallExpr.args
} else {
functionCallExpr.args
}
if(builtinFunctionReturnType(targetStatement.name, args, program).isUnknown) {
if(builtinFunctionReturnType(targetStatement.name).isUnknown) {
if(functionCallExpr.parent is Expression || functionCallExpr.parent is Assignment)
errors.err("function doesn't return a value", functionCallExpr.position)
}
@ -1006,7 +1005,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
@ -1034,7 +1033,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)
@ -1055,24 +1054,9 @@ 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((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR) {
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
}
if(args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
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)
}
}
@ -1112,19 +1096,6 @@ internal class AstChecker(private val program: Program,
}
}
override fun visit(pipe: PipeExpression) = process(pipe)
override fun visit(pipe: Pipe) = process(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")
}
override fun visit(postIncrDecr: PostIncrDecr) {
if(postIncrDecr.target.identifier != null) {
val targetName = postIncrDecr.target.identifier!!.nameInSource
@ -1157,8 +1128,8 @@ internal class AstChecker(private val program: Program,
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
@ -1496,7 +1467,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()) {
@ -1505,7 +1476,7 @@ internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statem
else
errors.warn("result values of subroutine call are discarded (use void?)", call.position)
} else if (target is BuiltinFunctionPlaceholder) {
val rt = builtinFunctionReturnType(target.name, call.args, program)
val rt = builtinFunctionReturnType(target.name)
if (rt.isKnown)
errors.warn("result value of a function call is discarded (use void?)", call.position)
}

View File

@ -94,7 +94,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)

View File

@ -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
@ -24,6 +23,10 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
errors.err("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)
}
private fun nameShadowWarning(name: String, position: Position, existing: Statement) {
errors.warn("name '$name' shadows occurrence at ${existing.position.file} line ${existing.position.line}", position)
}
override fun visit(block: Block) {
if(block.name in compTarget.machine.opcodeNames)
errors.err("can't use a cpu opcode name as a symbol: '${block.name}'", block.position)
@ -50,9 +53,17 @@ 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.definingScope.lookup(listOf(decl.name))
if (existing != null && existing !== decl)
nameError(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, existingOuter)
}
if(decl.definingBlock.name==decl.name)
nameError(decl.name, decl.position, decl.definingBlock)
@ -74,8 +85,12 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
// checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
val existing = subroutine.lookup(listOf(subroutine.name))
if (existing != null && existing !== subroutine)
nameError(subroutine.name, subroutine.position, existing)
if (existing != null && existing !== subroutine) {
if(existing.parent!==existing.parent)
nameShadowWarning(subroutine.name, existing.position, subroutine)
else
nameError(subroutine.name, existing.position, subroutine)
}
// check that there are no local symbols (variables, labels, subs) that redefine the subroutine's parameters.
val symbolsInSub = subroutine.allDefinedSymbols
@ -135,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

View File

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

View File

@ -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
@ -8,7 +7,10 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.NumericDatatypes
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
@ -111,67 +113,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))
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 process(pipe, parent)
}
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))
}
return process(pipeExpr, parent)
}
private fun process(pipe: IPipe, parent: Node): Iterable<IAstModification> {
if(pipe.source is IPipe)
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
return noModifications
// TODO don't use artifical inserted args, fix the places that check for arg numbers instead.
// add the "missing" first argument to each function call in the pipe segments
// so that all function call related checks just pass
// might have to remove it again when entering code generation pass, or just replace it there
// with the proper output value of the previous pipe segment.
// val mutations = mutableListOf<IAstModification>()
// var valueDt = pipe.source.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
// pipe.segments.forEach { call->
// val dummyFirstArg = when (valueDt) {
// DataType.UBYTE -> FunctionCallExpression(IdentifierReference(listOf("rnd"), pipe.position), mutableListOf(), pipe.position)
// DataType.UWORD -> FunctionCallExpression(IdentifierReference(listOf("rndw"), pipe.position), mutableListOf(), pipe.position)
// DataType.BYTE, DataType.WORD -> IdentifierReference(
// getTempRegisterName(InferredTypes.InferredType.known(valueDt)),
// pipe.position
// ) // there's no builtin function we can abuse that returns a signed byte or word type // TODO maybe use a typecasted expression around rnd?
// DataType.FLOAT -> FunctionCallExpression(IdentifierReference(listOf("rndf"), pipe.position), mutableListOf(), pipe.position)
// else -> throw FatalAstException("invalid dt")
// }
//
// mutations += IAstModification.SetExpression(
// { newexpr -> call.args.add(0, newexpr) },
// dummyFirstArg, parent
// )
//
// if(call!==pipe.segments.last())
// valueDt = call.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
// }
// return mutations
}
}

View File

@ -1,123 +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.expressions.StringLiteral
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(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val leftStr = expr.left as? StringLiteral
val rightStr = expr.right as? StringLiteral
if(expr.operator == "+") {
val concatenatedString = concatString(expr)
if(concatenatedString!=null)
return listOf(IAstModification.ReplaceNode(expr, concatenatedString, parent))
}
else if(expr.operator == "*") {
if (leftStr!=null) {
val amount = expr.right.constValue(program)
if(amount!=null) {
val string = leftStr.value.repeat(amount.number.toInt())
val strval = StringLiteral(string, leftStr.encoding, expr.position)
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
}
}
else if (rightStr!=null) {
val amount = expr.right.constValue(program)
if(amount!=null) {
val string = rightStr.value.repeat(amount.number.toInt())
val strval = StringLiteral(string, rightStr.encoding, expr.position)
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
}
}
}
return noModifications
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent)
}
private fun concatString(expr: BinaryExpression): StringLiteral? {
val rightStrval = expr.right as? StringLiteral
val leftStrval = expr.left as? StringLiteral
return when {
expr.operator!="+" -> null
expr.left is BinaryExpression && rightStrval!=null -> {
val subStrVal = concatString(expr.left as BinaryExpression)
if(subStrVal==null)
null
else
StringLiteral("${subStrVal.value}${rightStrval.value}", subStrVal.encoding, rightStrval.position)
}
expr.right is BinaryExpression && leftStrval!=null -> {
val subStrVal = concatString(expr.right as BinaryExpression)
if(subStrVal==null)
null
else
StringLiteral("${leftStrval.value}${subStrVal.value}", subStrVal.encoding, leftStrval.position)
}
leftStrval!=null && rightStrval!=null -> {
StringLiteral("${leftStrval.value}${rightStrval.value}", leftStrval.encoding, leftStrval.position)
}
else -> null
}
}
}
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()
}

View File

@ -7,7 +7,6 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
import prog8.code.ast.PtIdentifier
import prog8.code.core.*
import prog8.code.target.VMTarget
@ -121,11 +120,17 @@ 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.
// and if an assembly block doesn't contain a rts/rti, and some other situations.
if (!subroutine.isAsmSubroutine && (!subroutine.inline || !options.optimize)) {
if (!subroutine.isAsmSubroutine) {
if(subroutine.statements.isEmpty() ||
(subroutine.amountOfRtsInAsm() == 0
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
@ -228,11 +233,9 @@ internal class BeforeAsmAstChanger(val program: Program,
// TODO: somehow figure out if the expr will result in stack-evaluation STILL after being split off,
// in that case: do *not* split it off but just keep it as it is (otherwise code size increases)
// TODO: this should be replaced by a general expression-evaluation optimization step.
// the actual conditional expression in the statement should be no more than VARIABLE <COMPARISON-OPERATOR> SIMPLE-EXPRESSION
// 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)
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimization for Vm target
return CondExprSimplificationResult(null, null, null, null)
var leftAssignment: Assignment? = null
@ -282,7 +285,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
@ -346,5 +349,4 @@ internal class BeforeAsmAstChanger(val program: Program,
)
return modifications
}
}

View File

@ -18,7 +18,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
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

View File

@ -4,6 +4,7 @@ import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.VarDecl
@ -27,6 +28,10 @@ internal class LiteralsToAutoVars(private val program: Program,
return noModifications
}
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
val binExpr = string.parent as? BinaryExpression
if(binExpr!=null &&(binExpr.operator=="+" || binExpr.operator=="*"))
return noModifications // allow string concatenation or repeats later, based on just string literals
// replace the literal string by an identifier reference to the interned string
val parentFunc = (string.parent as? IFunctionCall)?.target
if(parentFunc!=null) {

View File

@ -189,21 +189,15 @@ internal class StatementReorderer(val program: Program,
return modifications + parameterChanges + varsChanges
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(parent !is VarDecl) {
// don't replace the initializer value in a vardecl - this will be moved to a separate
// assignment statement soon in after(VarDecl)
return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent)
}
return noModifications
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
if (expr.left.constValue(program) != null && expr.operator in AssociativeOperators && expr.right.constValue(program) == null)
if (expr.left.constValue(program) != null
&& expr.operator in AssociativeOperators
&& expr.right.constValue(program) == null
&& maySwapOperandOrder(expr))
return listOf(IAstModification.SwapOperands(expr))
// when using a simple bit shift and assigning it to a variable of a different type,
@ -254,12 +248,12 @@ internal class StatementReorderer(val program: Program,
}
}
else if(expr.operator in LogicalOperators) {
// make sure that logical expressions like "var and other-logical-expression
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
// generating the wrong results later
fun wrapped(expr: Expression): Expression =
BinaryExpression(expr, "!=", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
fun wrapped(expr: Expression): Expression {
return if(expr.inferType(program).isBytes)
expr
else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
}
fun isLogicalExpr(expr: Expression?): Boolean {
if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))
@ -316,6 +310,9 @@ internal class StatementReorderer(val program: Program,
}
}
if(valueType.isString && (targetType istype DataType.STR || targetType istype DataType.ARRAY_B || targetType istype DataType.ARRAY_UB))
return copyStringValue(assignment)
return noModifications
}
@ -328,7 +325,7 @@ internal class StatementReorderer(val program: Program,
return noModifications
}
if(binExpr.operator in AssociativeOperators) {
if(binExpr.operator in AssociativeOperators && maySwapOperandOrder(binExpr)) {
if (binExpr.right isSameAs assignment.target) {
// A = v <associative-operator> A ==> A = A <associative-operator> v
return listOf(IAstModification.SwapOperands(binExpr))
@ -409,9 +406,23 @@ internal class StatementReorderer(val program: Program,
return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent))
}
private fun copyStringValue(assign: Assignment): List<IAstModification> {
val identifier = assign.target.identifier!!
val strcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "internal_stringcopy"), assign.position),
mutableListOf(
assign.value as? IdentifierReference ?: assign.value,
identifier
),
true,
assign.position
)
return listOf(IAstModification.ReplaceNode(assign, strcopy, assign.parent))
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val function = functionCallStatement.target.targetStatement(program)!!
checkUnusedReturnValues(functionCallStatement, function, program, errors)
val function = functionCallStatement.target.targetStatement(program)
?: throw FatalAstException("no target for $functionCallStatement")
checkUnusedReturnValues(functionCallStatement, function, errors)
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
}
}

View File

@ -7,7 +7,6 @@ import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.*
import prog8.code.core.ArrayDatatypes
import prog8.code.core.ElementToArrayTypes
import prog8.code.core.Position
import java.util.*

View File

@ -114,6 +114,10 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
return noModifications
if((assignment.value as? BinaryExpression)?.operator in ComparisonOperators) {
// special case, treat a boolean comparison result as the same type as the target value to avoid needless casts later
return noModifications
}
val modifications = mutableListOf<IAstModification>()
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
return modifications

View File

@ -11,10 +11,7 @@ import prog8.ast.statements.Assignment
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.ArrayDatatypes
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.*
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
@ -62,7 +59,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val nextAssign = assignment.nextSibling() as? Assignment
if(nextAssign!=null && nextAssign.target.isSameAs(assignment.target, program)) {
if(nextAssign.value isSameAs assignment.value && assignment.value !is IFunctionCall) // don't remove function calls even when they're duplicates
if(!nextAssign.isAugmentable && nextAssign.value isSameAs assignment.value && assignment.value !is IFunctionCall) // don't remove function calls even when they're duplicates
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
}
@ -94,13 +91,47 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
override fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// try to replace a multi-comparison expression (if x==1 or x==2 or x==3 ... ) by a simple containment check.
// but only if the containment check is the top-level expression.
if(parent is BinaryExpression)
return noModifications
if(expr.operator == "or") {
val leftBinExpr = expr.left as? BinaryExpression
val rightBinExpr = expr.right as? BinaryExpression
if(leftBinExpr!=null && leftBinExpr.operator=="==" && rightBinExpr!=null && rightBinExpr.operator=="==") {
if(leftBinExpr.right is NumericLiteral && rightBinExpr.right is NumericLiteral) {
if(leftBinExpr.left isSameAs rightBinExpr.left)
errors.warn("consider using 'in' or 'when' to test for multiple values", expr.position)
val leftBinExpr1 = expr.left as? BinaryExpression
val rightBinExpr1 = expr.right as? BinaryExpression
if(rightBinExpr1?.operator=="==" && rightBinExpr1.right is NumericLiteral && leftBinExpr1!=null) {
val needle = rightBinExpr1.left
val values = mutableListOf(rightBinExpr1.right as NumericLiteral)
fun isMultiComparisonRecurse(expr: BinaryExpression): Boolean {
if(expr.operator=="==") {
if(expr.right is NumericLiteral && expr.left isSameAs needle) {
values.add(expr.right as NumericLiteral)
return true
}
return false
}
if(expr.operator!="or")
return false
val leftBinExpr = expr.left as? BinaryExpression
val rightBinExpr = expr.right as? BinaryExpression
if(leftBinExpr==null || rightBinExpr==null || rightBinExpr.right !is NumericLiteral || !rightBinExpr.left.isSameAs(needle))
return false
if(rightBinExpr.operator=="==")
values.add(rightBinExpr.right as NumericLiteral)
else
return false
return isMultiComparisonRecurse(leftBinExpr)
}
if(isMultiComparisonRecurse(leftBinExpr1)) {
// replace it!
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
val elementType = needle.inferType(program).getOrElse { throw FatalAstException("invalid needle dt") }
val arrayType = ElementToArrayTypes.getValue(elementType)
val valuesArray = ArrayLiteral(InferredTypes.InferredType.known(arrayType), valueCopies.toTypedArray(), expr.position)
val containment = ContainmentCheck(needle, valuesArray, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
}
}
}
@ -202,5 +233,14 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
if(functionCallExpr.args[0].inferType(program).isBytes) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
}
return noModifications
}
}

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