Compare commits

...

171 Commits
v8.2 ... v8.5

Author SHA1 Message Date
46dac909ef vm/math.p8: complete the sin and cos routines 2022-09-22 15:49:19 +02:00
b1e4347e10 fix compiler crash sometimes when casting byte to word 2022-09-22 13:00:47 +02:00
97aa91c75e removed 16 bits sin/cos routines from math library (sin16, sin16r etc) 2022-09-22 12:55:00 +02:00
4f8fb32136 some docs about compiler internal architecture 2022-09-21 17:34:52 +02:00
e0fbce0087 few more unittests for IR 2022-09-21 02:59:36 +02:00
fb22f78fb3 added '-keepIR' option to save the IR file if it's generated. 2022-09-20 12:30:22 +02:00
d6393cdbe5 '-vm' option now also reads .p8ir files 2022-09-20 12:14:33 +02:00
5167fdb3f0 docs 2022-09-20 04:10:49 +02:00
ab00822764 move IR optimizer to IR Codegen module 2022-09-19 19:41:43 +02:00
b4352ad38b refactor IR codegen into separate module 2022-09-19 19:24:24 +02:00
d07d00fa41 Join codeAst and codeCore modules 2022-09-19 17:28:18 +02:00
11d87e4725 VM: support cpu registers 2022-09-19 17:13:46 +02:00
627ed51a1b IR: mem mapped vars and memory slabs 2022-09-19 15:20:40 +02:00
c8f3bfa726 vm assembler now understands simple indexed addresses (symbol+number) 2022-09-18 02:17:42 +02:00
3091e3a1c8 IR support for instructions operating on cpu regs 2022-09-18 01:51:04 +02:00
2f3e7d1c27 IR support for storing incbins and romsubs 2022-09-17 16:07:41 +02:00
0e831d4b92 fix superfluous usage of addressOf() 2022-09-16 00:31:04 +02:00
7294ec9a3c working on address-of 2022-09-15 22:44:33 +02:00
e34bab9585 change syntax of address-of in p8virt code to &X, instead of {X} 2022-09-13 23:28:52 +02:00
7dd14955c1 added remaining signature stuff to IRAsmSubroutine 2022-09-13 23:06:05 +02:00
6428ced157 added subroutine params to IRSubroutine 2022-09-13 23:06:05 +02:00
30a42ec1bd IR tweak 2022-09-13 23:06:05 +02:00
aacea3e9db incbin in IR 2022-09-13 23:06:05 +02:00
6886b61186 also output inline asm chunks 2022-09-13 23:06:05 +02:00
0744c9fa29 properly flatten label names for the IR code 2022-09-13 23:06:05 +02:00
502a665ffc getting address-of into IR without allocations 2022-09-13 23:06:05 +02:00
3c315703c0 making IR file reader 2022-09-13 23:06:05 +02:00
12ed07a607 comments 2022-09-13 23:06:05 +02:00
101b33c381 split intermediate representation into separate module 2022-09-13 23:06:05 +02:00
97f4316653 rename IR classes 2022-09-13 23:06:05 +02:00
b0704e86f0 block structure 2022-09-13 23:06:05 +02:00
a182b13e5a fixup for memoryslabs 2022-09-13 23:06:05 +02:00
80b630a1e4 added memoryslabs to symboltable 2022-09-13 23:06:05 +02:00
475efbe007 steps to make actual IR based on VM code. For now, as experimental codegen. 2022-09-13 23:06:05 +02:00
3ab5e5ac48 added cx16.kbdbuf_clear() 2022-09-01 18:40:17 +02:00
c6c5ff2089 added joystick controls to cx16 tehtriz 2022-08-23 18:11:35 +02:00
176ec8ac7d fix 6502 codegen bug: complex comparison expression is evaluated wrong.
Fixed by reintroducing splitting of comparison expression in if statements by using a temporary variable and/or register to precompute left/right values.
2022-08-23 00:05:57 +02:00
dcdd4b3255 found bug in comparison expr codegen 2022-08-22 23:16:56 +02:00
fc0a0105b3 move memoryslab administration from allocator to symboltable 2022-08-21 19:48:56 +02:00
f3960d21a8 fix xmlwriter 2022-08-21 19:12:01 +02:00
a44d853c1b added memoryslabs to symboltable 2022-08-21 19:05:01 +02:00
6b41734d6a check memory() calls before entering codegen 2022-08-21 19:02:34 +02:00
c33dc0f3be version 2022-08-21 14:37:10 +02:00
bb5ffb24a8 add IDEA antlr parser build info to documentation 2022-08-21 13:32:31 +02:00
a878c9a61d add some documentation to the psg module 2022-08-19 22:17:23 +02:00
6454bf8ec4 added mouse cursor to amiga example
slightly sped up text rendering in gfx2 highres mode
2022-08-16 04:25:59 +02:00
40aa733ea7 clearer name 2022-08-15 20:55:35 +02:00
f37a822725 move 2022-08-14 13:17:03 +02:00
f249ccd414 added asm optimization for same pointer index 2022-08-14 12:50:46 +02:00
7ef4ddf0f3 fixed operator precedence: bitwise must come before comparisons 2022-08-14 12:34:00 +02:00
d8e18df3a1 added c64 starfield example 2022-08-14 12:02:23 +02:00
78d3d9d27d vm: get rid of jumpi traces, fix IR value issue with STOREIX 2022-08-13 20:00:13 +02:00
0aa0ec5abd fix c64 zeropage locations of cx16 virtual registers 2022-08-13 00:14:19 +02:00
b6eef3612f added some ported bench8 test programs 2022-08-12 22:08:27 +02:00
666d62dd7a fix cx16.r0 base address to be $04 on the C-64, and fix zeropage duplicate free addresses 2022-08-12 17:49:31 +02:00
44ee4b989f optimize code for logical expressions more if right operand is simple 2022-08-12 00:49:40 +02:00
18790d867c optimize conditional expression WORD & $ff00 to just msb(WORD)&$ff 2022-08-12 00:21:44 +02:00
d6b8936376 fix mkword(@(ptr), 0) wrong asm 2022-08-11 23:01:19 +02:00
4d840c7db8 optimized mkword(0, X) 2022-08-11 22:51:09 +02:00
4d2b21816d optimized uword <<8 and >>8 2022-08-11 22:25:15 +02:00
2d34fdd28f in a block marked option force_output, make all subroutines in asm use .block rather than .proc
this fixes some obscure assembly issues where subroutines were omitted from the output program by 64tass
2022-08-10 21:28:40 +02:00
68abda1219 fix a few small compiler errors (removing functioncall, removing block, assigning virtual register return value) 2022-08-09 23:38:29 +02:00
f778f08f76 tweak 2022-08-08 21:09:49 +02:00
ac1bd2fb7b virtual: properly output "memmapped" variables too
still as regular variables though
2022-08-08 20:42:17 +02:00
4b7b1379d9 also binexpr split on and,or,xor if appropriate 2022-08-08 00:09:18 +02:00
e560e2ab3f vm instructions now contain info on input/output registers 2022-08-07 18:49:16 +02:00
1e441c2ddf tweak vm codegen 2022-08-07 13:45:03 +02:00
93ce74eeb1 removed problematic expression "simplifications" (that introduced arbitrary r9 temp register usage) 2022-08-07 12:26:11 +02:00
f718f4251b working on better encoding of romsub in new ast/vmtarget 2022-08-07 12:21:10 +02:00
4644c9b621 got rid of GoSub ast node and codegen complexity related to that.
sometimes programs get smaller, sometimes bigger.
2022-08-07 03:24:20 +02:00
197081f10d keyboardhandler 2022-08-04 23:04:16 +02:00
00b717cde8 tweak 2022-08-04 18:35:10 +02:00
34aa917ca4 allow bool return type (and arguments) for asmsub / romsub 2022-08-02 23:07:42 +02:00
a38ddcb364 diskio use other filename buffer to avoid always having large buffer 2022-08-02 00:58:32 +02:00
5b9576df4e added diskio.send_command()
diskio now reuses some buffer internally for file names to save some memory
2022-08-01 22:59:27 +02:00
310219e5d7 make sure memory slabs block is at the bottom of the asm file to not allocate needless space in the resulting prg 2022-07-31 15:37:36 +02:00
a0deb463c9 optimized codegen for some equality comparison expressions and some logical expressions 2022-07-31 15:25:54 +02:00
90ddec2ad8 avoid multiple change events in watch mode
added bsieve example
2022-07-31 11:58:27 +02:00
f6b03d5a78 added diskio.diskname(), improved error checking in diskio.directory() 2022-07-30 13:35:42 +02:00
f531daa872 on C64, the cx16.r0...cx16.r15 virtual regs are now in zeropage as well when using kernalsafe or full 2022-07-28 19:13:33 +02:00
046dceb5c2 added optimized case for signed division by 2 2022-07-24 13:59:35 +02:00
dcc1f00048 fix rounding errors in signed divide by power-of-two
The optimized bit-shifting division is removed (for now)
2022-07-24 12:34:55 +02:00
05f935b598 simplify & fix recursion detector 2022-07-22 22:22:43 +02:00
f2d27403c5 add string.endswith() to efficiently test for a suffix without copying
add string.startswith() to efficiently test for string prefix without copying
2022-07-21 00:38:30 +02:00
473efbe67a tweaks 2022-07-17 22:09:56 +02:00
aeabf0f324 nicer colors 2022-07-17 21:37:15 +02:00
80ab552ad8 fix wrong code for signed word >= 0 2022-07-17 19:02:56 +02:00
7d4695c5b2 cx16: graphics module y resolution corrected from 200 to 240. added 'cx16/circles' example. 2022-07-17 18:59:52 +02:00
5189eaca36 move the vm unit tests to codeGenVirtual module and remove virtualmachine dependency in the compiler module 2022-07-17 12:56:22 +02:00
cfb31377fc c64 zeropage: added a few more locations to Kernalsafe free list that should be safe
this makes $02-$21 inclusive, available for use later (x16 virtual registers are placed here on x16...)
2022-07-17 12:12:47 +02:00
a07c52e112 conv.any2uword / conf.hex2uword can now deal with iso lower and upper case letters as well. 2022-07-17 02:39:40 +02:00
8e1071aa89 fix compiler crashes: txt.chrout("a"), uword[] a = ["ls", subroutine] without & before subroutine. 2022-07-15 23:17:03 +02:00
7cb9a6ba60 diskio.status() more robust (stops at newline char instead of overwriting buffer), diskio.f_open better detects error status 2022-07-15 22:21:34 +02:00
350dc731f1 cx16: sys.reset_system() now resets vera fully as well (such as PSG sound), kernal didn't seem to do that 2022-07-14 23:44:53 +02:00
f690f58bd4 callfar() now accepts a variable as address, so it can be used to indirect JSR to a subroutine whose address is not fixed. ('goto' already could indirect JMP to a variable address.) 2022-07-14 19:29:59 +02:00
4bc65e9ef7 fix stack crash in cx16.push_vera_context() 2022-07-14 16:33:09 +02:00
2d600da8b6 fix codegen crash on certain nested typecast 2022-07-13 22:24:31 +02:00
35af53828a fix endless loop in optimizer, fix cx16 register clobbering in psg interrupt handler, fix crash on certain arrays, fix undefined symbol when it's in another imported module 2022-07-13 18:42:06 +02:00
10ddd5b127 fixed missing non-boolean operand cast in logical expressions 2022-07-12 22:28:06 +02:00
f46e131f18 todo 2022-07-12 19:41:51 +02:00
feb5c8be95 vm: some more peephole optimizations 2022-07-12 19:04:19 +02:00
edf12bec71 improve bool params typecasting, fix compiler crash on abs(floatvar) 2022-07-12 17:52:37 +02:00
ff1fc28287 added immediate value vm logical instructions because these are so common 2022-07-12 16:12:32 +02:00
314398ba4c added immediate value vm arithmetic instructions because these are so common 2022-07-12 15:21:26 +02:00
840331347b added a few more vm optimizations and unit tests 2022-07-12 12:42:37 +02:00
6181b12ab8 added -esa option to override the evalstack location, and shift cx16.r0-r15 accordingly 2022-07-11 19:29:04 +02:00
68da661edc optimize comparison to true/1 into comparison to zero, optimize while/until conditions 2022-07-11 16:42:52 +02:00
88cbb6913d tweak bool type handling 2022-07-11 14:55:50 +02:00
7a26646e1b tweak bool type handling 2022-07-11 02:08:12 +02:00
92eb3b0bf6 bool logical testcase 2022-07-09 22:29:38 +02:00
fb63434eee tweak maze example 2022-07-09 22:13:30 +02:00
97f90d9684 Merge branch 'master' into bool_type 2022-07-09 22:09:49 +02:00
f91786367f added maze example 2022-07-09 22:00:46 +02:00
6a57337a68 improved bool type checking 2022-07-08 22:59:35 +02:00
211e2bb37a improved bool type checking 2022-07-08 22:29:13 +02:00
d2d08bf143 fix compiler error about bool vs ubyte 2022-07-08 22:03:05 +02:00
8acb37b6c2 use bool type in examples and libraries 2022-07-08 21:50:32 +02:00
81b3d2db4f fix compiler crash 2022-07-08 21:50:06 +02:00
9633c0b07a added bool to syntax files 2022-07-07 23:30:41 +02:00
1dfa8ee7d8 add ARRAY_BOOL array type 2022-07-07 23:07:30 +02:00
1163543a98 fix bool param lookup problem 2022-07-07 22:23:56 +02:00
bdb7de34be added several compiler checks against weird boolean type use in expressions 2022-07-07 22:23:56 +02:00
9500fc11ac document new bool datatype and removal of boolean() conversion function 2022-07-07 22:23:56 +02:00
65daf29acd fix compiler crash related to word types in certain comparison expressions 2022-07-07 22:23:56 +02:00
298b25cf7d fix compiler crash on certain typecasting assignment 2022-07-07 22:23:56 +02:00
41f4e22a17 introduce BOOL type 2022-07-07 22:23:56 +02:00
288c57c144 ack to allow user to override the following two with command line redefinition: 2022-07-07 22:16:08 +02:00
7ff8923569 document -D command 2022-07-06 23:45:41 +02:00
b41779bd02 added -D command line option to define symbols in the assembly file 2022-07-06 23:40:36 +02:00
beea6bc794 about bool 2022-07-04 20:26:03 +02:00
fee58e98c5 tiny optimization 2022-07-03 13:05:30 +02:00
c51c1da618 psg micro optimizations 2022-07-03 11:55:13 +02:00
ea2812f50f add max volume to psg envelope 2022-07-03 11:26:56 +02:00
3ec05709d5 convert the sounds in cx16 tehtriz to use the psg module instead 2022-07-03 01:40:29 +02:00
4bdac7404a added sustain to psg envelope 2022-07-03 00:55:25 +02:00
cc41218d37 added nicer vm example 2022-07-03 00:41:04 +02:00
4b336b1853 if passing a subroutine or label name as an uword argument, without &, add the addressof automatically 2022-07-02 23:55:32 +02:00
e1c77ce236 fix pop() name scoping 2022-07-02 23:27:08 +02:00
064d412ec8 added cx16.push_vera_context() and cx16.pop_vera_context() for use in irq handlers 2022-07-02 23:13:00 +02:00
7fff4f249d optimize msb(cx16.r0) -> cx16.r0H, lsb(cx16.r0) -> cx16.r0L 2022-07-02 21:38:22 +02:00
7a3745f642 psg tweaks 2022-07-02 20:33:40 +02:00
f8658f6afa precalc vera freq to not use floating point math anymore 2022-07-02 19:40:18 +02:00
223b725a10 psg abstraction and attack/release envelope 2022-07-02 18:47:12 +02:00
25aad8d7be improve const-evaluation of builtin expressions 2022-07-02 16:29:01 +02:00
b2c9b7635d revert restriction on certain associative operator reshuffling
it caused larger generated code
2022-07-02 13:59:24 +02:00
24d13dd120 fix problematic optimizations to logical expressions 2022-07-02 00:56:24 +02:00
965340ff90 logical and/or/xor/not all replaced by bitwise &,|,^,~ (ast, codegens)
this also fixed some invalid outcomes of logical expressions!
2022-07-02 00:38:17 +02:00
8e36fe6bef temporary workaround for code problem around 'not' 2022-07-01 01:01:15 +02:00
2eb41a8caf temporary workaround for code problem around 'not' 2022-07-01 00:38:19 +02:00
fb989ae62f cx16: reset rom/ram/monitor banks at program exit to sane values. 2022-07-01 00:14:38 +02:00
7901ec2a64 "not" no longer in LogicalOperators because it makes assembler generate invalid code somehow 2022-06-30 22:49:27 +02:00
f675dbc726 vm var allocator now also recognises the memory-mapped variables. no longer crashes 2022-06-30 22:09:49 +02:00
2ad4fdbbb9 added cx16 version of bdmusic, needs ADSR though 2022-06-30 21:33:48 +02:00
97cb0cbd08 tweak "not" removal/rewriting 2022-06-30 02:16:30 +02:00
4ca0805de1 bump version 2022-06-29 01:35:14 +02:00
4b358abbb7 "not" operator removed from ast and codegen (it's been replaced with x==0 as equivalent) 2022-06-29 01:13:08 +02:00
dc82a0fc16 better not(x) replacement by x==0 2022-06-28 23:50:23 +02:00
435d6f6f3f vm: and/or/xor/not are all bitwise operations again 2022-06-28 03:17:51 +02:00
ef92451d1a fix logical expressions on arbitrary values, for now with boolean() around the operands 2022-06-28 01:18:36 +02:00
06184bdcb1 get rid of failed mccarthy shortcut evaluation 2022-06-27 21:44:52 +02:00
af98d01053 failed attempt at McCarthy shortcut evaluation 2022-06-27 21:40:48 +02:00
bb1cda0916 fix: boolean values of terms in logical expressions are now properly evaluated 2022-06-26 23:55:34 +02:00
a6d0ea347c bank caching not required for pcm_play() 2022-06-26 22:08:10 +02:00
0fcd57192b cx16diskio.f_read() now correctly deals with banked ram boundary 2022-06-26 21:42:56 +02:00
a6ffa5738b update to kotlin 1.7.0 2022-06-26 18:54:29 +02:00
c75bd97537 update kotest 2022-06-26 18:51:03 +02:00
eea09f4de5 fix invalid asm label sometimes generated for multiple loops in same subroutine 2022-06-24 02:26:45 +02:00
5656ec11d3 fix missing abs(byte) routine 2022-06-24 01:51:54 +02:00
eb53e44cb0 zsound stream test 2022-06-24 01:51:33 +02:00
69f3106062 first vm peephole optimizer 2022-06-22 00:21:06 +02:00
8ab99f6129 zsound combo example 2022-06-21 00:38:59 +02:00
216 changed files with 10820 additions and 5704 deletions

View File

@ -1,21 +1,21 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="io.kotest.assertions.core.jvm" type="repository"> <library name="io.kotest.assertions.core.jvm" type="repository">
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.2.3" /> <properties maven-id="io.kotest:kotest-assertions-core-jvm:5.3.2" />
<CLASSES> <CLASSES>
<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$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/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-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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/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-common-jvm/5.3.2/kotest-common-jvm-5.3.2.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-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -1,20 +1,20 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="io.kotest.property.jvm" type="repository"> <library name="io.kotest.property.jvm" type="repository">
<properties maven-id="io.kotest:kotest-property-jvm:5.2.3" /> <properties maven-id="io.kotest:kotest-property-jvm:5.3.2" />
<CLASSES> <CLASSES>
<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$/io/kotest/kotest-property-jvm/5.3.2/kotest-property-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/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-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.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-common-jvm/5.3.2/kotest-common-jvm-5.3.2.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-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.2.3/kotest-assertions-api-jvm-5.2.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.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/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
</CLASSES> </CLASSES>

View File

@ -1,38 +1,38 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="io.kotest.runner.junit5.jvm" type="repository"> <library name="io.kotest.runner.junit5.jvm" type="repository">
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.2.3" /> <properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.3.2" />
<CLASSES> <CLASSES>
<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-runner-junit5-jvm/5.3.2/kotest-runner-junit5-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.2.3/kotest-framework-api-jvm-5.2.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.3.2/kotest-framework-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.2.3/kotest-assertions-shared-jvm-5.2.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.1/kotlinx-coroutines-test-jvm-1.6.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.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-common-jvm/5.3.2/kotest-common-jvm-5.3.2.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/kotest/kotest-framework-engine-jvm/5.3.2/kotest-framework-engine-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.138/classgraph-4.8.138.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.146/classgraph-4.8.146.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.1/kotlinx-coroutines-debug-1.6.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.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/5.9.0/jna-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.2.3/kotest-framework-discovery-jvm-5.2.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.3.2/kotest-framework-discovery-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.2.3/kotest-assertions-core-jvm-5.2.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/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-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.2.3/kotest-extensions-jvm-5.2.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.3.2/kotest-extensions-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/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/1.12.3/mockk-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.2/mockk-dsl-jvm-1.12.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.12.3/mockk-common-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.2/mockk-dsl-1.12.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.3/mockk-dsl-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.12.2/mockk-common-1.12.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.3/mockk-dsl-jvm-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.2/mockk-agent-jvm-1.12.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.3/mockk-agent-jvm-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.2/mockk-agent-api-1.12.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.3/mockk-agent-api-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.2/mockk-agent-common-1.12.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.3/mockk-agent-common-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/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/1.12.6/byte-buddy-1.12.6.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$/net/bytebuddy/byte-buddy-agent/1.12.6/byte-buddy-agent-1.12.6.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.2.3/kotest-framework-concurrency-jvm-5.2.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.3.2/kotest-framework-concurrency-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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/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/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
@ -41,11 +41,11 @@
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/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-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

2
.idea/misc.xml generated
View File

@ -19,7 +19,7 @@
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<type id="Python" /> <type id="Python" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

3
.idea/modules.xml generated
View File

@ -2,10 +2,10 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/codeAst/codeAst.iml" filepath="$PROJECT_DIR$/codeAst/codeAst.iml" />
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" /> <module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" /> <module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" /> <module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" filepath="$PROJECT_DIR$/codeGenIntermediate/codeGenIntermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" /> <module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" />
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" /> <module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" /> <module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
@ -14,6 +14,7 @@
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" /> <module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" /> <module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" /> <module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" /> <module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" /> <module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
</modules> </modules>

View File

@ -5,7 +5,7 @@ import prog8.code.core.*
/** /**
* Tree structure containing all symbol definitions in the program * Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types) and labels). * (blocks, subroutines, variables (all types), memoryslabs, and labels).
*/ */
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) { class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
fun print() = printIndented(0) fun print() = printIndented(0)
@ -40,6 +40,24 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
vars vars
} }
val allMemMappedVariables: Collection<StMemVar> by lazy {
val vars = mutableListOf<StMemVar>()
fun collect(node: StNode) {
for(child in node.children) {
if(child.value.type== StNodeType.MEMVAR)
vars.add(child.value as StMemVar)
else
collect(child.value)
}
}
collect(this)
vars
}
val allMemorySlabs: Collection<StMemorySlab> by lazy {
children.mapNotNull { if (it.value.type == StNodeType.MEMORYSLAB) it.value as StMemorySlab else null }
}
override fun lookup(scopedName: List<String>) = flat[scopedName] override fun lookup(scopedName: List<String>) = flat[scopedName]
} }
@ -54,7 +72,8 @@ enum class StNodeType {
STATICVAR, STATICVAR,
MEMVAR, MEMVAR,
CONSTANT, CONSTANT,
BUILTINFUNC BUILTINFUNC,
MEMORYSLAB
} }
@ -128,6 +147,7 @@ open class StNode(val name: String,
StNodeType.LABEL -> print("(L) ") StNodeType.LABEL -> print("(L) ")
StNodeType.STATICVAR -> print("(V) ") StNodeType.STATICVAR -> print("(V) ")
StNodeType.MEMVAR -> print("(M) ") StNodeType.MEMVAR -> print("(M) ")
StNodeType.MEMORYSLAB -> print("(MS) ")
StNodeType.CONSTANT -> print("(C) ") StNodeType.CONSTANT -> print("(C) ")
StNodeType.BUILTINFUNC -> print("(F) ") StNodeType.BUILTINFUNC -> print("(F) ")
StNodeType.ROMSUB -> print("(R) ") StNodeType.ROMSUB -> print("(R) ")
@ -149,26 +169,26 @@ open class StNode(val name: String,
class StStaticVariable(name: String, class StStaticVariable(name: String,
val dt: DataType, val dt: DataType,
val initialNumericValue: Double?, val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
val initialStringValue: StString?, val onetimeInitializationStringValue: StString?,
val initialArrayValue: StArray?, val onetimeInitializationArrayValue: StArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish, val zpwish: ZeropageWish,
position: Position) : StNode(name, StNodeType.STATICVAR, position) { position: Position) : StNode(name, StNodeType.STATICVAR, position) {
init { init {
if(length!=null) { if(length!=null) {
require(initialNumericValue == null) require(onetimeInitializationNumericValue == null)
if(initialArrayValue!=null) if(onetimeInitializationArrayValue!=null)
require(length == initialArrayValue.size) require(length == onetimeInitializationArrayValue.size)
} }
if(initialNumericValue!=null) if(onetimeInitializationNumericValue!=null)
require(dt in NumericDatatypes) require(dt in NumericDatatypes)
if(initialArrayValue!=null) if(onetimeInitializationArrayValue!=null)
require(dt in ArrayDatatypes) require(dt in ArrayDatatypes)
if(initialStringValue!=null) { if(onetimeInitializationStringValue!=null) {
require(dt == DataType.STR) require(dt == DataType.STR)
require(length == initialStringValue.first.length+1) require(length == onetimeInitializationStringValue.first.length+1)
} }
} }
@ -186,15 +206,31 @@ class StConstant(name: String, val dt: DataType, val value: Double, position: Po
} }
class StMemVar(name: String, val dt: DataType, val address: UInt, position: Position) : class StMemVar(name: String,
val dt: DataType,
val address: UInt,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
position: Position) :
StNode(name, StNodeType.MEMVAR, position) { StNode(name, StNodeType.MEMVAR, position) {
override fun printProperties() { override fun printProperties() {
print("$name dt=$dt address=${address.toHex()}") print("$name dt=$dt address=${address.toHex()}")
} }
} }
class StMemorySlab(
name: String,
val size: UInt,
val align: UInt,
val allocatedAddress: UInt? = null, // this is used (for now) in the VM code generator. TODO remove this once no longer used
position: Position
):
StNode(name, StNodeType.MEMORYSLAB, position) {
override fun printProperties() {
print("$name size=$size align=$align address=$allocatedAddress")
}
}
class StSub(name: String, val parameters: List<StSubroutineParameter>, position: Position) : class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, position: Position) :
StNode(name, StNodeType.SUBROUTINE, position) { StNode(name, StNodeType.SUBROUTINE, position) {
override fun printProperties() { override fun printProperties() {
print(name) print(name)
@ -202,7 +238,11 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, position:
} }
class StRomSub(name: String, val address: UInt, parameters: List<StSubroutineParameter>, position: Position) : class StRomSub(name: String,
val address: UInt,
val parameters: List<StRomSubParameter>,
val returns: List<RegisterOrStatusflag>,
position: Position) :
StNode(name, StNodeType.ROMSUB, position) { StNode(name, StNodeType.ROMSUB, position) {
override fun printProperties() { override fun printProperties() {
print("$name address=${address.toHex()}") print("$name address=${address.toHex()}")
@ -211,6 +251,7 @@ class StRomSub(name: String, val address: UInt, parameters: List<StSubroutinePar
class StSubroutineParameter(val name: String, val type: DataType) class StSubroutineParameter(val name: String, val type: DataType)
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOf: List<String>?) class StArrayElement(val number: Double?, val addressOf: List<String>?)
typealias StString = Pair<String, Encoding> typealias StString = Pair<String, Encoding>

View File

@ -3,7 +3,7 @@ package prog8.code.ast
import prog8.code.core.* import prog8.code.core.*
import java.nio.file.Path import java.nio.file.Path
// New (work-in-progress) simplified AST for the code generator. // New simplified AST for the code generator.
sealed class PtNode(val position: Position) { sealed class PtNode(val position: Position) {

View File

@ -8,6 +8,21 @@ import kotlin.math.round
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) { sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type==DataType.UNDEFINED) {
@Suppress("LeakingThis")
when(this) {
is PtBuiltinFunctionCall -> { /* void function call */ }
is PtFunctionCall -> { /* void function call */ }
is PtIdentifier -> { /* non-variable identifier */ }
else -> throw IllegalArgumentException("type should be known @$position")
}
}
}
override fun printProperties() { override fun printProperties() {
print(type) print(type)
} }
@ -73,6 +88,7 @@ class PtBuiltinFunctionCall(val name: String,
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) { class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
val left: PtExpression val left: PtExpression
get() = children[0] as PtExpression get() = children[0] as PtExpression
val right: PtExpression val right: PtExpression
@ -126,10 +142,12 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
init { init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type!=DataType.FLOAT) { if(type!=DataType.FLOAT) {
val rounded = round(number) val rounded = round(number)
if (rounded != number) if (rounded != number)
throw IllegalArgumentException("refused rounding of float to avoid loss of precision") throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
} }
} }
@ -153,6 +171,12 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
val value: PtExpression val value: PtExpression
get() = children.single() as PtExpression get() = children.single() as PtExpression
init {
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
if(operator !in setOf("+", "-", "~"))
throw IllegalArgumentException("invalid prefix operator: $operator")
}
override fun printProperties() { override fun printProperties() {
print(operator) print(operator)
} }

View File

@ -8,6 +8,7 @@ class PtAsmSub(
val address: UInt?, val address: UInt?,
val clobbers: Set<CpuRegister>, val clobbers: Set<CpuRegister>,
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>, val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
val returnTypes: List<DataType>, // TODO join with register as Pairs ?
val retvalRegisters: List<RegisterOrStatusflag>, val retvalRegisters: List<RegisterOrStatusflag>,
val inline: Boolean, val inline: Boolean,
position: Position position: Position

View File

@ -20,5 +20,8 @@ class CompilationOptions(val output: OutputType,
var asmQuiet: Boolean = false, var asmQuiet: Boolean = false,
var asmListfile: Boolean = false, var asmListfile: Boolean = false,
var experimentalCodegen: Boolean = false, var experimentalCodegen: Boolean = false,
var outputDir: Path = Path("") var keepIR: Boolean = false,
var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()
) )

View File

@ -6,12 +6,14 @@ enum class DataType {
UWORD, // pass by value UWORD, // pass by value
WORD, // pass by value WORD, // pass by value
FLOAT, // pass by value FLOAT, // pass by value
BOOL, // pass by value
STR, // pass by reference STR, // pass by reference
ARRAY_UB, // pass by reference ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference ARRAY_UW, // pass by reference
ARRAY_W, // pass by reference ARRAY_W, // pass by reference
ARRAY_F, // pass by reference ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference
UNDEFINED; UNDEFINED;
/** /**
@ -19,11 +21,12 @@ enum class DataType {
*/ */
infix fun isAssignableTo(targetType: DataType) = infix fun isAssignableTo(targetType: DataType) =
when(this) { when(this) {
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT) BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT) BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
UWORD -> targetType.oneOf(UWORD, FLOAT) UWORD -> targetType.oneOf(UWORD, FLOAT)
WORD -> targetType.oneOf(WORD, FLOAT) WORD -> targetType.oneOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD) STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this in ArrayDatatypes -> targetType == this
else -> false else -> false
@ -109,18 +112,20 @@ enum class BranchCondition {
} }
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE) val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD) val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD) val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT) val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT) val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F) val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD) val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val IterableDatatypes = arrayOf( val IterableDatatypes = arrayOf(
DataType.STR, DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F DataType.ARRAY_F, DataType.ARRAY_BOOL
) )
val PassByValueDatatypes = NumericDatatypes val PassByValueDatatypes = NumericDatatypes
val PassByReferenceDatatypes = IterableDatatypes val PassByReferenceDatatypes = IterableDatatypes
@ -130,14 +135,16 @@ val ArrayToElementTypes = mapOf(
DataType.ARRAY_UB to DataType.UBYTE, DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD, DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD, DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL
) )
val ElementToArrayTypes = mapOf( val ElementToArrayTypes = mapOf(
DataType.BYTE to DataType.ARRAY_B, DataType.BYTE to DataType.ARRAY_B,
DataType.UBYTE to DataType.ARRAY_UB, DataType.UBYTE to DataType.ARRAY_UB,
DataType.WORD to DataType.ARRAY_W, DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW, DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL
) )
val Cx16VirtualRegisters = arrayOf( val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3, RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,

View File

@ -3,11 +3,6 @@ package prog8.code.core
import java.nio.file.Path import java.nio.file.Path
interface IMachineFloat {
fun toDouble(): Double
fun makeFloatFillAsm(): String
}
enum class CpuType { enum class CpuType {
CPU6502, CPU6502,
CPU65c02, CPU65c02,
@ -18,8 +13,8 @@ interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int val FLOAT_MEM_SIZE: Int
val ESTACK_LO: UInt var ESTACK_LO: UInt
val ESTACK_HI: UInt var ESTACK_HI: UInt
val PROGRAM_LOAD_ADDRESS : UInt val PROGRAM_LOAD_ADDRESS : UInt
val opcodeNames: Set<String> val opcodeNames: Set<String>
@ -27,9 +22,14 @@ interface IMachineDefinition {
val cpu: CpuType val cpu: CpuType
fun initializeZeropage(compilerOptions: CompilationOptions) fun initializeZeropage(compilerOptions: CompilationOptions)
fun getFloat(num: Number): IMachineFloat fun getFloatAsmBytes(num: Number): String
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean fun isIOAddress(address: UInt): Boolean
fun overrideEvalStack(evalStackBaseAddress: UInt) {
require(evalStackBaseAddress and 255u == 0u)
ESTACK_LO = evalStackBaseAddress
ESTACK_HI = evalStackBaseAddress + 256u
}
} }

View File

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

View File

@ -117,4 +117,6 @@ abstract class Zeropage(protected val options: CompilationOptions) {
require(size>0) require(size>0)
return free.containsAll((address until address+size.toUInt()).toList()) return free.containsAll((address until address+size.toUInt()).toList())
} }
abstract fun allocateCx16VirtualRegisters()
} }

View File

@ -15,12 +15,12 @@ class AtariMachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x2000u override val PROGRAM_LOAD_ADDRESS = 0x2000u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = TODO("float from number") override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.output == OutputType.XEX) return if (compilerOptions.output == OutputType.XEX)

View File

@ -12,7 +12,6 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use? override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
init { init {
if (options.floats && options.zeropage !in arrayOf( if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE, ZeropageType.FLOATSAFE,
@ -40,6 +39,14 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
} }
} }
val distictFree = free.distinct()
free.clear()
free.addAll(distictFree)
removeReservedFromFreePool() removeReservedFromFreePool()
} }
override fun allocateCx16VirtualRegisters() {
TODO("Not known if atari can put the virtual regs in ZP")
}
} }

View File

@ -16,12 +16,12 @@ class C128MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x1c01u override val PROGRAM_LOAD_ADDRESS = 0x1c01u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
override val ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)

View File

@ -38,6 +38,14 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
} }
} }
val distictFree = free.distinct()
free.clear()
free.addAll(distictFree)
removeReservedFromFreePool() removeReservedFromFreePool()
} }
override fun allocateCx16VirtualRegisters() {
TODO("Not known if C128 can put the virtual regs in ZP")
}
} }

View File

@ -16,12 +16,12 @@ class C64MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x0801u override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0xce00u // $ce00-$ceff inclusive override var ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
override val ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive override var ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)

View File

@ -1,9 +1,6 @@
package prog8.code.target.c64 package prog8.code.target.c64
import prog8.code.core.CompilationOptions import prog8.code.core.*
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class C64Zeropage(options: CompilationOptions) : Zeropage(options) { class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
@ -29,10 +26,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
} else { } else {
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) { if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
free.addAll(listOf( free.addAll(listOf(
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x16, 0x17, 0x18, 0x19, 0x1a, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
0x22, 0x23, 0x24, 0x25,
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53, 0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
@ -48,8 +44,8 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FLOATSAFE) { if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zeropage locations used for floating point operations from the free list // remove the zeropage locations used for floating point operations from the free list
free.removeAll(listOf( free.removeAll(listOf(
0x22, 0x23, 0x24, 0x25, 0x03, 0x04, 0x10, 0x11, 0x12,
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
@ -60,7 +56,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if(options.zeropage!= ZeropageType.DONTUSE) { if(options.zeropage!= ZeropageType.DONTUSE) {
// add the free Zp addresses // add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O* // these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e, free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6, 0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()}) 0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
} else { } else {
@ -69,6 +65,32 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
} }
} }
val distictFree = free.distinct()
free.clear()
free.addAll(distictFree)
removeReservedFromFreePool() removeReservedFromFreePool()
if(options.zeropage==ZeropageType.FULL || options.zeropage==ZeropageType.KERNALSAFE) {
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
allocateCx16VirtualRegisters()
}
}
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
free.remove((4+reg*2).toUInt())
free.remove((5+reg*2).toUInt())
}
} }
} }

View File

@ -1,12 +1,11 @@
package prog8.code.target.cbm package prog8.code.target.cbm
import prog8.code.core.IMachineFloat
import prog8.code.core.InternalCompilerException import prog8.code.core.InternalCompilerException
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.pow import kotlin.math.pow
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte): IMachineFloat { data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
companion object { companion object {
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255 const val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
@ -58,7 +57,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
} }
} }
override fun toDouble(): Double { fun toDouble(): Double {
if (this == zero) return 0.0 if (this == zero) return 0.0
val exp = b0.toInt() - 128 val exp = b0.toInt() - 128
val sign = (b1.toInt() and 0x80) > 0 val sign = (b1.toInt() and 0x80) > 0
@ -67,7 +66,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
return if (sign) -result else result return if (sign) -result else result
} }
override fun makeFloatFillAsm(): String { fun makeFloatFillAsm(): String {
val b0 = "$" + b0.toString(16).padStart(2, '0') val b0 = "$" + b0.toString(16).padStart(2, '0')
val b1 = "$" + b1.toString(16).padStart(2, '0') val b1 = "$" + b1.toString(16).padStart(2, '0')
val b2 = "$" + b2.toString(16).padStart(2, '0') val b2 = "$" + b2.toString(16).padStart(2, '0')

View File

@ -15,12 +15,12 @@ class CX16MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x0801u override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations) // the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override val ESTACK_LO = 0x0400u // $0400-$04ff inclusive override var ESTACK_LO = 0x0400u // $0400-$04ff inclusive
override val ESTACK_HI = 0x0500u // $0500-$05ff inclusive override var ESTACK_HI = 0x0500u // $0500-$05ff inclusive
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")

View File

@ -43,10 +43,20 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}") else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
} }
val distictFree = free.distinct()
free.clear()
free.addAll(distictFree)
removeReservedFromFreePool() removeReservedFromFreePool()
// note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses. allocateCx16VirtualRegisters()
// however, to be able for the compiler to "see" them as zero page variables, we have to register them here as well. }
}
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) { for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15 allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
@ -57,4 +67,3 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
} }
} }
} }
}

View File

@ -6,6 +6,8 @@ import prog8.code.core.IMachineDefinition
import prog8.code.core.Zeropage import prog8.code.core.Zeropage
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.name
import kotlin.io.path.readText
class VirtualMachineDefinition: IMachineDefinition { class VirtualMachineDefinition: IMachineDefinition {
@ -17,12 +19,12 @@ class VirtualMachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val ESTACK_LO = 0u // not actually used override var ESTACK_LO = 0u // not actually used
override val ESTACK_HI = 0u // not actually used override var ESTACK_HI = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used override lateinit var zeropage: Zeropage // not actually used
override fun getFloat(num: Number) = TODO("float from number") override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return listOf("syslib") return listOf("syslib")
@ -32,9 +34,16 @@ class VirtualMachineDefinition: IMachineDefinition {
println("\nStarting Virtual Machine...") println("\nStarting Virtual Machine...")
// to not have external module dependencies we launch the virtual machine via reflection // 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 vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val source = File("$programNameWithPath.p8virt").readText() val filename = programNameWithPath.name
if(filename.endsWith(".p8virt")) {
vm.runProgram(programNameWithPath.readText(), true)
} else if(File("$filename.p8virt").isFile) {
val source = File("$filename.p8virt").readText()
vm.runProgram(source, true) vm.runProgram(source, true)
} }
else
throw IllegalArgumentException("vm can only run .p8virt or .p8ir files")
}
override fun isIOAddress(address: UInt): Boolean = false override fun isIOAddress(address: UInt): Boolean = false

View File

@ -24,7 +24,6 @@ compileTestKotlin {
} }
dependencies { dependencies {
implementation project(':codeAst')
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':compilerAst') implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"

View File

@ -9,7 +9,6 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeAst" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="compilerAst" /> <orderEntry type="module" module-name="compilerAst" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />

View File

@ -8,6 +8,7 @@ import prog8.ast.Program
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.code.StMemorySlab
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
@ -18,7 +19,6 @@ import kotlin.io.path.Path
import kotlin.io.path.writeLines import kotlin.io.path.writeLines
internal const val generatedLabelPrefix = "prog8_label_"
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1" internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
@ -73,13 +73,6 @@ class AsmGen(internal val program: Program,
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
private var generatedLabelSequenceNumber: Int = 0
internal fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++
return "${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
}
internal fun outputSourceLine(node: Node) { internal fun outputSourceLine(node: Node) {
out(" ;\tsrc line: ${node.position.file}:${node.position.line}") out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
} }
@ -319,13 +312,12 @@ class AsmGen(internal val program: Program,
is Subroutine -> programGen.translateSubroutine(stmt) is Subroutine -> programGen.translateSubroutine(stmt)
is InlineAssembly -> translate(stmt) is InlineAssembly -> translate(stmt)
is BuiltinFunctionCallStatement -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt) is BuiltinFunctionCallStatement -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt)
is FunctionCallStatement -> functioncallAsmGen.translateFunctionCallStatement(stmt) // TODO try to remove this last usage of FunctionCallStatement node in the codegen. is FunctionCallStatement -> functioncallAsmGen.translateFunctionCallStatement(stmt)
is Assignment -> assignmentAsmGen.translate(stmt) is Assignment -> assignmentAsmGen.translate(stmt)
is Jump -> { is Jump -> {
val (asmLabel, indirect) = getJumpTarget(stmt) val (asmLabel, indirect) = getJumpTarget(stmt)
jmp(asmLabel, indirect) jmp(asmLabel, indirect)
} }
is GoSub -> translate(stmt)
is PostIncrDecr -> postincrdecrAsmGen.translate(stmt) is PostIncrDecr -> postincrdecrAsmGen.translate(stmt)
is Label -> translate(stmt) is Label -> translate(stmt)
is ConditionalBranch -> translate(stmt) is ConditionalBranch -> translate(stmt)
@ -449,15 +441,9 @@ class AsmGen(internal val program: Program,
internal fun saveXbeforeCall(functionCall: IFunctionCall) = internal fun saveXbeforeCall(functionCall: IFunctionCall) =
functioncallAsmGen.saveXbeforeCall(functionCall) functioncallAsmGen.saveXbeforeCall(functionCall)
internal fun saveXbeforeCall(gosub: GoSub) =
functioncallAsmGen.saveXbeforeCall(gosub)
internal fun restoreXafterCall(functionCall: IFunctionCall) = internal fun restoreXafterCall(functionCall: IFunctionCall) =
functioncallAsmGen.restoreXafterCall(functionCall) functioncallAsmGen.restoreXafterCall(functionCall)
internal fun restoreXafterCall(gosub: GoSub) =
functioncallAsmGen.restoreXafterCall(gosub)
internal fun translateNormalAssignment(assign: AsmAssignment) = internal fun translateNormalAssignment(assign: AsmAssignment) =
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)
@ -543,7 +529,7 @@ class AsmGen(internal val program: Program,
if(jump is Jump) { if(jump is Jump) {
translateCompareAndJumpIfTrue(booleanCondition, jump) translateCompareAndJumpIfTrue(booleanCondition, jump)
} else { } else {
val endLabel = makeLabel("if_end") val endLabel = program.makeLabel("if_end")
translateCompareAndJumpIfFalse(booleanCondition, endLabel) translateCompareAndJumpIfFalse(booleanCondition, endLabel)
translate(stmt.truepart) translate(stmt.truepart)
out(endLabel) out(endLabel)
@ -551,8 +537,8 @@ class AsmGen(internal val program: Program,
} }
else { else {
// both true and else parts // both true and else parts
val elseLabel = makeLabel("if_else") val elseLabel = program.makeLabel("if_else")
val endLabel = makeLabel("if_end") val endLabel = program.makeLabel("if_end")
translateCompareAndJumpIfFalse(booleanCondition, elseLabel) translateCompareAndJumpIfFalse(booleanCondition, elseLabel)
translate(stmt.truepart) translate(stmt.truepart)
jmp(endLabel) jmp(endLabel)
@ -568,7 +554,7 @@ class AsmGen(internal val program: Program,
} }
private fun translate(stmt: RepeatLoop) { private fun translate(stmt: RepeatLoop) {
val endLabel = makeLabel("repeatend") val endLabel = program.makeLabel("repeatend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
when (stmt.iterations) { when (stmt.iterations) {
@ -621,7 +607,7 @@ class AsmGen(internal val program: Program,
private fun repeatWordCount(count: Int, stmt: RepeatLoop) { private fun repeatWordCount(count: Int, stmt: RepeatLoop) {
require(count in 257..65535) require(count in 257..65535)
val repeatLabel = makeLabel("repeat") val repeatLabel = program.makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) { if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt) val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt)
out(""" out("""
@ -662,7 +648,7 @@ $repeatLabel""")
private fun repeatWordCountInAY(endLabel: String, stmt: RepeatLoop) { private fun repeatWordCountInAY(endLabel: String, stmt: RepeatLoop) {
// note: A/Y must have been loaded with the number of iterations! // note: A/Y must have been loaded with the number of iterations!
// no need to explicitly test for 0 iterations as this is done in the countdown logic below // no need to explicitly test for 0 iterations as this is done in the countdown logic below
val repeatLabel = makeLabel("repeat") val repeatLabel = program.makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt) val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
out(""" out("""
sta $counterVar sta $counterVar
@ -683,7 +669,7 @@ $repeatLabel lda $counterVar
private fun repeatByteCount(count: Int, stmt: RepeatLoop) { private fun repeatByteCount(count: Int, stmt: RepeatLoop) {
require(count in 2..256) require(count in 2..256)
val repeatLabel = makeLabel("repeat") val repeatLabel = program.makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) { if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt) val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
out(" lda #${count and 255} | sta $counterVar") out(" lda #${count and 255} | sta $counterVar")
@ -701,7 +687,7 @@ $repeatLabel lda $counterVar
private fun repeatCountInY(stmt: RepeatLoop, endLabel: String) { private fun repeatCountInY(stmt: RepeatLoop, endLabel: String) {
// note: Y must just have been loaded with the (variable) number of loops to be performed! // note: Y must just have been loaded with the (variable) number of loops to be performed!
val repeatLabel = makeLabel("repeat") val repeatLabel = program.makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) { if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt) val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
out(" beq $endLabel | sty $counterVar") out(" beq $endLabel | sty $counterVar")
@ -738,7 +724,7 @@ $repeatLabel lda $counterVar
} }
} }
val counterVar = makeLabel("counter") val counterVar = program.makeLabel("counter")
when(dt) { when(dt) {
DataType.UBYTE, DataType.UWORD -> { DataType.UBYTE, DataType.UWORD -> {
val result = zeropage.allocate(listOf(counterVar), dt, null, stmt.position, errors) val result = zeropage.allocate(listOf(counterVar), dt, null, stmt.position, errors)
@ -753,7 +739,7 @@ $repeatLabel lda $counterVar
} }
private fun translate(stmt: When) { private fun translate(stmt: When) {
val endLabel = makeLabel("choice_end") val endLabel = program.makeLabel("choice_end")
val choiceBlocks = mutableListOf<Pair<String, AnonymousScope>>() val choiceBlocks = mutableListOf<Pair<String, AnonymousScope>>()
val conditionDt = stmt.condition.inferType(program) val conditionDt = stmt.condition.inferType(program)
if(!conditionDt.isKnown) if(!conditionDt.isKnown)
@ -764,7 +750,7 @@ $repeatLabel lda $counterVar
assignExpressionToRegister(stmt.condition, RegisterOrPair.AY) assignExpressionToRegister(stmt.condition, RegisterOrPair.AY)
for(choice in stmt.choices) { for(choice in stmt.choices) {
val choiceLabel = makeLabel("choice") val choiceLabel = program.makeLabel("choice")
if(choice.values==null) { if(choice.values==null) {
// the else choice // the else choice
translate(choice.statements) translate(choice.statements)
@ -828,14 +814,14 @@ $repeatLabel lda $counterVar
} else { } else {
if(stmt.elsepart.isEmpty()) { if(stmt.elsepart.isEmpty()) {
val instruction = branchInstruction(stmt.condition, true) val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else") val elseLabel = program.makeLabel("branch_else")
out(" $instruction $elseLabel") out(" $instruction $elseLabel")
translate(stmt.truepart) translate(stmt.truepart)
out(elseLabel) out(elseLabel)
} else { } else {
val instruction = branchInstruction(stmt.condition, true) val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else") val elseLabel = program.makeLabel("branch_else")
val endLabel = makeLabel("branch_end") val endLabel = program.makeLabel("branch_end")
out(" $instruction $elseLabel") out(" $instruction $elseLabel")
translate(stmt.truepart) translate(stmt.truepart)
jmp(endLabel) jmp(endLabel)
@ -880,18 +866,6 @@ $repeatLabel lda $counterVar
} }
} }
private fun translate(gosub: GoSub) {
val tgt = gosub.identifier.targetSubroutine(program)
if(tgt!=null && tgt.isAsmSubroutine) {
// no need to rescue X , this has been taken care of already
out(" jsr ${getJumpTarget(gosub)}")
} else {
saveXbeforeCall(gosub)
out(" jsr ${getJumpTarget(gosub)}")
restoreXafterCall(gosub)
}
}
private fun getJumpTarget(jump: Jump): Pair<String, Boolean> { private fun getJumpTarget(jump: Jump): Pair<String, Boolean> {
val ident = jump.identifier val ident = jump.identifier
val label = jump.generatedLabel val label = jump.generatedLabel
@ -911,8 +885,6 @@ $repeatLabel lda $counterVar
} }
} }
private fun getJumpTarget(gosub: GoSub): String = asmSymbolName(gosub.identifier)
private fun translate(ret: Return, withRts: Boolean=true) { private fun translate(ret: Return, withRts: Boolean=true) {
ret.value?.let { returnvalue -> ret.value?.let { returnvalue ->
val sub = ret.definingSubroutine!! val sub = ret.definingSubroutine!!
@ -1173,14 +1145,14 @@ $repeatLabel lda $counterVar
return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel) return testVariableZeroAndJump(left, dt, operator, jumpIfFalseLabel)
when(dt) { when(dt) {
DataType.UBYTE, DataType.UWORD -> { DataType.BOOL, DataType.UBYTE, DataType.UWORD -> {
if(operator=="<") { if(operator=="<") {
out(" jmp $jumpIfFalseLabel") out(" jmp $jumpIfFalseLabel")
return return
} else if(operator==">=") { } else if(operator==">=") {
return return
} }
if(dt==DataType.UBYTE) { if(dt==DataType.UBYTE || dt==DataType.BOOL) {
assignExpressionToRegister(left, RegisterOrPair.A, false) assignExpressionToRegister(left, RegisterOrPair.A, false)
if (left is IFunctionCall && !left.isSimple) if (left is IFunctionCall && !left.isSimple)
out(" cmp #0") out(" cmp #0")
@ -1260,7 +1232,7 @@ $repeatLabel lda $counterVar
// optimized code if the expression is just an identifier (variable) // optimized code if the expression is just an identifier (variable)
val varname = asmVariableName(variable) val varname = asmVariableName(variable)
when(dt) { when(dt) {
DataType.UBYTE -> when(operator) { DataType.UBYTE, DataType.BOOL -> when(operator) {
"==" -> out(" lda $varname | bne $jumpIfFalseLabel") "==" -> out(" lda $varname | bne $jumpIfFalseLabel")
"!=" -> out(" lda $varname | beq $jumpIfFalseLabel") "!=" -> out(" lda $varname | beq $jumpIfFalseLabel")
">" -> out(" lda $varname | beq $jumpIfFalseLabel") ">" -> out(" lda $varname | beq $jumpIfFalseLabel")
@ -2882,7 +2854,7 @@ $repeatLabel lda $counterVar
} }
} }
} else { } else {
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, target.datatype, scope, variableAsmName = asmVariableName(target.name)) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, this, target.datatype, scope, variableAsmName = asmVariableName(target.scopedName))
if (dt in ByteDatatypes) { if (dt in ByteDatatypes) {
out(" pla") out(" pla")
assignRegister(RegisterOrPair.A, tgt) assignRegister(RegisterOrPair.A, tgt)
@ -2925,6 +2897,12 @@ $repeatLabel lda $counterVar
else else
extra extra
} }
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
val prefixedName = "prog8_memoryslab_$name"
symbolTable.add(StMemorySlab(prefixedName, size, align, null, position))
return prefixedName
}
} }

View File

@ -59,7 +59,14 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++ numberOfOptimizations++
} }
// TODO more assembly optimizations mods = optimizeSamePointerIndexing(linesByFourteen, machine, program)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14)
numberOfOptimizations++
}
// TODO more assembly peephole optimizations
return numberOfOptimizations return numberOfOptimizations
} }
@ -320,6 +327,48 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
return mods return mods
} }
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
// if Y isn't modified in between we can omit the second LDY:
// ldy #0
// lda (ptr),y
// ora #3 ; <-- instruction(s) that don't modify Y
// ldy #0 ; <-- can be removed
// sta (ptr),y
val mods = mutableListOf<Modification>()
for (lines in linesByFourteen) {
val first = lines[0].value.trimStart()
val second = lines[1].value.trimStart()
val third = lines[2].value.trimStart()
val fourth = lines[3].value.trimStart()
val fifth = lines[4].value.trimStart()
val sixth = lines[5].value.trimStart()
if(first.startsWith("ldy") && second.startsWith("lda") && fourth.startsWith("ldy") && fifth.startsWith("sta")) {
val firstvalue = first.substring(4)
val secondvalue = second.substring(4)
val fourthvalue = fourth.substring(4)
val fifthvalue = fifth.substring(4)
if("y" !in third && firstvalue==fourthvalue && secondvalue==fifthvalue && secondvalue.endsWith(",y") && fifthvalue.endsWith(",y")) {
mods.add(Modification(lines[3].index, true, null))
}
}
if(first.startsWith("ldy") && second.startsWith("lda") && fifth.startsWith("ldy") && sixth.startsWith("sta")) {
val firstvalue = first.substring(4)
val secondvalue = second.substring(4)
val fifthvalue = fifth.substring(4)
val sixthvalue = sixth.substring(4)
if("y" !in third && "y" !in fourth && firstvalue==fifthvalue && secondvalue==sixthvalue && secondvalue.endsWith(",y") && sixthvalue.endsWith(",y")) {
mods.add(Modification(lines[4].index, true, null))
}
}
}
return mods
}
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> { private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()

View File

@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError import com.github.michaelbull.result.mapError
import prog8.ast.generatedLabelPrefix
import prog8.code.core.* import prog8.code.core.*
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path

View File

@ -44,7 +44,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope) "abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope) "any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"boolean" -> funcBoolean(fcall, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope) "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope) "sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
@ -140,20 +139,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
throw AssemblyError("callfar only works on cx16 target at this time") throw AssemblyError("callfar only works on cx16 target at this time")
val bank = fcall.args[0].constValue(program)?.number?.toInt() val bank = fcall.args[0].constValue(program)?.number?.toInt()
val address = fcall.args[1].constValue(program)?.number?.toInt() val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0
if(bank==null || address==null)
throw AssemblyError("callfar (jsrfar) requires constant arguments")
if(address !in 0xa000..0xbfff)
throw AssemblyError("callfar done on address outside of cx16 banked ram")
if(bank==0)
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
val argAddrArg = fcall.args[2] val argAddrArg = fcall.args[2]
if(bank==null)
throw AssemblyError("callfar (jsrfar) bank has to be a constant")
if(fcall.args[1].constValue(program) == null) {
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word")
}
if(argAddrArg.constValue(program)?.number == 0.0) { if(argAddrArg.constValue(program)?.number == 0.0) {
asmgen.out(""" asmgen.out("""
jsr cx16.jsrfar jsr cx16.jsrfar
.word ${address.toHex()} + .word ${address.toHex()}
.byte ${bank.toHex()}""") .byte ${bank.toHex()}""")
} else { } else {
when(argAddrArg) { when(argAddrArg) {
@ -163,7 +161,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(""" asmgen.out("""
lda ${asmgen.asmVariableName(argAddrArg.identifier)} lda ${asmgen.asmVariableName(argAddrArg.identifier)}
jsr cx16.jsrfar jsr cx16.jsrfar
.word ${address.toHex()} + .word ${address.toHex()}
.byte ${bank.toHex()} .byte ${bank.toHex()}
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""") sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
} }
@ -171,7 +169,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(""" asmgen.out("""
lda ${argAddrArg.number.toHex()} lda ${argAddrArg.number.toHex()}
jsr cx16.jsrfar jsr cx16.jsrfar
.word ${address.toHex()} + .word ${address.toHex()}
.byte ${bank.toHex()} .byte ${bank.toHex()}
sta ${argAddrArg.number.toHex()}""") sta ${argAddrArg.number.toHex()}""")
} }
@ -313,22 +311,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"} require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val size = (fcall.args[1] as NumericLiteral).number.toUInt() val size = (fcall.args[1] as NumericLiteral).number.toUInt()
val align = (fcall.args[2] as NumericLiteral).number.toUInt() val align = (fcall.args[2] as NumericLiteral).number.toUInt()
val prefixedName = asmgen.addMemorySlab(name, size, align, fcall.position)
val existing = allocations.getMemorySlab(name) val slabname = IdentifierReference(listOf("prog8_slabs", prefixedName), fcall.position)
if(existing!=null && (existing.first!=size || existing.second!=align))
throw AssemblyError("memory slab '$name' already exists with a different size or alignment at ${fcall.position}")
val slabname = IdentifierReference(listOf("prog8_slabs", name), fcall.position)
slabname.linkParents(fcall) slabname.linkParents(fcall)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position)) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
val target = val target =
if(resultToStack) if(resultToStack)
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null) AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
else else
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, asmgen) AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen)
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position) val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
allocations.allocateMemorySlab(name, size, align)
} }
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
@ -337,7 +330,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" jsr prog8_lib.func_sqrt16_stack") asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else { else {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A") asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
} }
} }
@ -650,7 +643,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A") DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
} }
} }
@ -671,7 +664,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_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") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
} }
} }
@ -694,32 +687,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY") DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, 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?) { private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) { when(func.name) {
"rnd" -> { "rnd" -> {
@ -727,7 +698,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" jsr prog8_lib.func_rnd_stack") asmgen.out(" jsr prog8_lib.func_rnd_stack")
else { else {
asmgen.out(" jsr math.randbyte") asmgen.out(" jsr math.randbyte")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
} }
} }
"rndw" -> { "rndw" -> {
@ -735,7 +706,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out(" jsr prog8_lib.func_rndw_stack") asmgen.out(" jsr prog8_lib.func_rndw_stack")
else { else {
asmgen.out(" jsr math.randword") asmgen.out(" jsr math.randword")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY)
} }
} }
else -> throw AssemblyError("wrong func") else -> throw AssemblyError("wrong func")
@ -889,9 +860,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
val mr0 = fcall.args[0] as? DirectMemoryRead val mr0 = fcall.args[0] as? DirectMemoryRead
val mr1 = fcall.args[1] as? DirectMemoryRead val mr1 = fcall.args[1] as? DirectMemoryRead
if (mr0 != null) if (mr0 != null)
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference needAsave = mr0.addressExpression !is NumericLiteral
if (mr1 != null) if (mr1 != null)
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral && mr1.addressExpression !is IdentifierReference) needAsave = needAsave or (mr1.addressExpression !is NumericLiteral)
} }
when(reg) { when(reg) {
RegisterOrPair.AX -> { RegisterOrPair.AX -> {
@ -1108,7 +1079,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
AsmAssignSource.fromAstSource(value, program, asmgen) AsmAssignSource.fromAstSource(value, program, asmgen)
} }
} }
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
@ -1124,7 +1095,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
AsmAssignSource.fromAstSource(value, program, asmgen) AsmAssignSource.fromAstSource(value, program, asmgen)
} }
} }
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen)
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }

View File

@ -136,7 +136,7 @@ internal class ExpressionsAsmGen(private val program: Program,
private fun translateExpression(typecast: TypecastExpression) { private fun translateExpression(typecast: TypecastExpression) {
translateExpressionInternal(typecast.expression) translateExpressionInternal(typecast.expression)
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) { when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> { DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) { when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {} DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
@ -520,11 +520,36 @@ internal class ExpressionsAsmGen(private val program: Program,
if(rightVal!=null && rightVal==2) { if(rightVal!=null && rightVal==2) {
translateExpressionInternal(expr.left) translateExpressionInternal(expr.left)
when (leftDt) { when (leftDt) {
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x") DataType.UBYTE -> {
DataType.BYTE -> asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") asmgen.out(" lsr P8ESTACK_LO+1,x")
DataType.UWORD -> asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
DataType.WORD -> asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") DataType.UWORD -> {
else -> throw AssemblyError("wrong dt") asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
}
DataType.BYTE -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_LO+1,x
bpl +
inc P8ESTACK_LO+1,x
lda P8ESTACK_LO+1,x
+ asl a
ror P8ESTACK_LO+1,x""")
}
DataType.WORD -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_HI+1,x
bpl ++
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+ lda P8ESTACK_HI+1,x
+ asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x""")
}
else -> throw AssemblyError("weird dt")
} }
return return
} }
@ -532,8 +557,8 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
in ComparisonOperators -> { in ComparisonOperators -> {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) { if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.constValue(program)?.number?.toInt() val rightVal = expr.right.constValue(program)?.number
if(rightVal==0) if(rightVal==0.0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator) return translateComparisonWithZero(expr.left, leftDt, expr.operator)
} }
} }
@ -560,6 +585,42 @@ internal class ExpressionsAsmGen(private val program: Program,
} }
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) { private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
if(expr.isSimple) {
if(operator=="!=") {
when (dt) {
in ByteDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
asmgen.out("""
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
in WordDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
DataType.FLOAT -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
asmgen.out("""
jsr floats.SIGN
sta P8ESTACK_LO,x
dex""")
return
}
else -> {}
}
}
/* operator == is not worth it to special case, the code is mostly larger */
}
translateExpressionInternal(expr) translateExpressionInternal(expr)
when(operator) { when(operator) {
"==" -> { "==" -> {
@ -664,22 +725,6 @@ internal class ExpressionsAsmGen(private val program: Program,
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
"not" -> {
when(type) {
// if reg==0 ->
/*
lda P8ESTACK_LO+1,x
beq +
lda #1
+ eor #1
sta P8ESTACK_LO+1,x
rts
*/
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.not_byte")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.not_word")
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("invalid prefix operator ${expr.operator}") else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
} }
} }
@ -784,9 +829,6 @@ internal class ExpressionsAsmGen(private val program: Program,
"&" -> asmgen.out(" jsr prog8_lib.bitand_b") "&" -> asmgen.out(" jsr prog8_lib.bitand_b")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b") "^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
"|" -> asmgen.out(" jsr prog8_lib.bitor_b") "|" -> asmgen.out(" jsr prog8_lib.bitor_b")
"and" -> asmgen.out(" jsr prog8_lib.and_b")
"or" -> asmgen.out(" jsr prog8_lib.or_b")
"xor" -> asmgen.out(" jsr prog8_lib.xor_b")
else -> throw AssemblyError("invalid operator $operator") else -> throw AssemblyError("invalid operator $operator")
} }
} }
@ -818,9 +860,6 @@ internal class ExpressionsAsmGen(private val program: Program,
"&" -> asmgen.out(" jsr prog8_lib.bitand_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w") "^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
"|" -> asmgen.out(" jsr prog8_lib.bitor_w") "|" -> asmgen.out(" jsr prog8_lib.bitor_w")
"and" -> asmgen.out(" jsr prog8_lib.and_w")
"or" -> asmgen.out(" jsr prog8_lib.or_w")
"xor" -> asmgen.out(" jsr prog8_lib.xor_w")
else -> throw AssemblyError("invalid operator $operator") else -> throw AssemblyError("invalid operator $operator")
} }
} }
@ -837,7 +876,7 @@ internal class ExpressionsAsmGen(private val program: Program,
">=" -> asmgen.out(" jsr floats.greatereq_f") ">=" -> asmgen.out(" jsr floats.greatereq_f")
"==" -> asmgen.out(" jsr floats.equal_f") "==" -> asmgen.out(" jsr floats.equal_f")
"!=" -> asmgen.out(" jsr floats.notequal_f") "!=" -> asmgen.out(" jsr floats.notequal_f")
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype") "%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
else -> throw AssemblyError("invalid operator $operator") else -> throw AssemblyError("invalid operator $operator")
} }
} }

View File

@ -31,10 +31,10 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
} }
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) { private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = program.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = program.makeLabel("for_end")
val modifiedLabel = asmgen.makeLabel("for_modified") val modifiedLabel = program.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb") val modifiedLabel2 = program.makeLabel("for_modifiedb")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val stepsize=range.step.constValue(program)!!.number.toInt() val stepsize=range.step.constValue(program)!!.number.toInt()
@ -238,8 +238,8 @@ $endLabel""")
} }
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) { private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = program.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident) val iterableName = asmgen.asmVariableName(ident)
val decl = ident.targetVarDecl(program)!! val decl = ident.targetVarDecl(program)!!
@ -263,7 +263,7 @@ $endLabel""")
} }
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
val length = decl.arraysize!!.constIndex()!! val length = decl.arraysize!!.constIndex()!!
val indexVar = asmgen.makeLabel("for_index") val indexVar = program.makeLabel("for_index")
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
$loopLabel sty $indexVar $loopLabel sty $indexVar
@ -299,7 +299,7 @@ $loopLabel sty $indexVar
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
val length = decl.arraysize!!.constIndex()!! * 2 val length = decl.arraysize!!.constIndex()!! * 2
val indexVar = asmgen.makeLabel("for_index") val indexVar = program.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.loopVar) val loopvarName = asmgen.asmVariableName(stmt.loopVar)
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
@ -359,8 +359,8 @@ $loopLabel sty $indexVar
} }
// not one of the easy cases, generate more complex code... // not one of the easy cases, generate more complex code...
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = program.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
when(iterableDt) { when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
@ -471,8 +471,8 @@ $loopLabel""")
} }
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = program.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out(""" asmgen.out("""
@ -497,8 +497,8 @@ $endLabel""")
} }
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = program.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out(""" asmgen.out("""
@ -534,8 +534,8 @@ $endLabel""")
} }
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = program.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out(""" asmgen.out("""
@ -561,8 +561,8 @@ $loopLabel""")
} }
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) { private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = program.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = program.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar) val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out(""" asmgen.out("""

View File

@ -3,8 +3,14 @@ package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.expressions.* import prog8.ast.expressions.AddressOf
import prog8.ast.statements.* import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.Subroutine
import prog8.ast.statements.SubroutineParameter
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignSource import prog8.codegen.cpu6502.assignment.AsmAssignSource
import prog8.codegen.cpu6502.assignment.AsmAssignTarget import prog8.codegen.cpu6502.assignment.AsmAssignTarget
@ -32,17 +38,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} }
} }
internal fun saveXbeforeCall(gosub: GoSub) {
val sub = gosub.identifier.targetSubroutine(program)
if(sub?.shouldSaveX()==true) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if(regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, gosub.definingSubroutine!!)
}
}
internal fun restoreXafterCall(stmt: IFunctionCall) { internal fun restoreXafterCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) { if(sub.shouldSaveX()) {
@ -54,17 +49,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} }
} }
internal fun restoreXafterCall(gosub: GoSub) {
val sub = gosub.identifier.targetSubroutine(program)
if(sub?.shouldSaveX()==true) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if(regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
internal fun optimizeIntArgsViaRegisters(sub: Subroutine) = internal fun optimizeIntArgsViaRegisters(sub: Subroutine) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes) (sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) || (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
@ -78,11 +62,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}") val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
val subAsmName = asmgen.asmSymbolName(call.target) val subAsmName = asmgen.asmSymbolName(call.target)
if(!isExpression && !sub.isAsmSubroutine) {
if(!optimizeIntArgsViaRegisters(sub))
throw AssemblyError("functioncall statements to non-asmsub should have been replaced by GoSub $call")
}
if(sub.isAsmSubroutine) { if(sub.isAsmSubroutine) {
argumentsViaRegisters(sub, call) argumentsViaRegisters(sub, call)
if (sub.inline && asmgen.options.optimize) { if (sub.inline && asmgen.options.optimize) {
@ -156,7 +135,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} }
argOrder.forEach { argOrder.forEach {
val param = callee.parameters[it] val param = callee.parameters[it]
val targetVar = callee.searchAsmParameter(param.name)!! val targetVar = callee.searchParameter(param.name)!!
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine) asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine)
} }
} }
@ -231,10 +210,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} else { } else {
val target: AsmAssignTarget = val target: AsmAssignTarget =
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, sub, register = register)
else { else {
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
AsmAssignTarget.fromRegisters(register, signed, sub, program, asmgen) AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
} }
val src = if(valueDt in PassByReferenceDatatypes) { val src = if(valueDt in PassByReferenceDatatypes) {
if(value is IdentifierReference) { if(value is IdentifierReference) {

View File

@ -44,6 +44,15 @@ internal class ProgramAndVarsGen(
if(errors.noErrors()) { if(errors.noErrors()) {
program.allBlocks.forEach { block2asm(it) } program.allBlocks.forEach { block2asm(it) }
// the global list of all floating point constants for the whole program
asmgen.out("; global float constants")
for (flt in allocator.globalFloatConsts) {
val floatFill = compTarget.machine.getFloatAsmBytes(flt.key)
val floatvalue = flt.key
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
}
memorySlabs() memorySlabs()
footer() footer()
} }
@ -70,8 +79,17 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}") asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word") asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word") asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}") asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}") asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
asmgen.out(".endweak")
if(options.symbolDefs.isNotEmpty()) {
asmgen.out("; -- user supplied symbols on the command line")
for((name, value) in options.symbolDefs) {
asmgen.out("$name = $value")
}
}
when(options.output) { when(options.output) {
OutputType.RAW -> { OutputType.RAW -> {
@ -127,11 +145,8 @@ internal class ProgramAndVarsGen(
"cx16" -> { "cx16" -> {
if(options.floats) if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr main.start | lda #4 | sta $01") asmgen.out(" jsr main.start")
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit") asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
} }
"c64" -> { "c64" -> {
asmgen.out(" jsr main.start | lda #31 | sta $01") asmgen.out(" jsr main.start | lda #31 | sta $01")
@ -155,23 +170,15 @@ internal class ProgramAndVarsGen(
private fun memorySlabs() { private fun memorySlabs() {
asmgen.out("; memory slabs") asmgen.out("; memory slabs")
asmgen.out("prog8_slabs\t.block") asmgen.out("prog8_slabs\t.block")
for((name, info) in allocator.memorySlabs) { for(slab in symboltable.allMemorySlabs) {
if(info.second>1u) if(slab.align>1u)
asmgen.out("\t.align ${info.second.toHex()}") asmgen.out("\t.align ${slab.align.toHex()}")
asmgen.out("$name\t.fill ${info.first}") asmgen.out("${slab.name}\t.fill ${slab.size}")
} }
asmgen.out("\t.bend") asmgen.out("\t.bend")
} }
private fun footer() { private fun footer() {
// the global list of all floating point constants for the whole program
asmgen.out("; global float constants")
for (flt in allocator.globalFloatConsts) {
val floatFill = compTarget.machine.getFloat(flt.key).makeFloatFillAsm()
val floatvalue = flt.key
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
}
// program end // program end
asmgen.out("prog8_program_end\t; end of program label for progend()") asmgen.out("prog8_program_end\t; end of program label for progend()")
} }
@ -209,9 +216,9 @@ internal class ProgramAndVarsGen(
if(!options.dontReinitGlobals) { if(!options.dontReinitGlobals) {
// generate subroutine to initialize block-level (global) variables // generate subroutine to initialize block-level (global) variables
if (initializers.isNotEmpty()) { if (initializers.isNotEmpty()) {
asmgen.out("prog8_init_vars\t.proc\n") asmgen.out("prog8_init_vars\t.block\n")
initializers.forEach { assign -> asmgen.translate(assign) } initializers.forEach { assign -> asmgen.translate(assign) }
asmgen.out(" rts\n .pend") asmgen.out(" rts\n .bend")
} }
} }
@ -263,17 +270,27 @@ internal class ProgramAndVarsGen(
asmgen.out("") asmgen.out("")
val asmStartScope: String
val asmEndScope: String
if(sub.definingBlock.options().contains("force_output")) {
asmStartScope = ".block"
asmEndScope = ".bend"
} else {
asmStartScope = ".proc"
asmEndScope = ".pend"
}
if(sub.isAsmSubroutine) { if(sub.isAsmSubroutine) {
if(sub.asmAddress!=null) if(sub.asmAddress!=null)
return // already done at the memvars section return // already done at the memvars section
// asmsub with most likely just an inline asm in it // asmsub with most likely just an inline asm in it
asmgen.out("${sub.name}\t.proc") asmgen.out("${sub.name}\t$asmStartScope")
sub.statements.forEach { asmgen.translate(it) } sub.statements.forEach { asmgen.translate(it) }
asmgen.out(" .pend\n") asmgen.out(" $asmEndScope\n")
} else { } else {
// regular subroutine // regular subroutine
asmgen.out("${sub.name}\t.proc") asmgen.out("${sub.name}\t$asmStartScope")
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") } val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.SUBROUTINE) require(scope.type==StNodeType.SUBROUTINE)
@ -302,7 +319,7 @@ internal class ProgramAndVarsGen(
asmgen.out("; simple int arg(s) passed via register(s)") asmgen.out("; simple int arg(s) passed via register(s)")
if(sub.parameters.size==1) { if(sub.parameters.size==1) {
val dt = sub.parameters[0].type val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, sub, variableAsmName = sub.parameters[0].name) val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypes) if(dt in ByteDatatypes)
asmgen.assignRegister(RegisterOrPair.A, target) asmgen.assignRegister(RegisterOrPair.A, target)
else else
@ -310,8 +327,8 @@ internal class ProgramAndVarsGen(
} else { } else {
require(sub.parameters.size==2) require(sub.parameters.size==2)
// 2 simple byte args, first in A, second in Y // 2 simple byte args, first in A, second in Y
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name) val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name) val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
asmgen.assignRegister(RegisterOrPair.A, target1) asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2) asmgen.assignRegister(RegisterOrPair.Y, target2)
} }
@ -350,7 +367,7 @@ internal class ProgramAndVarsGen(
.map { it.value as StStaticVariable } .map { it.value as StStaticVariable }
nonZpVariables2asm(variables) nonZpVariables2asm(variables)
asmgen.out(" .pend\n") asmgen.out(" $asmEndScope\n")
} }
} }
@ -432,9 +449,9 @@ internal class ProgramAndVarsGen(
val result = mutableListOf<ZpStringWithInitial>() val result = mutableListOf<ZpStringWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR } val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
for (variable in vars) { for (variable in vars) {
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table val svar = symboltable.flat.getValue(variable.key) as StStaticVariable
if(svar.initialStringValue!=null) if(svar.onetimeInitializationStringValue!=null)
result.add(ZpStringWithInitial(variable.key, variable.value, svar.initialStringValue!!)) result.add(ZpStringWithInitial(variable.key, variable.value, svar.onetimeInitializationStringValue!!))
} }
return result return result
} }
@ -443,9 +460,9 @@ internal class ProgramAndVarsGen(
val result = mutableListOf<ZpArrayWithInitial>() val result = mutableListOf<ZpArrayWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes } val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
for (variable in vars) { for (variable in vars) {
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table val svar = symboltable.flat.getValue(variable.key) as StStaticVariable
if(svar.initialArrayValue!=null) if(svar.onetimeInitializationArrayValue!=null)
result.add(ZpArrayWithInitial(variable.key, variable.value, svar.initialArrayValue!!)) result.add(ZpArrayWithInitial(variable.key, variable.value, svar.onetimeInitializationArrayValue!!))
} }
return result return result
} }
@ -464,7 +481,7 @@ internal class ProgramAndVarsGen(
asmgen.out("; non-zeropage variables") asmgen.out("; non-zeropage variables")
val (stringvars, othervars) = variables.partition { it.dt==DataType.STR } val (stringvars, othervars) = variables.partition { it.dt==DataType.STR }
stringvars.forEach { stringvars.forEach {
outputStringvar(it.name, it.initialStringValue!!.second, it.initialStringValue!!.first) outputStringvar(it.name, it.onetimeInitializationStringValue!!.second, it.onetimeInitializationStringValue!!.first)
} }
othervars.sortedBy { it.type }.forEach { othervars.sortedBy { it.type }.forEach {
staticVariable2asm(it) staticVariable2asm(it)
@ -474,11 +491,11 @@ internal class ProgramAndVarsGen(
private fun staticVariable2asm(variable: StStaticVariable) { private fun staticVariable2asm(variable: StStaticVariable) {
val name = variable.name val name = variable.name
val initialValue: Number = val initialValue: Number =
if(variable.initialNumericValue!=null) { if(variable.onetimeInitializationNumericValue!=null) {
if(variable.dt== DataType.FLOAT) if(variable.dt== DataType.FLOAT)
variable.initialNumericValue!! variable.onetimeInitializationNumericValue!!
else else
variable.initialNumericValue!!.toInt() variable.onetimeInitializationNumericValue!!.toInt()
} else 0 } else 0
when (variable.dt) { when (variable.dt) {
@ -490,14 +507,14 @@ internal class ProgramAndVarsGen(
if(initialValue==0) { if(initialValue==0) {
asmgen.out("$name\t.byte 0,0,0,0,0 ; float") asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
} else { } else {
val floatFill = compTarget.machine.getFloat(initialValue).makeFloatFillAsm() val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
asmgen.out("$name\t.byte $floatFill ; float $initialValue") asmgen.out("$name\t.byte $floatFill ; float $initialValue")
} }
} }
DataType.STR -> { DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog") throw AssemblyError("all string vars should have been interned into prog")
} }
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.initialArrayValue, variable.length) in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
else -> { else -> {
throw AssemblyError("weird dt") throw AssemblyError("weird dt")
} }
@ -549,7 +566,7 @@ internal class ProgramAndVarsGen(
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map { val floatFills = array.map {
compTarget.machine.getFloat(it.number!!).makeFloatFillAsm() compTarget.machine.getFloatAsmBytes(it.number!!)
} }
asmgen.out(varname) asmgen.out(varname)
for (f in array.zip(floatFills)) for (f in array.zip(floatFills))

View File

@ -15,8 +15,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
) { ) {
private val zeropage = options.compTarget.machine.zeropage private val zeropage = options.compTarget.machine.zeropage
private val memorySlabsInternal = mutableMapOf<String, Pair<UInt, UInt>>()
internal val memorySlabs: Map<String, Pair<UInt, UInt>> = memorySlabsInternal
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname) internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
internal val zeropageVars: Map<List<String>, Zeropage.ZpAllocation> = zeropage.allocatedVariables internal val zeropageVars: Map<List<String>, Zeropage.ZpAllocation> = zeropage.allocatedVariables
@ -24,12 +22,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
allocateZeropageVariables() allocateZeropageVariables()
} }
internal fun getMemorySlab(name: String) = memorySlabsInternal[name]
internal fun allocateMemorySlab(name: String, size: UInt, align: UInt) {
memorySlabsInternal[name] = Pair(size, align)
}
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
internal fun getFloatAsmConst(number: Double): String { internal fun getFloatAsmConst(number: Double): String {

View File

@ -29,7 +29,6 @@ internal enum class SourceStorageKind {
} }
internal class AsmAssignTarget(val kind: TargetStorageKind, internal class AsmAssignTarget(val kind: TargetStorageKind,
private val program: Program,
private val asmgen: AsmGen, private val asmgen: AsmGen,
val datatype: DataType, val datatype: DataType,
val scope: Subroutine?, val scope: Subroutine?,
@ -70,28 +69,28 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
if(reg.statusflag!=null) if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly") throw AssemblyError("can't assign value to processor statusflag directly")
else else
return AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this) return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this)
} }
} }
return AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this) return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
} }
arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this) arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this)
memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this) memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
else -> throw AssemblyError("weird target") else -> throw AssemblyError("weird target")
} }
} }
} }
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget = fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, asmgen: AsmGen): AsmAssignTarget =
when(registers) { when(registers) {
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers) RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
RegisterOrPair.AX, RegisterOrPair.AX,
RegisterOrPair.AY, RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers) RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
RegisterOrPair.FAC1, RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers) RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, register = registers)
RegisterOrPair.R0, RegisterOrPair.R0,
RegisterOrPair.R1, RegisterOrPair.R1,
RegisterOrPair.R2, RegisterOrPair.R2,
@ -107,7 +106,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.R12, RegisterOrPair.R12,
RegisterOrPair.R13, RegisterOrPair.R13,
RegisterOrPair.R14, RegisterOrPair.R14,
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers) RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
} }
} }
} }

View File

@ -2,7 +2,6 @@ package prog8.codegen.cpu6502.assignment
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.getTempRegisterName
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen import prog8.codegen.cpu6502.AsmGen
@ -30,7 +29,7 @@ internal class AssignmentAsmGen(private val program: Program,
internal fun virtualRegsToVariables(origtarget: AsmAssignTarget): AsmAssignTarget { internal fun virtualRegsToVariables(origtarget: AsmAssignTarget): AsmAssignTarget {
return if(origtarget.kind==TargetStorageKind.REGISTER && origtarget.register in Cx16VirtualRegisters) { return if(origtarget.kind==TargetStorageKind.REGISTER && origtarget.register in Cx16VirtualRegisters) {
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, origtarget.datatype, origtarget.scope, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, origtarget.datatype, origtarget.scope,
variableAsmName = "cx16.${origtarget.register!!.name.lowercase()}", origAstTarget = origtarget.origAstTarget) variableAsmName = "cx16.${origtarget.register!!.name.lowercase()}", origAstTarget = origtarget.origAstTarget)
} else origtarget } else origtarget
} }
@ -221,9 +220,25 @@ internal class AssignmentAsmGen(private val program: Program,
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A) RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X) RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y) RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX) RegisterOrPair.AX -> assignVirtualRegister(assign.target, RegisterOrPair.AX)
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) RegisterOrPair.AY -> assignVirtualRegister(assign.target, RegisterOrPair.AY)
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY) RegisterOrPair.XY -> assignVirtualRegister(assign.target, RegisterOrPair.XY)
RegisterOrPair.R0 -> assignVirtualRegister(assign.target, RegisterOrPair.R0)
RegisterOrPair.R1 -> assignVirtualRegister(assign.target, RegisterOrPair.R1)
RegisterOrPair.R2 -> assignVirtualRegister(assign.target, RegisterOrPair.R2)
RegisterOrPair.R3 -> assignVirtualRegister(assign.target, RegisterOrPair.R3)
RegisterOrPair.R4 -> assignVirtualRegister(assign.target, RegisterOrPair.R4)
RegisterOrPair.R5 -> assignVirtualRegister(assign.target, RegisterOrPair.R5)
RegisterOrPair.R6 -> assignVirtualRegister(assign.target, RegisterOrPair.R6)
RegisterOrPair.R7 -> assignVirtualRegister(assign.target, RegisterOrPair.R7)
RegisterOrPair.R8 -> assignVirtualRegister(assign.target, RegisterOrPair.R8)
RegisterOrPair.R9 -> assignVirtualRegister(assign.target, RegisterOrPair.R9)
RegisterOrPair.R10 -> assignVirtualRegister(assign.target, RegisterOrPair.R10)
RegisterOrPair.R11 -> assignVirtualRegister(assign.target, RegisterOrPair.R11)
RegisterOrPair.R12 -> assignVirtualRegister(assign.target, RegisterOrPair.R12)
RegisterOrPair.R13 -> assignVirtualRegister(assign.target, RegisterOrPair.R13)
RegisterOrPair.R14 -> assignVirtualRegister(assign.target, RegisterOrPair.R14)
RegisterOrPair.R15 -> assignVirtualRegister(assign.target, RegisterOrPair.R15)
else -> { else -> {
val sflag = returnValue.second.statusflag val sflag = returnValue.second.statusflag
if(sflag!=null) if(sflag!=null)
@ -283,7 +298,6 @@ internal class AssignmentAsmGen(private val program: Program,
"+" -> {} "+" -> {}
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype) "-" -> augmentableAsmGen.inplaceNegate(target, target.datatype)
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype) "~" -> augmentableAsmGen.inplaceInvert(target, target.datatype)
"not" -> augmentableAsmGen.inplaceBooleanNot(target, target.datatype)
else -> throw AssemblyError("invalid prefix operator") else -> throw AssemblyError("invalid prefix operator")
} }
} }
@ -303,11 +317,32 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
when(target.datatype) {
in ByteDatatypes -> {
asmgen.out(" lda cx16.${register.toString().lowercase()}L")
assignRegisterByte(target, CpuRegister.A)
}
in WordDatatypes -> assignRegisterpairWord(target, register)
else -> throw AssemblyError("expected byte or word")
}
}
private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean { private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean {
if(expr.operator in ComparisonOperators) { if(expr.operator in ComparisonOperators) {
assignConstantByte(assign.target, 0) if(expr.right.constValue(program)?.number == 0.0) {
if(expr.operator == "==" || expr.operator=="!=") {
when(assign.target.datatype) {
in ByteDatatypes -> if(attemptAssignToByteCompareZero(expr, assign)) return true
else -> {
// do nothing, this is handled by a type cast.
}
}
}
}
val origTarget = assign.target.origAstTarget val origTarget = assign.target.origAstTarget
if(origTarget!=null) { if(origTarget!=null) {
assignConstantByte(assign.target, 0)
val assignTrue = AnonymousScope(mutableListOf( val assignTrue = AnonymousScope(mutableListOf(
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position) Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
), assign.position) ), assign.position)
@ -322,66 +357,114 @@ internal class AssignmentAsmGen(private val program: Program,
if(!expr.inferType(program).isInteger) if(!expr.inferType(program).isInteger)
return false return false
// optimized code for logical expressions if(expr.operator in setOf("&", "|", "^", "and", "or", "xor")) {
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this... if(expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes &&
if(expr.operator=="and") { expr.left.isSimple && expr.right.isSimple) {
val iDt = expr.left.inferType(program) if(expr.right is NumericLiteral || expr.right is IdentifierReference)
val dt = iDt.getOrElse { throw AssemblyError("weird dt") } assignLogicalWithSimpleRightOperandByte(assign.target, expr.left, expr.operator, expr.right)
if (dt in ByteDatatypes) { else if(expr.left is NumericLiteral || expr.left is IdentifierReference)
val tmpReg = getTempRegisterName(iDt).joinToString(".") assignLogicalWithSimpleRightOperandByte(assign.target, expr.right, expr.operator, expr.left)
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 { else {
asmgen.out(" ldy #0") assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingSubroutine)
asmgen.restoreRegisterStack(CpuRegister.A, false)
when (expr.operator) {
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid operator")
}
assignRegisterByte(assign.target, CpuRegister.A)
}
return true
}
if(expr.left.inferType(program).isWords && expr.right.inferType(program).isWords &&
expr.left.isSimple && expr.right.isSimple) {
if(expr.right is NumericLiteral || expr.right is IdentifierReference)
assignLogicalWithSimpleRightOperandWord(assign.target, expr.left, expr.operator, expr.right)
else if(expr.left is NumericLiteral || expr.left is IdentifierReference)
assignLogicalWithSimpleRightOperandWord(assign.target, expr.right, expr.operator, expr.left)
else {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingSubroutine)
when (expr.operator) {
"&", "and" -> asmgen.out(" pla | and P8ZP_SCRATCH_W1+1 | tay | pla | and P8ZP_SCRATCH_W1")
"|", "or" -> asmgen.out(" pla | ora P8ZP_SCRATCH_W1+1 | tay | pla | ora P8ZP_SCRATCH_W1")
"^", "xor" -> asmgen.out(" pla | eor P8ZP_SCRATCH_W1+1 | tay | pla | eor P8ZP_SCRATCH_W1")
else -> throw AssemblyError("invalid operator")
}
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
} }
return true 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 return false
}
if(expr.operator == "==" || expr.operator == "!=") {
// expression datatype is BOOL (ubyte) but operands can be anything
if(expr.left.inferType(program).isBytes && expr.right.inferType(program).isBytes &&
expr.left.isSimple && expr.right.isSimple) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE, expr.definingSubroutine)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") {
asmgen.out("""
cmp P8ZP_SCRATCH_B1
bne +
lda #1
bne ++
+ lda #0
+""")
} else {
asmgen.out("""
cmp P8ZP_SCRATCH_B1
beq +
lda #1
bne ++
+ lda #0
+""")
}
assignRegisterByte(assign.target, CpuRegister.A)
return true
} else if(expr.left.inferType(program).isWords && expr.right.inferType(program).isWords &&
expr.left.isSimple && expr.right.isSimple) {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", DataType.UWORD, expr.definingSubroutine)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(expr.operator=="==") {
asmgen.out("""
cmp P8ZP_SCRATCH_W1
bne +
cpy P8ZP_SCRATCH_W1+1
bne +
lda #1
bne ++
+ lda #0
+""")
} else {
asmgen.out("""
cmp P8ZP_SCRATCH_W1
bne +
cpy P8ZP_SCRATCH_W1+1
bne +
lda #0
bne ++
+ lda #1
+""")
}
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
return false
}
else if(expr.operator=="+" || expr.operator=="-") {
val dt = expr.inferType(program).getOrElse { throw AssemblyError("invalid dt") } val dt = expr.inferType(program).getOrElse { throw AssemblyError("invalid dt") }
val left = expr.left val left = expr.left
val right = expr.right val right = expr.right
@ -510,12 +593,124 @@ internal class AssignmentAsmGen(private val program: Program,
else -> return false else -> return false
} }
} }
}
return false return false
} }
private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: Expression, operator: String, right: Expression) {
assignExpressionToRegister(left, RegisterOrPair.A, false)
val operand = when(right) {
is NumericLiteral -> "#${right.number.toHex()}"
is IdentifierReference -> asmgen.asmSymbolName(right)
else -> throw AssemblyError("wrong right operand type")
}
when (operator) {
"&", "and" -> asmgen.out(" and $operand")
"|", "or" -> asmgen.out(" ora $operand")
"^", "xor" -> asmgen.out(" eor $operand")
else -> throw AssemblyError("invalid operator")
}
assignRegisterByte(target, CpuRegister.A)
}
private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: Expression, operator: String, right: Expression) {
assignExpressionToRegister(left, RegisterOrPair.AY, false)
when(right) {
is NumericLiteral -> {
val number = right.number.toHex()
when (operator) {
"&", "and" -> asmgen.out(" and #<$number | pha | tya | and #>$number | tay | pla")
"|", "or" -> asmgen.out(" ora #<$number | pha | tya | ora #>$number | tay | pla")
"^", "xor" -> asmgen.out(" eor #<$number | pha | tya | eor #>$number | tay | pla")
else -> throw AssemblyError("invalid operator")
}
}
is IdentifierReference -> {
val name = asmgen.asmSymbolName(right)
when (operator) {
"&", "and" -> asmgen.out(" and $name | pha | tya | and $name+1 | tay | pla")
"|", "or" -> asmgen.out(" ora $name | pha | tya | ora $name+1 | tay | pla")
"^", "xor" -> asmgen.out(" eor $name | pha | tya | eor $name+1 | tay | pla")
else -> throw AssemblyError("invalid operator")
}
}
else -> throw AssemblyError("wrong right operand type")
}
assignRegisterpairWord(target, RegisterOrPair.AY)
}
private fun attemptAssignToByteCompareZero(expr: BinaryExpression, assign: AsmAssignment): Boolean {
when (expr.operator) {
"==" -> {
when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #0
beq ++
+ lda #1
+""")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | and #1 | eor #1")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
"!=" -> {
when(val dt = expr.left.inferType(program).getOrElse { throw AssemblyError("invalid dt") }) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
asmgen.out(" beq + | lda #1")
asmgen.out("+")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
DataType.FLOAT -> {
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN")
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
else->{
return false
}
}
}
else -> return false
}
}
private fun fallbackToStackEval(assign: AsmAssignment) { 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.
// this routine is called for assigning a binaryexpression value: // this routine is called for assigning a binaryexpression value:
// - if it's a boolean comparison expression and the workaround isn't possible (no origTarget ast node) // - if it's a boolean comparison expression and the workaround isn't possible (no origTarget ast node)
// - for all other binary expressions. // - for all other binary expressions.
@ -539,7 +734,7 @@ internal class AssignmentAsmGen(private val program: Program,
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference) val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.out(" ldy #${stringVal.value.length}") asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
@ -558,14 +753,14 @@ internal class AssignmentAsmGen(private val program: Program,
in ByteDatatypes -> { in ByteDatatypes -> {
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.out(" ldy #${arrayVal.value.size}") asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
} }
in WordDatatypes -> { in WordDatatypes -> {
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine) assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #${arrayVal.value.size}") asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" jsr prog8_lib.containment_wordarray") asmgen.out(" jsr prog8_lib.containment_wordarray")
} }
@ -582,7 +777,7 @@ internal class AssignmentAsmGen(private val program: Program,
// use subroutine // use subroutine
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterLocal(CpuRegister.A)
val stringVal = variable.value as StringLiteral val stringVal = variable.value as StringLiteral
asmgen.out(" ldy #${stringVal.value.length}") asmgen.out(" ldy #${stringVal.value.length}")
@ -596,7 +791,7 @@ internal class AssignmentAsmGen(private val program: Program,
val arrayVal = variable.value as ArrayLiteral val arrayVal = variable.value as ArrayLiteral
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!) asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A) asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.out(" ldy #${arrayVal.value.size}") asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
@ -605,7 +800,7 @@ internal class AssignmentAsmGen(private val program: Program,
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
val arrayVal = variable.value as ArrayLiteral val arrayVal = variable.value as ArrayLiteral
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine) assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #${arrayVal.value.size}") asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" jsr prog8_lib.containment_wordarray") asmgen.out(" jsr prog8_lib.containment_wordarray")
return return
@ -716,10 +911,10 @@ internal class AssignmentAsmGen(private val program: Program,
return return
} }
if(origTypeCastExpression.type == DataType.UBYTE) { if(valueDt in WordDatatypes && origTypeCastExpression.type == DataType.UBYTE) {
val parentTc = origTypeCastExpression.parent as? TypecastExpression val parentTc = origTypeCastExpression.parent as? TypecastExpression
if(parentTc!=null && parentTc.type==DataType.UWORD) { if(parentTc!=null && parentTc.type==DataType.UWORD) {
// typecast something to ubyte and directly back to uword // typecast a word value to ubyte and directly back to uword
// generate code for lsb(value) here instead of the ubyte typecast // generate code for lsb(value) here instead of the ubyte typecast
return assignCastViaLsbFunc(value, target) return assignCastViaLsbFunc(value, target)
} }
@ -769,6 +964,23 @@ internal class AssignmentAsmGen(private val program: Program,
assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype) assignTypeCastedFloatFAC1("P8ZP_SCRATCH_W1", target.datatype)
assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes) assignVariableToRegister("P8ZP_SCRATCH_W1", target.register!!, target.datatype in SignedDatatypes)
} else { } else {
if(!(valueDt isAssignableTo targetDt)) {
if(valueDt in WordDatatypes && targetDt in ByteDatatypes) {
// word to byte, just take the lsb
return assignCastViaLsbFunc(value, target)
} else if(valueDt in WordDatatypes && targetDt in WordDatatypes) {
// word to word, just assign
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
} else if(valueDt in ByteDatatypes && targetDt in ByteDatatypes) {
// byte to byte, just assign
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
} else if(valueDt in ByteDatatypes && targetDt in WordDatatypes) {
// byte to word, just assign
assignExpressionToRegister(value, target.register!!, targetDt==DataType.WORD)
}
else
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker")
}
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD) assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
} }
return return
@ -2150,10 +2362,7 @@ internal class AssignmentAsmGen(private val program: Program,
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0") RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float") RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
asmgen.out( asmgen.out(" stz cx16.${target.register.toString().lowercase()} | stz cx16.${target.register.toString().lowercase()}+1")
" stz cx16.${
target.register.toString().lowercase()
} | stz cx16.${target.register.toString().lowercase()}+1")
} }
else -> throw AssemblyError("weird register") else -> throw AssemblyError("weird register")
} }
@ -2546,7 +2755,7 @@ internal class AssignmentAsmGen(private val program: Program,
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair, signed: Boolean) { internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair, signed: Boolean) {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
@ -2556,14 +2765,14 @@ internal class AssignmentAsmGen(private val program: Program,
throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr") throw AssemblyError("can't directly assign a FLOAT expression to an integer variable $expr")
} else { } else {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, scope, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
} }
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) { internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, signed, null, asmgen)
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName) val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY) val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY)
translateNormalAssignment(assign) translateNormalAssignment(assign)

View File

@ -28,7 +28,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"+" -> {} "+" -> {}
"-" -> inplaceNegate(target, type) "-" -> inplaceNegate(target, type)
"~" -> inplaceInvert(target, type) "~" -> inplaceInvert(target, type)
"not" -> inplaceBooleanNot(target, type)
else -> throw AssemblyError("invalid prefix operator") else -> throw AssemblyError("invalid prefix operator")
} }
} }
@ -237,7 +236,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
else -> { else -> {
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from // TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, memory.definingSubroutine)) asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingSubroutine))
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1") asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
when { when {
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt()) valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
@ -306,7 +305,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
AsmAssignTarget.fromRegisters( AsmAssignTarget.fromRegisters(
RegisterOrPair.A, RegisterOrPair.A,
target.datatype == DataType.BYTE, null, target.datatype == DataType.BYTE, null,
program,
asmgen asmgen
) )
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
@ -318,7 +316,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
AsmAssignTarget.fromRegisters( AsmAssignTarget.fromRegisters(
RegisterOrPair.AY, RegisterOrPair.AY,
target.datatype == DataType.WORD, null, target.datatype == DataType.WORD, null,
program,
asmgen asmgen
) )
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
@ -330,7 +327,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
AsmAssignTarget.fromRegisters( AsmAssignTarget.fromRegisters(
RegisterOrPair.FAC1, RegisterOrPair.FAC1,
true, null, true, null,
program,
asmgen asmgen
) )
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
@ -391,9 +387,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne - bne -
+""") +""")
} }
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") "&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1") "|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") "^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
asmgen.storeAIntoZpPointerVar(sourceName) asmgen.storeAIntoZpPointerVar(sourceName)
@ -428,9 +424,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne - bne -
+""") +""")
} }
"&", "and" -> asmgen.out(" and $otherName") "&" -> asmgen.out(" and $otherName")
"|", "or" -> asmgen.out(" ora $otherName") "|" -> asmgen.out(" ora $otherName")
"^", "xor" -> asmgen.out(" eor $otherName") "^" -> asmgen.out(" eor $otherName")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
asmgen.storeAIntoZpPointerVar(sourceName) asmgen.storeAIntoZpPointerVar(sourceName)
@ -485,17 +481,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.storeAIntoZpPointerVar(sourceName) asmgen.storeAIntoZpPointerVar(sourceName)
} }
} }
"&", "and" -> { "&" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and #$value") asmgen.out(" and #$value")
asmgen.storeAIntoZpPointerVar(sourceName) asmgen.storeAIntoZpPointerVar(sourceName)
} }
"|", "or" -> { "|"-> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" ora #$value") asmgen.out(" ora #$value")
asmgen.storeAIntoZpPointerVar(sourceName) asmgen.storeAIntoZpPointerVar(sourceName)
} }
"^", "xor" -> { "^" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value") asmgen.out(" eor #$value")
asmgen.storeAIntoZpPointerVar(sourceName) asmgen.storeAIntoZpPointerVar(sourceName)
@ -563,15 +559,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""") +""")
} }
} }
"&", "and" -> { "&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name") asmgen.out(" and $name | sta $name")
} }
"|", "or" -> { "|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name") asmgen.out(" ora $name | sta $name")
} }
"^", "xor" -> { "^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name") asmgen.out(" eor $name | sta $name")
} }
@ -649,9 +645,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""") +""")
} }
} }
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name") "&" -> asmgen.out(" lda $name | and $otherName | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name") "|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name") "^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
"==" -> { "==" -> {
asmgen.out(""" asmgen.out("""
lda $otherName lda $otherName
@ -740,9 +736,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
} }
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name") "&" -> asmgen.out(" lda $name | and #$value | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name") "|" -> asmgen.out(" lda $name | ora #$value | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name") "^" -> asmgen.out(" lda $name | eor #$value | sta $name")
"==" -> { "==" -> {
asmgen.out(""" asmgen.out("""
lda $name lda $name
@ -785,15 +781,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sbc P8ZP_SCRATCH_B1 sbc P8ZP_SCRATCH_B1
sta $name""") sta $name""")
} }
"|", "or" -> { "|" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" ora $name | sta $name") asmgen.out(" ora $name | sta $name")
} }
"&", "and" -> { "&" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" and $name | sta $name") asmgen.out(" and $name | sta $name")
} }
"^", "xor" -> { "^" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" eor $name | sta $name") asmgen.out(" eor $name | sta $name")
} }
@ -828,11 +824,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
dec $name+1 dec $name+1
+""") +""")
} }
"|", "or" -> { "|" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" ora $name | sta $name") asmgen.out(" ora $name | sta $name")
} }
"&", "and" -> { "&" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" and $name | sta $name") asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) { if(dt in WordDatatypes) {
@ -842,7 +838,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
} }
} }
"^", "xor" -> { "^" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
asmgen.out(" eor $name | sta $name") asmgen.out(" eor $name | sta $name")
} }
@ -1059,7 +1055,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
} }
"&", "and" -> { "&" -> {
when { when {
value == 0 -> { value == 0 -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02)) if(asmgen.isTargetCpu(CpuType.CPU65c02))
@ -1096,7 +1092,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1") else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
} }
} }
"|", "or" -> { "|" -> {
when { when {
value == 0 -> {} value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1") value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
@ -1104,7 +1100,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1") else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
} }
} }
"^", "xor" -> { "^" -> {
when { when {
value == 0 -> {} value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1") value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
@ -1268,7 +1264,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""") +""")
} }
} }
"&", "and" -> { "&" -> {
asmgen.out(" lda $otherName | and $name | sta $name") asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes) { if(dt in WordDatatypes) {
if(asmgen.isTargetCpu(CpuType.CPU65c02)) if(asmgen.isTargetCpu(CpuType.CPU65c02))
@ -1277,8 +1273,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
} }
} }
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name") "|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name") "^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1349,9 +1345,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""") """)
} }
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1") "&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1") "|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1") "^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1521,7 +1517,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne - bne -
+""") +""")
} }
"&", "and" -> { "&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name") asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) { if(dt in WordDatatypes) {
@ -1531,11 +1527,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
} }
} }
"|", "or" -> { "|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name") asmgen.out(" ora $name | sta $name")
} }
"^", "xor" -> { "^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name") asmgen.out(" eor $name | sta $name")
} }
@ -1567,15 +1563,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
remainderVarByWordInAY() remainderVarByWordInAY()
} }
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&", "and" -> { "&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1") asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
} }
"|", "or" -> { "|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1") asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
} }
"^", "xor" -> { "^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1") asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
} }
@ -1800,136 +1796,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
internal fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
when (dt) {
DataType.UBYTE -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
beq +
lda #1
+ eor #1
sta ${target.asmVarname}""")
}
TargetStorageKind.MEMORY -> {
val mem = target.memory!!
when (mem.addressExpression) {
is NumericLiteral -> {
val addr = (mem.addressExpression as NumericLiteral).number.toHex()
asmgen.out("""
lda $addr
beq +
lda #1
+ eor #1
sta $addr""")
}
is IdentifierReference -> {
val sourceName = asmgen.loadByteFromPointerIntoA(mem.addressExpression as IdentifierReference)
asmgen.out("""
beq +
lda #1
+ eor #1""")
asmgen.storeAIntoZpPointerVar(sourceName)
}
else -> {
asmgen.assignExpressionToVariable(mem.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
asmgen.out("""
beq +
lda #1
+ eor #1""")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
}
}
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.A -> asmgen.out("""
cmp #0
beq +
lda #1
+ eor #1""")
RegisterOrPair.X -> asmgen.out("""
txa
beq +
lda #1
+ eor #1
tax""")
RegisterOrPair.Y -> asmgen.out("""
tya
beq +
lda #1
+ eor #1
tay""")
else -> throw AssemblyError("invalid reg dt for byte not")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for byte stack not")
else -> throw AssemblyError("no asm gen for in-place not of ubyte ${target.kind}")
}
}
DataType.UWORD -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${target.asmVarname}
ora ${target.asmVarname}+1
beq +
lda #1
+ eor #1
sta ${target.asmVarname}
lsr a
sta ${target.asmVarname}+1""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.AX -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
ora P8ZP_SCRATCH_REG
beq +
lda #0
tax
beq ++
+ lda #1
+""")
}
RegisterOrPair.AY -> {
asmgen.out("""
sty P8ZP_SCRATCH_REG
ora P8ZP_SCRATCH_REG
beq +
lda #0
tay
beq ++
+ lda #1
+""")
}
RegisterOrPair.XY -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
tya
ora P8ZP_SCRATCH_REG
beq +
ldy #0
ldx #0
beq ++
+ ldx #1
+""")
}
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
else -> throw AssemblyError("invalid reg dt for word not")
}
}
TargetStorageKind.STACK -> TODO("no asm gen for word stack not")
else -> throw AssemblyError("no asm gen for in-place not of uword for ${target.kind}")
}
}
else -> throw AssemblyError("boolean-not of invalid type")
}
}
internal fun inplaceInvert(target: AsmAssignTarget, dt: DataType) { internal fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
when (dt) { when (dt) {
DataType.UBYTE -> { DataType.UBYTE -> {

View File

@ -24,8 +24,9 @@ compileTestKotlin {
} }
dependencies { dependencies {
implementation project(':codeAst')
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"

View File

@ -10,7 +10,8 @@
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="module" module-name="codeAst" /> <orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
</component> </component>
</module> </module>

View File

@ -1,39 +0,0 @@
package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyGenerator
import prog8.code.core.IAssemblyProgram
import prog8.code.core.IErrorReporter
/*
NOTE: The goal is to keep the dependencies as lean as possible! For now, we depend only on:
- codeAst (the 'lean' new AST and the SymbolTable)
- codeCore (various base enums and interfaces)
This *should* be enough to build a complete code generator with. But we'll see :)
*/
class AsmGen(internal val program: PtProgram,
internal val symbolTable: SymbolTable,
internal val options: CompilationOptions,
internal val errors: IErrorReporter
): IAssemblyGenerator {
override fun compileToAssembly(): IAssemblyProgram? {
println("\n** experimental code generator **\n")
println("Writing AST into XML form...")
val xmlConv = AstToXmlConverter(program, symbolTable, options)
xmlConv.writeXml()
println("..todo: create assembly program into ${options.outputDir.toAbsolutePath()}..")
return AssemblyProgram("dummy")
}
}

View File

@ -1,13 +0,0 @@
package prog8.codegen.experimental
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
internal class AssemblyProgram(override val name: String) : IAssemblyProgram
{
override fun assemble(options: CompilationOptions): Boolean {
println("..todo: assemble code into binary..")
return true
}
}

View File

@ -1,657 +0,0 @@
package prog8.codegen.experimental
import prog8.code.*
import prog8.code.ast.*
import prog8.code.core.*
import javax.xml.stream.XMLOutputFactory
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.div
/*
NOTE: The goal is to keep the dependencies as lean as possible! For now, we depend only on:
- codeAst (the 'lean' new AST and the SymbolTable)
- codeCore (various base enums and interfaces)
This *should* be enough to build a complete code generator with. But we'll see :)
*/
class AstToXmlConverter(internal val program: PtProgram,
internal val symbolTable: SymbolTable,
internal val options: CompilationOptions
) {
private lateinit var xml: IndentingXmlWriter
fun writeXml() {
val writer = (options.outputDir / Path(program.name+"-ast.xml")).toFile().printWriter()
xml = IndentingXmlWriter(XMLOutputFactory.newFactory().createXMLStreamWriter(writer))
xml.doc()
xml.elt("program")
xml.attr("name", program.name)
xml.startChildren()
writeOptions(options)
program.children.forEach { writeNode(it) }
writeSymboltable(symbolTable)
xml.endElt()
xml.endDoc()
xml.close()
}
private fun writeSymboltable(st: SymbolTable) {
xml.elt("symboltable")
xml.startChildren()
st.flat.forEach{ (name, entry) ->
xml.elt("entry")
xml.attr("name", name.joinToString("."))
xml.attr("type", entry.type.name)
xml.startChildren()
writeStNode(entry)
xml.endElt()
}
xml.endElt()
}
private fun writeStNode(node: StNode) {
when(node.type) {
StNodeType.GLOBAL,
StNodeType.LABEL,
StNodeType.BLOCK,
StNodeType.BUILTINFUNC,
StNodeType.SUBROUTINE -> {/* no additional info*/}
StNodeType.ROMSUB -> {
node as StRomSub
xml.elt("romsub")
xml.attr("address", node.address.toString())
xml.endElt()
}
StNodeType.STATICVAR -> {
node as StStaticVariable
xml.elt("var")
xml.attr("type", node.dt.name)
xml.attr("zpwish", node.zpwish.name)
if(node.length!=null)
xml.attr("length", node.length.toString())
if(node.initialNumericValue!=null || node.initialArrayValue!=null || node.initialStringValue!=null) {
xml.startChildren()
if(node.initialNumericValue!=null) {
writeNumber(node.dt, node.initialNumericValue!!)
}
if(node.initialStringValue!=null) {
xml.writeTextNode(
"string",
listOf(Pair("encoding", node.initialStringValue!!.second.name)),
node.initialStringValue!!.first,
false
)
}
if(node.initialArrayValue!=null) {
xml.elt("array")
xml.startChildren()
val eltDt = ArrayToElementTypes.getValue(node.dt)
node.initialArrayValue!!.forEach {
if(it.number!=null) {
writeNumber(eltDt, it.number!!)
}
if(it.addressOf!=null) {
xml.elt("addressof")
xml.attr("symbol", it.addressOf!!.joinToString("."))
xml.endElt()
}
}
xml.endElt()
}
}
xml.endElt()
}
StNodeType.MEMVAR -> {
node as StMemVar
xml.writeTextNode("memvar",
listOf(Pair("type", node.dt.name)),
node.address.toString(),
false)
}
StNodeType.CONSTANT -> {
node as StConstant
xml.writeTextNode("const",
listOf(Pair("type", node.dt.name)),
intOrDouble(node.dt, node.value).toString(),
false)
}
}
}
private fun writeOptions(options: CompilationOptions) {
xml.elt("options")
xml.attr("target", options.compTarget.name)
xml.attr("output", options.output.name)
xml.attr("launcher", options.launcher.name)
xml.attr("zeropage", options.zeropage.name)
xml.attr("loadaddress", options.loadAddress.toString())
xml.attr("floatsenabled", options.floats.toString())
xml.attr("nosysinit", options.noSysInit.toString())
xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
xml.attr("optimize", options.optimize.toString())
if(options.zpReserved.isNotEmpty()) {
xml.startChildren()
options.zpReserved.forEach {
xml.elt("zpreserved")
xml.attr("from", it.first.toString())
xml.attr("to", it.last.toString())
xml.endElt()
}
}
xml.endElt()
}
private fun writeNode(it: PtNode) {
when(it) {
is PtBlock -> write(it)
is PtSub -> write(it)
is PtVariable -> write(it)
is PtAssignment -> write(it)
is PtConstant -> write(it)
is PtAsmSub -> write(it)
is PtAddressOf -> write(it)
is PtArrayIndexer -> write(it)
is PtArray -> write(it)
is PtBinaryExpression -> write(it)
is PtBuiltinFunctionCall -> write(it)
is PtConditionalBranch -> write(it)
is PtContainmentCheck -> write(it)
is PtForLoop -> write(it)
is PtFunctionCall -> write(it)
is PtIdentifier -> write(it)
is PtIfElse -> write(it)
is PtInlineAssembly -> write(it)
is PtIncludeBinary -> write(it)
is PtJump -> write(it)
is PtMemoryByte -> write(it)
is PtMemMapped -> write(it)
is PtNumber -> write(it)
is PtPostIncrDecr -> write(it)
is PtPrefix -> write(it)
is PtRange -> write(it)
is PtRepeatLoop -> write(it)
is PtReturn -> write(it)
is PtString -> write(it)
is PtTypeCast -> write(it)
is PtWhen -> write(it)
is PtWhenChoice -> write(it)
is PtLabel -> write(it)
is PtNop -> {}
is PtBreakpoint -> write(it)
is PtScopeVarsDecls -> write(it)
is PtNodeGroup -> it.children.forEach { writeNode(it) }
else -> TODO("$it")
}
}
private fun write(vars: PtScopeVarsDecls) {
xml.elt("vars")
xml.startChildren()
vars.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(breakPt: PtBreakpoint) {
xml.elt("breakpoint")
xml.pos(breakPt.position)
xml.endElt()
}
private fun write(array: PtArray) {
xml.elt("array")
xml.attr("type", array.type.name)
xml.startChildren()
array.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(prefix: PtPrefix) {
xml.elt("prefix")
xml.attr("op", prefix.operator)
xml.attr("type", prefix.type.name)
xml.startChildren()
xml.elt("value")
xml.startChildren()
writeNode(prefix.value)
xml.endElt()
xml.endElt()
}
private fun write(string: PtString) =
xml.writeTextNode("string", listOf(Pair("encoding", string.encoding.name)), string.value, false)
private fun write(rept: PtRepeatLoop) {
xml.elt("repeat")
xml.pos(rept.position)
xml.startChildren()
xml.elt("count")
xml.startChildren()
writeNode(rept.count)
xml.endElt()
writeNode(rept.statements)
xml.endElt()
}
private fun write(branch: PtConditionalBranch) {
xml.elt("conditionalbranch")
xml.attr("condition", branch.condition.name)
xml.pos(branch.position)
xml.startChildren()
xml.elt("true")
xml.startChildren()
writeNode(branch.trueScope)
xml.endElt()
if(branch.falseScope.children.isNotEmpty()) {
xml.elt("false")
xml.startChildren()
writeNode(branch.falseScope)
xml.endElt()
}
xml.endElt()
}
private fun write(check: PtContainmentCheck) {
xml.elt("containment")
xml.attr("type", check.type.name)
xml.startChildren()
xml.elt("element")
xml.startChildren()
writeNode(check.children[0])
xml.endElt()
xml.elt("iterable")
xml.startChildren()
writeNode(check.children[1])
xml.endElt()
xml.endElt()
}
private fun write(range: PtRange) {
xml.elt("range")
xml.attr("type", range.type.name)
xml.startChildren()
xml.elt("from")
xml.startChildren()
writeNode(range.from)
xml.endElt()
xml.elt("to")
xml.startChildren()
writeNode(range.to)
xml.endElt()
xml.elt("step")
xml.startChildren()
writeNode(range.step)
xml.endElt()
xml.endElt()
}
private fun write(forLoop: PtForLoop) {
xml.elt("for")
xml.attr("loopvar", strTargetName(forLoop.variable))
xml.pos(forLoop.position)
xml.startChildren()
xml.elt("iterable")
xml.startChildren()
writeNode(forLoop.iterable)
xml.endElt()
writeNode(forLoop.statements)
xml.endElt()
}
private fun write(membyte: PtMemoryByte) {
xml.elt("membyte")
xml.attr("type", membyte.type.name)
xml.startChildren()
xml.elt("address")
xml.startChildren()
writeNode(membyte.address)
xml.endElt()
xml.endElt()
}
private fun write(whenStmt: PtWhen) {
xml.elt("when")
xml.pos(whenStmt.position)
xml.startChildren()
xml.elt("value")
xml.startChildren()
writeNode(whenStmt.value)
xml.endElt()
xml.elt("choices")
xml.startChildren()
writeNode(whenStmt.choices)
xml.endElt()
xml.endElt()
}
private fun write(choice: PtWhenChoice) {
xml.elt("choice")
if(choice.isElse) {
xml.attr("else", "true")
xml.startChildren()
} else {
xml.startChildren()
xml.elt("values")
xml.startChildren()
writeNode(choice.values)
xml.endElt()
}
writeNode(choice.statements)
xml.endElt()
}
private fun write(inlineAsm: PtInlineAssembly) {
xml.elt("assembly")
xml.pos(inlineAsm.position)
xml.startChildren()
xml.writeTextNode("code", emptyList(), inlineAsm.assembly)
xml.endElt()
}
private fun write(inlineBinary: PtIncludeBinary) {
xml.elt("binary")
xml.attr("filename", inlineBinary.file.absolutePathString())
if(inlineBinary.offset!=null)
xml.attr("offset", inlineBinary.offset!!.toString())
if(inlineBinary.length!=null)
xml.attr("length", inlineBinary.length!!.toString())
xml.pos(inlineBinary.position)
xml.endElt()
}
private fun write(fcall: PtBuiltinFunctionCall) {
xml.elt("builtinfcall")
xml.attr("name", fcall.name)
if(fcall.void)
xml.attr("type", "VOID")
else
xml.attr("type", fcall.type.name)
xml.startChildren()
fcall.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(cast: PtTypeCast) {
xml.elt("cast")
xml.attr("type", cast.type.name)
xml.startChildren()
writeNode(cast.value)
xml.endElt()
}
private fun write(aix: PtArrayIndexer) {
xml.elt("arrayindexed")
xml.attr("type", aix.type.name)
xml.startChildren()
write(aix.variable)
writeNode(aix.index)
xml.endElt()
}
private fun write(binexpr: PtBinaryExpression) {
xml.elt("binexpr")
xml.attr("op", binexpr.operator)
xml.attr("type", binexpr.type.name)
xml.startChildren()
writeNode(binexpr.left)
writeNode(binexpr.right)
xml.endElt()
}
private fun write(addrof: PtAddressOf) {
xml.elt("addressof")
xml.attr("symbol", strTargetName(addrof.identifier))
xml.endElt()
}
private fun write(fcall: PtFunctionCall) {
xml.elt("fcall")
xml.attr("name", strTargetName(fcall))
if(fcall.void)
xml.attr("type", "VOID")
else
xml.attr("type", fcall.type.name)
xml.pos(fcall.position)
xml.startChildren()
fcall.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(number: PtNumber) = writeNumber(number.type, number.number)
private fun writeNumber(type: DataType, number: Double) =
xml.writeTextNode("number", listOf(Pair("type", type.name)), intOrDouble(type, number).toString(), false)
private fun write(symbol: PtIdentifier) {
xml.elt("symbol")
xml.attr("name", strTargetName(symbol))
xml.attr("type", symbol.type.name)
xml.endElt()
}
private fun write(assign: PtAssignment) {
xml.elt("assign")
xml.pos(assign.position)
xml.startChildren()
write(assign.target)
writeNode(assign.value)
xml.endElt()
}
private fun write(ifElse: PtIfElse) {
xml.elt("ifelse")
xml.pos(ifElse.position)
xml.startChildren()
xml.elt("condition")
xml.startChildren()
writeNode(ifElse.condition)
xml.endElt()
xml.elt("true")
xml.pos(ifElse.ifScope.position)
xml.startChildren()
writeNode(ifElse.ifScope)
xml.endElt()
if(ifElse.elseScope.children.isNotEmpty()) {
xml.elt("false")
xml.pos(ifElse.elseScope.position)
xml.startChildren()
writeNode(ifElse.elseScope)
xml.endElt()
}
xml.endElt()
}
private fun write(ret: PtReturn) {
xml.elt("return")
if(ret.hasValue) {
xml.startChildren()
writeNode(ret.value!!)
}
xml.endElt()
}
private fun write(incdec: PtPostIncrDecr) {
if(incdec.operator=="++") xml.elt("inc") else xml.elt("dec")
xml.startChildren()
write(incdec.target)
xml.endElt()
}
private fun write(label: PtLabel) {
xml.elt("label")
xml.attr("name", label.scopedName.joinToString("."))
xml.pos(label.position)
xml.endElt()
}
private fun write(block: PtBlock) {
xml.elt("block")
xml.attr("name", block.scopedName.joinToString("."))
if(block.address!=null)
xml.attr("address", block.address!!.toString())
xml.attr("library", block.library.toString())
xml.pos(block.position)
xml.startChildren()
block.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(memMapped: PtMemMapped) {
xml.writeTextNode("memvar",
listOf(
Pair("name", memMapped.scopedName.joinToString(".")),
Pair("type", memMapped.type.name)
),
memMapped.address.toString(),
false)
}
private fun write(target: PtAssignTarget) {
xml.elt("target")
xml.startChildren()
if(target.identifier!=null) {
writeNode(target.identifier!!)
} else if(target.memory!=null) {
writeNode(target.memory!!)
} else if(target.array!=null) {
writeNode(target.array!!)
} else
throw InternalCompilerException("weird assign target")
xml.endElt()
}
private fun write(jump: PtJump) {
xml.elt("jump")
if(jump.identifier!=null) xml.attr("symbol", strTargetName(jump.identifier!!))
else if(jump.address!=null) xml.attr("address", jump.address!!.toString())
else if(jump.generatedLabel!=null) xml.attr("label", jump.generatedLabel!!)
else
throw InternalCompilerException("weird jump target")
xml.endElt()
}
private fun write(sub: PtSub) {
xml.elt("sub")
xml.attr("name", sub.scopedName.joinToString("."))
if(sub.inline)
xml.attr("inline", "true")
xml.attr("returntype", sub.returntype?.toString() ?: "VOID")
xml.pos(sub.position)
xml.startChildren()
if(sub.parameters.isNotEmpty()) {
xml.elt("parameters")
xml.startChildren()
sub.parameters.forEach { write(it) }
xml.endElt()
}
sub.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(parameter: PtSubroutineParameter, registerOrStatusflag: RegisterOrStatusflag? = null) {
xml.elt("param")
xml.attr("name", parameter.name)
xml.attr("type", parameter.type.name)
if(registerOrStatusflag?.statusflag!=null) {
xml.attr("statusflag", registerOrStatusflag.statusflag!!.toString())
}
if(registerOrStatusflag?.registerOrPair!=null){
xml.attr("registers", registerOrStatusflag.registerOrPair!!.name)
}
xml.endElt()
}
private fun write(asmSub: PtAsmSub) {
if(asmSub.address!=null) {
xml.elt("romsub")
xml.attr("name", asmSub.scopedName.joinToString("."))
xml.attr("address", asmSub.address!!.toString())
if(asmSub.inline)
xml.attr("inline", "true")
xml.pos(asmSub.position)
xml.startChildren()
paramsEtcetera(asmSub)
xml.endElt()
}
else {
xml.elt("asmsub")
xml.attr("name", asmSub.scopedName.joinToString("."))
if(asmSub.inline)
xml.attr("inline", "true")
xml.pos(asmSub.position)
xml.startChildren()
paramsEtcetera(asmSub)
xml.elt("code")
xml.startChildren()
asmSub.children.forEach { writeNode(it) }
xml.endElt()
xml.endElt()
}
}
private fun paramsEtcetera(asmSub: PtAsmSub) {
if(asmSub.parameters.isNotEmpty()) {
xml.elt("parameters")
xml.startChildren()
asmSub.parameters.forEach { (param, reg) -> write(param, reg) }
xml.endElt()
}
if(asmSub.clobbers.isNotEmpty()) {
xml.elt("clobbers")
xml.attr("registers", asmSub.clobbers.joinToString(",") { it.name })
xml.endElt()
}
if(asmSub.retvalRegisters.isNotEmpty()) {
xml.elt("returns")
xml.startChildren()
asmSub.retvalRegisters.forEach {
xml.elt("register")
if(it.statusflag!=null)
xml.attr("statusflag", it.statusflag!!.toString())
if(it.registerOrPair!=null)
xml.attr("registers", it.registerOrPair!!.toString())
xml.endElt()
}
xml.endElt()
}
}
private fun write(constant: PtConstant) {
xml.writeTextNode("const",
listOf(
Pair("name", constant.scopedName.joinToString(".")),
Pair("type", constant.type.name)
),
intOrDouble(constant.type, constant.value).toString(), false)
}
private fun write(variable: PtVariable) {
// the variable declaration nodes are still present in the Ast,
// but the Symboltable should be used look up their details.
xml.elt("vardecl")
xml.attr("name", variable.scopedName.joinToString("."))
xml.attr("type", variable.type.name)
if(variable.arraySize!=null)
xml.attr("arraysize", variable.arraySize.toString())
if(variable.value!=null) {
// static initialization value
xml.startChildren()
writeNode(variable.value!!)
}
xml.endElt()
}
private fun strTargetName(ident: PtIdentifier): String = ident.targetName.joinToString(".")
private fun strTargetName(call: PtFunctionCall): String = call.functionName.joinToString(".")
private fun intOrDouble(type: DataType, value: Double): Number =
if(type in IntegerDatatypes) value.toInt() else value
}

View File

@ -0,0 +1,30 @@
package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyGenerator
import prog8.code.core.IAssemblyProgram
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
class CodeGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
private val options: CompilationOptions,
private val errors: IErrorReporter
): IAssemblyGenerator {
override fun compileToAssembly(): IAssemblyProgram? {
// you could write a code generator directly on the PtProgram AST,
// but you can also use the Intermediate Representation to build a codegen on:
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate()
// this stub only writes the IR program to disk but doesn't generate anything else.
IRFileWriter(irProgram).writeFile()
println("** experimental codegen stub: no assembly generated **")
return null
}
}

View File

@ -1,89 +0,0 @@
package prog8.codegen.experimental
import prog8.code.core.Position
import java.util.*
import javax.xml.stream.XMLStreamWriter
class IndentingXmlWriter(val xml: XMLStreamWriter): XMLStreamWriter by xml {
private var indent = 0
private var content = Stack<Boolean>()
fun doc(version: String? = null) = if(version==null) writeStartDocument() else writeStartDocument(version)
fun endDoc() = writeEndDocument()
fun elt(name: String) = writeStartElement(name)
fun attr(name: String, value: String) = writeAttribute(name, value)
fun attrs(attributes: List<Pair<String, String>>) = attributes.forEach { writeAttribute(it.first, it.second) }
fun startChildren() {
xml.writeCharacters("\n")
content.pop()
content.push(true)
}
fun endElt(writeIndent: Boolean=true) = writeEndElement(writeIndent)
fun pos(pos: Position) = writeAttribute("src", pos.toString())
fun comment(text: String) {
writeComment(text)
writeCharacters("\n")
}
override fun writeStartDocument() {
xml.writeStartDocument()
xml.writeCharacters("\n")
content.push(true)
}
override fun writeStartDocument(version: String) {
xml.writeStartDocument(version)
xml.writeCharacters("\n")
content.push(true)
}
override fun writeEndDocument() {
xml.writeEndDocument()
xml.writeCharacters("\n")
require(indent==0)
require(content.size==1)
}
override fun writeStartElement(name: String) {
xml.writeCharacters(" ".repeat(indent))
xml.writeStartElement(name)
indent++
content.push(false)
}
override fun writeStartElement(name: String, ns: String) {
xml.writeCharacters(" ".repeat(indent))
xml.writeStartElement(name, ns)
indent++
content.push(false)
}
fun writeEndElement(writeIndents: Boolean) {
indent--
if(content.pop() && writeIndents)
xml.writeCharacters(" ".repeat(indent))
xml.writeEndElement()
xml.writeCharacters("\n")
}
override fun writeEndElement() = writeEndElement(true)
override fun writeStartElement(name: String, ns: String, p2: String) {
xml.writeCharacters(" ".repeat(indent))
xml.writeStartElement(name, ns, p2)
indent++
content.push(false)
}
fun writeTextNode(name: String, attrs: List<Pair<String, String>>, text: String, cdata: Boolean = true) {
xml.writeCharacters(" ".repeat(indent))
xml.writeStartElement(name)
attrs.forEach { (name, value) -> xml.writeAttribute(name, value) }
if(cdata)
xml.writeCData(text)
else
xml.writeCharacters(text)
xml.writeEndElement()
xml.writeCharacters("\n")
}
}

View File

@ -0,0 +1,64 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':virtualmachine')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="virtualmachine" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
</component>
</module>

View File

@ -1,15 +1,18 @@
package prog8.codegen.virtual package prog8.codegen.intermediate
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.SignedDatatypes import prog8.code.core.SignedDatatypes
import prog8.vm.Opcode import prog8.intermediate.IRCodeChunk
import prog8.vm.VmDataType import prog8.intermediate.IRCodeInstruction
import prog8.intermediate.Opcode
import prog8.intermediate.VmDataType
internal class AssignmentGen(private val codeGen: CodeGen, private val expressionEval: ExpressionGen) { internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
internal fun translate(assignment: PtAssignment): VmCodeChunk { internal fun translate(assignment: PtAssignment): IRCodeChunk {
if(assignment.target.children.single() is PtMachineRegister) if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
@ -19,56 +22,78 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
translateRegularAssign(assignment) translateRegularAssign(assignment)
} }
private fun translateInplaceAssign(assignment: PtAssignment): VmCodeChunk { private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunk {
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array
return if(ident!=null) { return if(ident!=null) {
val address = codeGen.allocations.get(ident.targetName) assignSelfInMemory(ident.targetName.joinToString("."), assignment.value, assignment)
assignSelfInMemory(address, assignment.value, assignment)
} else if(memory != null) { } else if(memory != null) {
if(memory.address is PtNumber) if(memory.address is PtNumber)
assignSelfInMemory((memory.address as PtNumber).number.toInt(), assignment.value, assignment) assignSelfInMemoryKnownAddress((memory.address as PtNumber).number.toInt(), assignment.value, assignment)
else else
fallbackAssign(assignment) fallbackAssign(assignment)
} else if(array!=null) { } else if(array!=null) {
// TODO in-place array element assignment? // NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
// will be optimized later and have the double assignments removed.
fallbackAssign(assignment) fallbackAssign(assignment)
} else { } else {
fallbackAssign(assignment) fallbackAssign(assignment)
} }
} }
private fun assignSelfInMemory( private fun assignSelfInMemoryKnownAddress(
address: Int, address: Int,
value: PtExpression, value: PtExpression,
origAssign: PtAssignment origAssign: PtAssignment
): VmCodeChunk { ): IRCodeChunk {
val vmDt = codeGen.vmType(value.type) val vmDt = codeGen.vmType(value.type)
val code = VmCodeChunk() val code = IRCodeChunk(origAssign.position)
when(value) { when(value) {
is PtIdentifier -> return code // do nothing, x=x null assignment. is PtIdentifier -> return code // do nothing, x=x null assignment.
is PtMachineRegister -> return code // do nothing, reg=reg null assignment is PtMachineRegister -> return code // do nothing, reg=reg null assignment
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address) is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null, value.position)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, origAssign) is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign)
is PtMemoryByte -> { is PtMemoryByte -> {
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt())) return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
code // do nothing, mem=mem null assignment. code // do nothing, mem=mem null assignment.
else { else {
// read and write a (i/o) memory location to itself. // read and write a (i/o) memory location to itself.
val tempReg = codeGen.vmRegisters.nextFree() val tempReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address) code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address) code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
code code
} }
} }
else -> return fallbackAssign(origAssign) else -> return fallbackAssign(origAssign)
} }
} }
private fun fallbackAssign(origAssign: PtAssignment): VmCodeChunk { private fun assignSelfInMemory(
symbol: String,
value: PtExpression,
origAssign: PtAssignment
): IRCodeChunk {
val vmDt = codeGen.vmType(value.type)
val code = IRCodeChunk(origAssign.position)
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, null, symbol, value.position)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
is PtMemoryByte -> {
val tempReg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
return code
}
else -> return fallbackAssign(origAssign)
}
}
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunk {
if (codeGen.options.slowCodegenWarnings) if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position) codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
return translateRegularAssign(origAssign) return translateRegularAssign(origAssign)
@ -79,53 +104,73 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
operand: PtExpression, operand: PtExpression,
vmDt: VmDataType, vmDt: VmDataType,
signed: Boolean, signed: Boolean,
address: Int, knownAddress: Int?,
symbol: String?,
origAssign: PtAssignment origAssign: PtAssignment
): VmCodeChunk { ): IRCodeChunk {
if(knownAddress!=null) {
when (operator) { when (operator) {
"+" -> return expressionEval.operatorPlusInplace(address, vmDt, operand) "+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand)
"-" -> return expressionEval.operatorMinusInplace(address, vmDt, operand) "-" -> return expressionEval.operatorMinusInplace(knownAddress, null, vmDt, operand)
"*" -> return expressionEval.operatorMultiplyInplace(address, vmDt, operand) "*" -> return expressionEval.operatorMultiplyInplace(knownAddress, null, vmDt, operand)
"/" -> return expressionEval.operatorDivideInplace(address, vmDt, signed, operand) "/" -> return expressionEval.operatorDivideInplace(knownAddress, null, vmDt, signed, operand)
"|" -> return expressionEval.operatorOrInplace(address, vmDt, operand) "|" -> return expressionEval.operatorOrInplace(knownAddress, null, vmDt, operand)
"&" -> return expressionEval.operatorAndInplace(address, vmDt, operand) "&" -> return expressionEval.operatorAndInplace(knownAddress, null, vmDt, operand)
"^" -> return expressionEval.operatorXorInplace(address, vmDt, operand) "^" -> return expressionEval.operatorXorInplace(knownAddress, null, vmDt, operand)
"<<" -> return expressionEval.operatorShiftLeftInplace(address, vmDt, operand) "<<" -> return expressionEval.operatorShiftLeftInplace(knownAddress, null, vmDt, operand)
">>" -> return expressionEval.operatorShiftRightInplace(address, vmDt, signed, operand) ">>" -> return expressionEval.operatorShiftRightInplace(knownAddress, null, vmDt, signed, operand)
else -> {} else -> {}
} }
} else {
symbol!!
when (operator) {
"+" -> return expressionEval.operatorPlusInplace(null, symbol, vmDt, operand)
"-" -> return expressionEval.operatorMinusInplace(null, symbol, vmDt, operand)
"*" -> return expressionEval.operatorMultiplyInplace(null, symbol, vmDt, operand)
"/" -> return expressionEval.operatorDivideInplace(null, symbol, vmDt, signed, operand)
"|" -> return expressionEval.operatorOrInplace(null, symbol, vmDt, operand)
"&" -> return expressionEval.operatorAndInplace(null, symbol, vmDt, operand)
"^" -> return expressionEval.operatorXorInplace(null, symbol, vmDt, operand)
"<<" -> return expressionEval.operatorShiftLeftInplace(null, symbol, vmDt, operand)
">>" -> return expressionEval.operatorShiftRightInplace(null, symbol, vmDt, signed, operand)
else -> {}
}
}
return fallbackAssign(origAssign) return fallbackAssign(origAssign)
} }
private fun inplacePrefix(operator: String, vmDt: VmDataType, address: Int): VmCodeChunk { private fun inplacePrefix(operator: String, vmDt: VmDataType, knownAddress: Int?, addressSymbol: String?, position: Position): IRCodeChunk {
val code= VmCodeChunk() val code= IRCodeChunk(position)
when(operator) { when(operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
code += VmCodeInstruction(Opcode.NEGM, vmDt, value = address) code += if(knownAddress!=null)
IRCodeInstruction(Opcode.NEGM, vmDt, value = knownAddress)
else
IRCodeInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
} }
"~" -> { "~" -> {
val regMask = codeGen.vmRegisters.nextFree() val regMask = codeGen.vmRegisters.nextFree()
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask) code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += VmCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address) code += if(knownAddress!=null)
} IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
"not" -> { else
code += VmCodeInstruction(Opcode.NOTM, vmDt, value = address) IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
} }
else -> throw AssemblyError("weird prefix operator") else -> throw AssemblyError("weird prefix operator")
} }
return code return code
} }
private fun translateRegularAssign(assignment: PtAssignment): VmCodeChunk { private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunk {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call. // note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array
val vmDt = codeGen.vmType(assignment.value.type) val vmDt = codeGen.vmType(assignment.value.type)
val code = VmCodeChunk() val code = IRCodeChunk(assignment.position)
var resultRegister = -1 var resultRegister = -1
var resultFpRegister = -1 var resultFpRegister = -1
val zero = codeGen.isZero(assignment.value) val zero = codeGen.isZero(assignment.value)
@ -145,19 +190,18 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
} }
} }
if(ident!=null) { if(ident!=null) {
val address = codeGen.allocations.get(ident.targetName) val symbol = ident.targetName.joinToString(".")
code += if(zero) { code += if(zero) {
VmCodeInstruction(Opcode.STOREZM, vmDt, value = address) IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
} else { } else {
if (vmDt == VmDataType.FLOAT) if (vmDt == VmDataType.FLOAT)
VmCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value = address) IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
else else
VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value = address) IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
} }
} }
else if(array!=null) { else if(array!=null) {
val variable = array.variable.targetName val variable = array.variable.targetName.joinToString(".")
var variableAddr = codeGen.allocations.get(variable)
val itemsize = codeGen.program.memsizer.memorySize(array.type) val itemsize = codeGen.program.memsizer.memorySize(array.type)
if(array.variable.type==DataType.UWORD) { if(array.variable.type==DataType.UWORD) {
@ -168,38 +212,43 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
throw AssemblyError("non-array var indexing requires bytes index") throw AssemblyError("non-array var indexing requires bytes index")
val idxReg = codeGen.vmRegisters.nextFree() val idxReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(array.index, idxReg, -1) code += expressionEval.translateExpression(array.index, idxReg, -1)
code += VmCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, value = variableAddr) if(zero) {
// there's no STOREZIX instruction
resultRegister = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
}
code += IRCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
return code return code
} }
val fixedIndex = constIntValue(array.index) val fixedIndex = constIntValue(array.index)
if(zero) { if(zero) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize val offset = fixedIndex*itemsize
code += VmCodeInstruction(Opcode.STOREZM, vmDt, value=variableAddr) code += IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset")
} else { } else {
val indexReg = codeGen.vmRegisters.nextFree() val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg) code += loadIndexReg(array, itemsize, indexReg, array.position)
code += VmCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, value=variableAddr) code += IRCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable)
} }
} else { } else {
if(vmDt== VmDataType.FLOAT) { if(vmDt== VmDataType.FLOAT) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize val offset = fixedIndex*itemsize
code += VmCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, value=variableAddr) code += IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset")
} else { } else {
val indexReg = codeGen.vmRegisters.nextFree() val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg) code += loadIndexReg(array, itemsize, indexReg, array.position)
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr) code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
} }
} else { } else {
if(fixedIndex!=null) { if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize val offset = fixedIndex*itemsize
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, value=variableAddr) code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset")
} else { } else {
val indexReg = codeGen.vmRegisters.nextFree() val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg) code += loadIndexReg(array, itemsize, indexReg, array.position)
code += VmCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, value=variableAddr) code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
} }
} }
} }
@ -208,19 +257,19 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
require(vmDt== VmDataType.BYTE) require(vmDt== VmDataType.BYTE)
if(zero) { if(zero) {
if(memory.address is PtNumber) { if(memory.address is PtNumber) {
code += VmCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) code += IRCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1) code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += VmCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) code += IRCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
} }
} else { } else {
if(memory.address is PtNumber) { if(memory.address is PtNumber) {
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1) code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += VmCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) code += IRCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
} }
} }
} }
@ -229,8 +278,8 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
return code return code
} }
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): VmCodeChunk { private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int, position: Position): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(position)
if(itemsize==1) { if(itemsize==1) {
code += expressionEval.translateExpression(array.index, indexReg, -1) code += expressionEval.translateExpression(array.index, indexReg, -1)
} }

View File

@ -1,16 +1,17 @@
package prog8.codegen.virtual package prog8.codegen.intermediate
import prog8.code.StStaticVariable import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.vm.Opcode import prog8.code.core.Position
import prog8.intermediate.*
import prog8.vm.Syscall import prog8.vm.Syscall
import prog8.vm.VmDataType
internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: ExpressionGen) {
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
return when(call.name) { return when(call.name) {
"any" -> funcAny(call, resultRegister) "any" -> funcAny(call, resultRegister)
"all" -> funcAll(call, resultRegister) "all" -> funcAll(call, resultRegister)
@ -25,20 +26,19 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
"rsave", "rsave",
"rsavex", "rsavex",
"rrestore", "rrestore",
"rrestorex" -> VmCodeChunk() // vm doesn't have registers to save/restore "rrestorex" -> IRCodeChunk(call.position) // vm doesn't have registers to save/restore
"rnd" -> funcRnd(resultRegister) "rnd" -> funcRnd(resultRegister, call.position)
"rndw" -> funcRndw(resultRegister) "rndw" -> funcRndw(resultRegister, call.position)
"callfar" -> throw AssemblyError("callfar() is for cx16 target only") "callfar" -> throw AssemblyError("callfar() is for cx16 target only")
"callrom" -> throw AssemblyError("callrom() is for cx16 target only") "callrom" -> throw AssemblyError("callrom() is for cx16 target only")
"msb" -> funcMsb(call, resultRegister) "msb" -> funcMsb(call, resultRegister)
"lsb" -> funcLsb(call, resultRegister) "lsb" -> funcLsb(call, resultRegister)
"boolean" -> funcBoolean(call, resultRegister)
"memory" -> funcMemory(call, resultRegister) "memory" -> funcMemory(call, resultRegister)
"peek" -> funcPeek(call, resultRegister) "peek" -> funcPeek(call, resultRegister)
"peekw" -> funcPeekW(call, resultRegister) "peekw" -> funcPeekW(call, resultRegister)
"poke" -> funcPoke(call) "poke" -> funcPoke(call)
"pokew" -> funcPokeW(call) "pokew" -> funcPokeW(call)
"pokemon" -> VmCodeChunk() "pokemon" -> IRCodeChunk(call.position)
"mkword" -> funcMkword(call, resultRegister) "mkword" -> funcMkword(call, resultRegister)
"sort" -> funcSort(call) "sort" -> funcSort(call)
"reverse" -> funcReverse(call) "reverse" -> funcReverse(call)
@ -46,24 +46,24 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
"ror" -> funcRolRor(Opcode.ROXR, call, resultRegister) "ror" -> funcRolRor(Opcode.ROXR, call, resultRegister)
"rol2" -> funcRolRor(Opcode.ROL, call, resultRegister) "rol2" -> funcRolRor(Opcode.ROL, call, resultRegister)
"ror2" -> funcRolRor(Opcode.ROR, call, resultRegister) "ror2" -> funcRolRor(Opcode.ROR, call, resultRegister)
else -> TODO("builtinfunc ${call.name}") else -> throw AssemblyError("missing builtinfunc for ${call.name}")
} }
} }
private fun funcCmp(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val leftRegister = codeGen.vmRegisters.nextFree() val leftRegister = codeGen.vmRegisters.nextFree()
val rightRegister = codeGen.vmRegisters.nextFree() val rightRegister = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], leftRegister, -1) code += exprGen.translateExpression(call.args[0], leftRegister, -1)
code += exprGen.translateExpression(call.args[1], rightRegister, -1) code += exprGen.translateExpression(call.args[1], rightRegister, -1)
code += VmCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister) code += IRCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
return code return code
} }
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val syscall = val syscall =
when (array.dt) { when (array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
@ -74,14 +74,14 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
code += exprGen.translateExpression(call.args[0], 0, -1) code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length) code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value = syscall.ordinal) code += IRCodeInstruction(Opcode.SYSCALL, value = syscall.ordinal)
if (resultRegister != 0) if (resultRegister != 0)
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0) code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0)
return code return code
} }
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall = val syscall =
@ -93,42 +93,42 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
DataType.ARRAY_F -> Syscall.ALL_FLOAT DataType.ARRAY_F -> Syscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1) code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0) if(resultRegister!=0)
code += VmCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
return code return code
} }
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val sourceDt = call.args.single().type val sourceDt = call.args.single().type
if(sourceDt!=DataType.UWORD) { if(sourceDt!=DataType.UWORD) {
code += exprGen.translateExpression(call.args[0], resultRegister, -1) code += exprGen.translateExpression(call.args[0], resultRegister, -1)
when (sourceDt) { when (sourceDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister) code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
} }
DataType.BYTE -> { DataType.BYTE -> {
val andReg = codeGen.vmRegisters.nextFree()
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=andReg, value=0x80) val compareReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=andReg, reg2=resultRegister) code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=andReg, labelSymbol = notNegativeLabel) code += IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80)
code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister) code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister) code += IRCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
code += VmCodeLabel(notNegativeLabel) code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel)
} }
DataType.WORD -> { DataType.WORD -> {
val andReg = codeGen.vmRegisters.nextFree()
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=andReg, value=0x8000) val compareReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=andReg, reg2=resultRegister) code += IRCodeInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=andReg, labelSymbol = notNegativeLabel) code += IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000)
code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister) code += IRCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
code += VmCodeLabel(notNegativeLabel) code += IRCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel)
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
@ -136,55 +136,55 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
return code return code
} }
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg) code += IRCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg)
return code return code
} }
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg) code += IRCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg)
return code return code
} }
private fun funcPop(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg) code += IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg)
code += assignRegisterTo(call.args.single(), reg) code += assignRegisterTo(call.args.single(), reg)
return code return code
} }
private fun funcPopw(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=reg) code += IRCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=reg)
code += assignRegisterTo(call.args.single(), reg) code += assignRegisterTo(call.args.single(), reg)
return code return code
} }
private fun funcPush(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg) code += IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg)
return code return code
} }
private fun funcPushw(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) code += exprGen.translateExpression(call.args.single(), reg, -1)
code += VmCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg) code += IRCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg)
return code return code
} }
private fun funcReverse(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunk {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val sortSyscall = val sortSyscall =
@ -194,14 +194,14 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
DataType.ARRAY_F -> Syscall.REVERSE_FLOATS DataType.ARRAY_F -> Syscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1) code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal) code += IRCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
return code return code
} }
private fun funcSort(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunk {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val sortSyscall = val sortSyscall =
@ -211,185 +211,153 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
DataType.ARRAY_UW -> Syscall.SORT_UWORD DataType.ARRAY_UW -> Syscall.SORT_UWORD
DataType.ARRAY_W -> Syscall.SORT_WORD DataType.ARRAY_W -> Syscall.SORT_WORD
DataType.STR -> Syscall.SORT_UBYTE DataType.STR -> Syscall.SORT_UBYTE
DataType.ARRAY_F -> throw java.lang.IllegalArgumentException("sorting a floating point array is not supported") DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
else -> throw IllegalArgumentException("weird type to sort") else -> throw IllegalArgumentException("weird type to sort")
} }
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1) code += exprGen.translateExpression(call.args[0], 0, -1)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += VmCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal) code += IRCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
return code return code
} }
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val msbReg = codeGen.vmRegisters.nextFree() val msbReg = codeGen.vmRegisters.nextFree()
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], msbReg, -1) code += exprGen.translateExpression(call.args[0], msbReg, -1)
code += exprGen.translateExpression(call.args[1], resultRegister, -1) code += exprGen.translateExpression(call.args[1], resultRegister, -1)
code += VmCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg) code += IRCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
return code return code
} }
private fun funcPokeW(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
if(codeGen.isZero(call.args[1])) { if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.WORD, value = address) code += IRCodeInstruction(Opcode.STOREZM, VmDataType.WORD, value = address)
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += VmCodeInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg) code += IRCodeInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg)
} }
} else { } else {
val valueReg = codeGen.vmRegisters.nextFree() val valueReg = codeGen.vmRegisters.nextFree()
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1) code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address) code += IRCodeInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address)
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1) code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg) code += IRCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg)
} }
} }
return code return code
} }
private fun funcPoke(call: PtBuiltinFunctionCall): VmCodeChunk { private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
if(codeGen.isZero(call.args[1])) { if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address) code += IRCodeInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address)
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += VmCodeInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg) code += IRCodeInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg)
} }
} else { } else {
val valueReg = codeGen.vmRegisters.nextFree() val valueReg = codeGen.vmRegisters.nextFree()
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1) code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address) code += IRCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address)
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1) code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += VmCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg) code += IRCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
} }
} }
return code return code
} }
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
if(call.args[0] is PtNumber) { if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address) code += IRCodeInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address)
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1) code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg) code += IRCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
} }
return code return code
} }
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
if(call.args[0] is PtNumber) { if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += VmCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address) code += IRCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address)
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1) code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += VmCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg) code += IRCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
} }
return code return code
} }
private fun funcRnd(resultRegister: Int): VmCodeChunk { private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(position)
code += VmCodeInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister) code += IRCodeInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
return code return code
} }
private fun funcRndw(resultRegister: Int): VmCodeChunk { private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(position)
code += VmCodeInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister) code += IRCodeInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
return code return code
} }
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val name = (call.args[0] as PtString).value val name = (call.args[0] as PtString).value
val size = (call.args[1] as PtNumber).number.toUInt() val size = (call.args[1] as PtNumber).number.toUInt()
val align = (call.args[2] as PtNumber).number.toUInt() val align = (call.args[2] as PtNumber).number.toUInt()
val existing = codeGen.allocations.getMemorySlab(name) val label = codeGen.addMemorySlab(name, size, align, call.position)
val address = if(existing==null) val code = IRCodeChunk(call.position)
codeGen.allocations.allocateMemorySlab(name, size, align) code += IRCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, labelSymbol = label)
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 return code
} }
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args.single(), resultRegister, -1) 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. // 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 return code
} }
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args.single(), resultRegister, -1) code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister) code += IRCodeInstruction(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. // 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 return code
} }
private fun funcBoolean(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = VmCodeChunk()
val vmDt = codeGen.vmType(call.args[0].type) val vmDt = codeGen.vmType(call.args[0].type)
when (vmDt) { val code = IRCodeChunk(call.position)
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 += exprGen.translateExpression(call.args[0], resultRegister, -1)
code += VmCodeInstruction(opcode, vmDt, reg1=resultRegister) code += IRCodeInstruction(opcode, vmDt, reg1=resultRegister)
code += assignRegisterTo(call.args[0], resultRegister) code += assignRegisterTo(call.args[0], resultRegister)
return code return code
} }
private fun assignRegisterTo(target: PtExpression, register: Int): VmCodeChunk { private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunk {
val code = VmCodeChunk() val code = IRCodeChunk(target.position)
val assignment = PtAssignment(target.position) val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position) val assignTarget = PtAssignTarget(target.position)
assignTarget.children.add(target) assignTarget.children.add(target)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
package prog8.codegen.intermediate
import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize() {
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.forEach { chunk ->
// we don't optimize Inline Asm chunks here.
if(chunk is IRCodeChunk) {
do {
val indexedInstructions = chunk.lines.withIndex()
.filter { it.value is IRCodeInstruction }
.map { IndexedValue(it.index, (it.value as IRCodeInstruction).ins) }
val changed = removeNops(chunk, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk, indexedInstructions)
|| removeWeirdBranches(chunk, indexedInstructions)
|| removeDoubleSecClc(chunk, indexedInstructions)
|| cleanupPushPop(chunk, indexedInstructions)
// TODO other optimizations:
// more complex optimizations such as unused registers
} while (changed)
}
}
}
}
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// push followed by pop to same target, or different target->replace with load
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.PUSH) {
if(idx < chunk.lines.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
if(insAfter!=null && insAfter.ins.opcode == Opcode.POP) {
if(ins.reg1==insAfter.ins.reg1) {
chunk.lines.removeAt(idx)
chunk.lines.removeAt(idx)
} else {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOADR, ins.type, reg1=insAfter.ins.reg1, reg2=ins.reg1)
chunk.lines.removeAt(idx+1)
}
changed = true
}
}
}
}
return changed
}
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// double sec, clc
// sec+clc or clc+sec
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
if(idx < chunk.lines.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
if(insAfter?.ins?.opcode == ins.opcode) {
chunk.lines.removeAt(idx)
changed = true
}
else if(ins.opcode== Opcode.SEC && insAfter?.ins?.opcode== Opcode.CLC) {
chunk.lines.removeAt(idx)
changed = true
}
else if(ins.opcode== Opcode.CLC && insAfter?.ins?.opcode== Opcode.SEC) {
chunk.lines.removeAt(idx)
changed = true
}
}
}
}
return changed
}
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// jump/branch to label immediately below
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
val labelSymbol = ins.labelSymbol
if(ins.opcode== Opcode.JUMP && labelSymbol!=null) {
// if jumping to label immediately following this
if(idx < chunk.lines.size-1) {
val label = chunk.lines[idx+1] as? IRCodeLabel
if(labelSymbol.size==1 && label?.name == labelSymbol[0]) {
chunk.lines.removeAt(idx)
changed = true
}
}
}
}
return changed
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
when (ins.opcode) {
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
if (ins.value == 1) {
chunk.lines.removeAt(idx)
changed = true
}
}
Opcode.ADD, Opcode.SUB -> {
if (ins.value == 1) {
chunk.lines[idx] = IRCodeInstruction(
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
ins.type,
ins.reg1
)
changed = true
} else if (ins.value == 0) {
chunk.lines.removeAt(idx)
changed = true
}
}
Opcode.AND -> {
if (ins.value == 0) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
changed = true
} else if (ins.value == 255 && ins.type == VmDataType.BYTE) {
chunk.lines.removeAt(idx)
changed = true
} else if (ins.value == 65535 && ins.type == VmDataType.WORD) {
chunk.lines.removeAt(idx)
changed = true
}
}
Opcode.OR -> {
if (ins.value == 0) {
chunk.lines.removeAt(idx)
changed = true
} else if ((ins.value == 255 && ins.type == VmDataType.BYTE) || (ins.value == 65535 && ins.type == VmDataType.WORD)) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
changed = true
}
}
Opcode.XOR -> {
if (ins.value == 0) {
chunk.lines.removeAt(idx)
changed = true
}
}
else -> {}
}
}
return changed
}
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.NOP) {
changed = true
chunk.lines.removeAt(idx)
}
}
return changed
}
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
var changed = false
indexedInstructions.forEach { (idx, ins) ->
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
// TODO: detect multiple float ffrom/fto to the same target, only keep first
// TODO: detect multiple sequential rnd with same reg1, only keep one
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
// TODO: detect multiple same ands, ors; only keep first
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them
// ...
}
return changed
}
}

View File

@ -0,0 +1,27 @@
package prog8.codegen.intermediate
import prog8.code.core.AssemblyError
internal class RegisterPool {
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 (int)")
return result
}
fun nextFreeFloat(): Int {
val result = firstFreeFloat
firstFreeFloat++
if(firstFreeFloat>65535)
throw AssemblyError("out of virtual registers (fp)")
return result
}
}

View File

@ -0,0 +1,20 @@
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = 0
override fun memorySize(arrayDt: DataType, numElements: Int) = 0
}
internal object DummyStringEncoder : IStringEncoding {
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
return emptyList()
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
return ""
}
}

View File

@ -0,0 +1,179 @@
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTable
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.codegen.intermediate.IRPeepholeOptimizer
import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(lines: List<IRCodeLine>): IRProgram {
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
val chunk = IRCodeChunk(Position.DUMMY)
for(line in lines)
chunk += line
sub += chunk
block += sub
val st = SymbolTable()
val target = VMTarget()
val options = CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
emptyList(),
floats = false,
noSysInit = true,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
val prog = IRProgram("test", st, options, target)
prog.addBlock(block)
return prog
}
fun IRProgram.lines(): List<IRCodeLine> = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }.flatMap { it.lines }
test("remove nops") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.JUMP, labelSymbol = "dummy"),
IRCodeInstruction(Opcode.NOP),
IRCodeInstruction(Opcode.NOP)
))
irProg.lines().size shouldBe 3
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.lines().size shouldBe 1
}
test("remove jmp to label below") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label"), // removed
IRCodeLabel("label"),
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label2"), // removed
IRCodeInstruction(Opcode.NOP), // removed
IRCodeLabel("label2"),
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label3"),
IRCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=1),
IRCodeLabel("label3")
))
irProg.lines().size shouldBe 8
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 5
(lines[0] as IRCodeLabel).name shouldBe "label"
(lines[1] as IRCodeLabel).name shouldBe "label2"
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.JUMP
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC
(lines[4] as IRCodeLabel).name shouldBe "label3"
}
test("remove double sec/clc") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.CLC),
IRCodeInstruction(Opcode.CLC),
IRCodeInstruction(Opcode.CLC)
))
irProg.lines().size shouldBe 6
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 1
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.CLC
}
test("push followed by pop") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42),
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=42),
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99),
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=222)
))
irProg.lines().size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 1
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOADR
(lines[0] as IRCodeInstruction).ins.reg1 shouldBe 222
(lines[0] as IRCodeInstruction).ins.reg2 shouldBe 99
}
test("remove useless div/mul, add/sub") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0)
))
irProg.lines().size shouldBe 10
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 4
}
test("replace add/sub 1 by inc/dec") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1)
))
irProg.lines().size shouldBe 2
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 2
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.DEC
}
test("remove useless and/or/xor") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 200),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 60000),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1)
))
irProg.lines().size shouldBe 8
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 4
}
test("replace and/or/xor by constant number") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255),
IRCodeInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535)
))
irProg.lines().size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 4
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[0] as IRCodeInstruction).ins.value shouldBe 0
(lines[1] as IRCodeInstruction).ins.value shouldBe 0
(lines[2] as IRCodeInstruction).ins.value shouldBe 255
(lines[3] as IRCodeInstruction).ins.value shouldBe 65535
}
})

View File

@ -3,6 +3,7 @@ plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
} }
java { java {
@ -24,13 +25,14 @@ compileTestKotlin {
} }
dependencies { dependencies {
implementation project(':virtualmachine')
implementation project(':codeAst')
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
} }
sourceSets { sourceSets {
@ -42,6 +44,22 @@ sourceSets {
srcDirs = ["${project.projectDir}/res"] srcDirs = ["${project.projectDir}/res"]
} }
} }
test {
java {
srcDir "${project.projectDir}/test"
}
}
} }
// note: there are no unit tests in this module! test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -4,13 +4,17 @@
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="module" module-name="codeAst" /> <orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="virtualmachine" /> <orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeGenIntermediate" />
</component> </component>
</module> </module>

View File

@ -1,138 +0,0 @@
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
internal class AssemblyProgram(override val name: String,
private val allocations: VariableAllocator
) : IAssemblyProgram {
private val globalInits = mutableListOf<VmCodeLine>()
private val blocks = mutableListOf<VmCodeChunk>()
override fun assemble(options: CompilationOptions): Boolean {
val outfile = options.outputDir / ("$name.p8virt")
println("write code to $outfile")
outfile.bufferedWriter().use { out ->
allocations.asVmMemory().forEach { (name, alloc) ->
out.write("; ${name.joinToString(".")}\n")
out.write(alloc + "\n")
}
out.write("------PROGRAM------\n")
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) }
}
return true
}
private fun BufferedWriter.writeLine(line: VmCodeLine) {
when(line) {
is VmCodeComment -> write("; ${line.comment}\n")
is VmCodeInstruction -> {
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")
}
}
fun addGlobalInits(chunk: VmCodeChunk) = globalInits.addAll(chunk.lines)
fun addBlock(block: VmCodeChunk) = blocks.add(block)
}
internal sealed class VmCodeLine
internal class VmCodeInstruction(
opcode: Opcode,
type: VmDataType?=null,
reg1: Int?=null, // 0-$ffff
reg2: Int?=null, // 0-$ffff
fpReg1: Int?=null, // 0-$ffff
fpReg2: Int?=null, // 0-$ffff
value: Int?=null, // 0-$ffff
fpValue: Float?=null,
labelSymbol: List<String>?=null // alternative to value for branch/call/jump labels
): VmCodeLine() {
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
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()
internal class VmCodeChunk(initial: VmCodeLine? = null) {
val lines = mutableListOf<VmCodeLine>()
init {
if(initial!=null)
lines.add(initial)
}
operator fun plusAssign(line: VmCodeLine) {
lines.add(line)
}
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

@ -1,831 +0,0 @@
package prog8.codegen.virtual
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.vm.Opcode
import prog8.vm.VmDataType
import kotlin.math.pow
internal class VmRegisterPool {
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 (int)")
return result
}
fun nextFreeFloat(): Int {
val result = firstFreeFloat
firstFreeFloat++
if(firstFreeFloat>65535)
throw AssemblyError("out of virtual registers (fp)")
return result
}
}
class CodeGen(internal val program: PtProgram,
internal val symbolTable: SymbolTable,
internal val options: CompilationOptions,
internal val errors: IErrorReporter
): IAssemblyGenerator {
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()
override fun compileToAssembly(): IAssemblyProgram? {
val vmprog = AssemblyProgram(program.name, allocations)
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()) {
vmprog.addBlock(translate(block))
}
println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}")
return vmprog
}
internal fun translateNode(node: PtNode): VmCodeChunk {
val code = when(node) {
is PtBlock -> translate(node)
is PtSub -> translate(node)
is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table
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 -> assignmentGen.translate(node)
is PtNodeGroup -> translateGroup(node.children)
is PtBuiltinFunctionCall -> translateBuiltinFunc(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 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,
is PtProgram,
is PtArrayIndexer,
is PtBinaryExpression,
is PtIdentifier,
is PtWhenChoice,
is PtPrefix,
is PtRange,
is PtAssignTarget,
is PtTypeCast,
is PtSubroutineParameter,
is PtNumber,
is PtArray,
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)
code.lines.add(0, VmCodeComment(node.position.toString()))
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()
val code = VmCodeChunk()
val valueReg = vmRegisters.nextFree()
val choiceReg = vmRegisters.nextFree()
val valueDt = vmType(whenStmt.value.type)
code += expressionEval.translateExpression(whenStmt.value, valueReg, -1)
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
val endLabel = createLabelName()
for (choice in choices) {
if(choice.isElse) {
code += translateNode(choice.statements)
} else {
val skipLabel = createLabelName()
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, labelSymbol = skipLabel)
code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
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, labelSymbol = matchLabel)
}
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = skipLabel)
code += VmCodeLabel(matchLabel)
code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
}
code += VmCodeLabel(skipLabel)
}
}
code += VmCodeLabel(endLabel)
return code
}
private fun translate(forLoop: PtForLoop): VmCodeChunk {
val loopvar = symbolTable.lookup(forLoop.variable.targetName) as StStaticVariable
val iterable = forLoop.iterable
val code = VmCodeChunk()
when(iterable) {
is PtRange -> {
if(iterable.from is PtNumber && iterable.to is PtNumber)
code += translateForInConstantRange(forLoop, loopvar)
else
code += translateForInNonConstantRange(forLoop, loopvar)
}
is PtIdentifier -> {
val arrayAddress = allocations.get(iterable.targetName)
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=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, 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
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")
}
return code
}
private fun translateForInNonConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
val iterable = forLoop.iterable as PtRange
val step = iterable.step.number.toInt()
if (step==0)
throw AssemblyError("step 0")
val indexReg = vmRegisters.nextFree()
val endvalueReg = vmRegisters.nextFree()
val loopvarAddress = allocations.get(loopvar.scopedName)
val loopvarDt = vmType(loopvar.dt)
val loopLabel = createLabelName()
val code = VmCodeChunk()
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)
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, step)
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, labelSymbol=loopLabel)
return code
}
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
val loopLabel = createLabelName()
val loopvarAddress = allocations.get(loopvar.scopedName)
val indexReg = 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()
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(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, step)
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
}
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
}
private fun addConstReg(dt: VmDataType, reg: Int, value: Int): VmCodeChunk {
val code = VmCodeChunk()
when(value) {
0 -> { /* do nothing */ }
1 -> {
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
}
2 -> {
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
}
-1 -> {
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
}
-2 -> {
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
}
else -> {
val valueReg = vmRegisters.nextFree()
if(value>0) {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= value)
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, reg2 = valueReg)
}
else {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= -value)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = valueReg)
}
}
}
return code
}
private fun addConstMem(dt: VmDataType, address: UInt, value: Int): VmCodeChunk {
val code = VmCodeChunk()
when(value) {
0 -> { /* do nothing */ }
1 -> {
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
}
2 -> {
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
}
-1 -> {
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
}
-2 -> {
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
}
else -> {
val valueReg = vmRegisters.nextFree()
val operandReg = vmRegisters.nextFree()
if(value>0) {
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = valueReg, reg2 = operandReg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
}
else {
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = valueReg, reg2 = operandReg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
}
}
}
return code
}
internal fun multiplyByConstFloat(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 = 0f)
} else {
val factorReg = vmRegisters.nextFreeFloat()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += VmCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpReg2 = factorReg)
}
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 {
if(ifElse.condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
val signed = ifElse.condition.left.type in arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val vmDt = vmType(ifElse.condition.left.type)
val code = VmCodeChunk()
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
}
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 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)
if(ident!=null) {
val address = allocations.get(ident.targetName)
code += VmCodeInstruction(operationMem, vmDt, value = address)
} else if(memory!=null) {
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 = constIntValue(array.index)
if(fixedIndex!=null) {
variableAddr += fixedIndex*itemsize
code += VmCodeInstruction(operationMem, vmDt, value=variableAddr)
} else {
val incReg = vmRegisters.nextFree()
val indexReg = vmRegisters.nextFree()
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")
return code
}
private fun translate(repeat: PtRepeatLoop): VmCodeChunk {
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, -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, labelSymbol = repeatLabel)
return code
}
private fun translate(jump: PtJump): VmCodeChunk {
val code = VmCodeChunk()
if(jump.address!=null)
throw AssemblyError("cannot jump to memory location in the vm target")
code += if(jump.generatedLabel!=null)
VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf(jump.generatedLabel!!))
else if(jump.identifier!=null)
VmCodeInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName)
else
throw AssemblyError("weird jump")
return code
}
private fun translateGroup(group: List<PtNode>): VmCodeChunk {
val code = VmCodeChunk()
group.forEach { code += translateNode(it) }
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 (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 {
val code = VmCodeChunk()
code += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
code += VmCodeLabel(sub.scopedName)
for (child in sub.children) {
code += translateNode(child)
}
code += VmCodeComment("SUB-END '${sub.name}'")
return code
}
private fun translate(block: PtBlock): VmCodeChunk {
val code = VmCodeChunk()
code += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
for (child in block.children) {
if(child !is PtAssignment) // global variable initialization is done elsewhere
code += translateNode(child)
}
code += VmCodeComment("BLOCK-END '${block.name}'")
return code
}
internal fun vmType(type: DataType): VmDataType {
return when(type) {
DataType.UBYTE,
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")
}
}
private var labelSequenceNumber = 0
internal fun createLabelName(): List<String> {
labelSequenceNumber++
return listOf("prog8_label_gen_$labelSequenceNumber")
}
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

@ -1,91 +0,0 @@
package prog8.codegen.virtual
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.*
class VariableAllocator(private val st: SymbolTable, private val program: PtProgram, errors: IErrorReporter) {
private val allocations = mutableMapOf<List<String>, Int>()
private var freeMemoryStart: Int
val freeMem: Int
get() = freeMemoryStart
init {
var nextLocation = 0
for (variable in st.allVariables) {
val memsize =
when (variable.dt) {
DataType.STR -> variable.initialStringValue!!.first.length + 1 // include the zero byte
in NumericDatatypes -> program.memsizer.memorySize(variable.dt)
in ArrayDatatypes -> program.memsizer.memorySize(variable.dt, variable.length!!)
else -> throw InternalCompilerException("weird dt")
}
allocations[variable.scopedName] = nextLocation
nextLocation += memsize
}
freeMemoryStart = nextLocation
}
fun get(name: List<String>) = allocations.getValue(name)
fun asVmMemory(): List<Pair<List<String>, String>> {
val mm = mutableListOf<Pair<List<String>, String>>()
for (variable in st.allVariables) {
val location = allocations.getValue(variable.scopedName)
val typeStr = when(variable.dt) {
DataType.UBYTE, DataType.ARRAY_UB, DataType.STR -> "ubyte"
DataType.BYTE, DataType.ARRAY_B -> "byte"
DataType.UWORD, DataType.ARRAY_UW -> "uword"
DataType.WORD, DataType.ARRAY_W -> "word"
DataType.FLOAT, DataType.ARRAY_F -> "float"
else -> throw InternalCompilerException("weird dt")
}
val value = when(variable.dt) {
DataType.FLOAT -> (variable.initialNumericValue ?: 0.0).toString()
in NumericDatatypes -> (variable.initialNumericValue ?: 0).toHex()
DataType.STR -> {
val encoded = program.encoding.encodeString(variable.initialStringValue!!.first, variable.initialStringValue!!.second)
encoded.joinToString(",") { it.toInt().toHex() } + ",0"
}
DataType.ARRAY_F -> {
if(variable.initialArrayValue!=null) {
variable.initialArrayValue!!.joinToString(",") { it.number!!.toString() }
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
in ArrayDatatypes -> {
if(variable.initialArrayValue!==null) {
variable.initialArrayValue!!.joinToString(",") { it.number!!.toHex() }
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
else -> throw InternalCompilerException("weird dt")
}
mm.add(Pair(variable.scopedName, "$location $typeStr $value"))
}
return mm
}
private val memorySlabsInternal = mutableMapOf<String, Triple<UInt, UInt, UInt>>()
internal val memorySlabs: Map<String, Triple<UInt, UInt, UInt>> = memorySlabsInternal
fun allocateMemorySlab(name: String, size: UInt, align: UInt): UInt {
val address =
if(align==0u || align==1u)
freeMemoryStart.toUInt()
else
(freeMemoryStart.toUInt() + align-1u) and (0xffffffffu xor (align-1u))
memorySlabsInternal[name] = Triple(address, size, align)
freeMemoryStart = (address + size).toInt()
return address
}
fun getMemorySlab(name: String): Triple<UInt, UInt, UInt>? = memorySlabsInternal[name]
}

View File

@ -0,0 +1,103 @@
package prog8.codegen.virtual
import prog8.code.core.AssemblyError
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.intermediate.*
import java.io.BufferedWriter
import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
override fun assemble(dummyOptions: CompilationOptions): Boolean {
val outfile = irProgram.options.outputDir / ("$name.p8virt")
println("write code to $outfile")
// at last, allocate the variables in memory.
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
outfile.bufferedWriter().use { out ->
allocations.asVmMemory().forEach { (name, alloc) ->
out.write("var ${name.joinToString(".")} $alloc\n")
}
out.write("------PROGRAM------\n")
if(!irProgram.options.dontReinitGlobals) {
out.write("; global var inits\n")
irProgram.globalInits.forEach { out.writeLine(it) }
}
irProgram.blocks.firstOrNull()?.let {
if(it.subroutines.any { it.name=="main.start" }) {
// there is a "main.start" entrypoint, jump to it
out.writeLine(IRCodeInstruction(Opcode.JUMP, labelSymbol = "main.start"))
}
}
out.write("; actual program code\n")
irProgram.blocks.forEach { block ->
if(block.address!=null)
TODO("blocks can't have a load address for vm")
out.write("; BLOCK ${block.name} ${block.position}\n")
block.inlineAssembly.forEach { asm ->
out.write("; ASM ${asm.position}\n")
out.write(asm.assembly)
out.write("\n")
}
block.subroutines.forEach { sub ->
out.write("; SUB ${sub.name} ${sub.position}\n")
out.write("_${sub.name}:\n")
sub.chunks.forEach { chunk ->
if(chunk is IRInlineAsmChunk) {
out.write("; ASM ${chunk.position}\n")
out.write(processInlinedAsm(chunk.assembly, allocations))
out.write("\n")
} else {
chunk.lines.forEach { out.writeLine(it) }
}
}
out.write("; END SUB ${sub.name}\n")
}
block.asmSubroutines.forEach { sub ->
out.write("; ASMSUB ${sub.name} ${sub.position}\n")
out.write("_${sub.name}:\n")
out.write(processInlinedAsm(sub.assembly, allocations))
out.write("\n; END ASMSUB ${sub.name}\n")
}
out.write("; END BLOCK ${block.name}\n")
}
}
return true
}
private fun processInlinedAsm(asm: String, allocations: VmVariableAllocator): String {
// TODO do we have to replace variable names by their allocated address???
return asm
}
}
private fun BufferedWriter.writeLine(line: IRCodeLine) {
when(line) {
is IRCodeComment -> {
write("; ${line.comment}\n")
}
is IRCodeInstruction -> {
write(line.ins.toString() + "\n")
}
is IRCodeInlineBinary -> {
write("!binary ")
line.data.withIndex().forEach {(index, byte) ->
write(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index<line.data.size-1)
write("\n!binary ")
}
write("\n")
}
is IRCodeLabel -> {
write("_${line.name}:\n")
}
else -> throw AssemblyError("invalid IR code line")
}
}

View File

@ -0,0 +1,40 @@
package prog8.codegen.virtual
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyGenerator
import prog8.code.core.IAssemblyProgram
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter
import java.nio.file.Path
class VmCodeGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
private val options: CompilationOptions,
private val errors: IErrorReporter
): IAssemblyGenerator {
override fun compileToAssembly(): IAssemblyProgram? {
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate()
return if(options.keepIR) {
//create IR file on disk and read it back.
IRFileWriter(irProgram).writeFile()
val irProgram2 = IRFileReader(options.outputDir, irProgram.name).readFile()
VmAssemblyProgram(irProgram2.name, irProgram2)
} else {
VmAssemblyProgram(irProgram.name, irProgram)
}
}
companion object {
fun compileIR(listingFilename: String): IAssemblyProgram {
val irProgram = IRFileReader(Path.of(""), listingFilename).readFile()
return VmAssemblyProgram(irProgram.name, irProgram)
}
}
}

View File

@ -0,0 +1,92 @@
package prog8.codegen.virtual
import prog8.code.SymbolTable
import prog8.code.core.*
import prog8.intermediate.getTypeString
internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
internal val allocations = mutableMapOf<List<String>, Int>()
private var freeMemoryStart: Int
val freeMem: Int
get() = freeMemoryStart
init {
var nextLocation = 0
for (variable in st.allVariables) {
val memsize =
when (variable.dt) {
DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
in NumericDatatypes -> memsizer.memorySize(variable.dt)
in ArrayDatatypes -> memsizer.memorySize(variable.dt, variable.length!!)
else -> throw InternalCompilerException("weird dt")
}
allocations[variable.scopedName] = nextLocation
nextLocation += memsize
}
for(slab in st.allMemorySlabs) {
// we ignore the alignment for the VM.
allocations[slab.scopedName] = nextLocation
nextLocation += slab.size.toInt()
}
freeMemoryStart = nextLocation
}
fun asVmMemory(): List<Pair<List<String>, String>> {
val mm = mutableListOf<Pair<List<String>, String>>()
// normal variables
for (variable in st.allVariables) {
val location = allocations.getValue(variable.scopedName)
val value = when(variable.dt) {
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex()
DataType.STR -> {
val encoded = encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue!!.second) + listOf(0u)
encoded.joinToString(",") { it.toInt().toHex() }
}
DataType.ARRAY_F -> {
if(variable.onetimeInitializationArrayValue!=null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toString() }
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() }
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
else -> throw InternalCompilerException("weird dt")
}
mm.add(Pair(variable.scopedName, "@$location ${getTypeString(variable)} $value"))
}
// memory mapped variables
for (variable in st.allMemMappedVariables) {
val value = when(variable.dt) {
DataType.FLOAT -> "0.0"
in NumericDatatypes -> "0"
DataType.ARRAY_F -> (1..variable.length!!).joinToString(",") { "0.0" }
in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" }
else -> throw InternalCompilerException("weird dt for mem mapped var")
}
mm.add(Pair(variable.scopedName, "@${variable.address} ${getTypeString(variable)} $value"))
}
// memory slabs.
for(slab in st.allMemorySlabs) {
val address = allocations.getValue(slab.scopedName)
mm.add(Pair(slab.scopedName, "@$address ubyte[${slab.size}] 0"))
}
return mm
}
}

View File

@ -22,6 +22,9 @@ class BinExprSplitter(private val program: Program, private val options: Compila
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> { override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(options.compTarget.name == VMTarget.NAME)
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions) if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
return noModifications return noModifications

View File

@ -22,9 +22,6 @@ class ConstExprEvaluator {
"&" -> bitwiseand(left, right) "&" -> bitwiseand(left, right)
"|" -> bitwiseor(left, right) "|" -> bitwiseor(left, right)
"^" -> bitwisexor(left, right) "^" -> bitwisexor(left, right)
"and" -> logicaland(left, right)
"or" -> logicalor(left, right)
"xor" -> logicalxor(left, right)
"<" -> NumericLiteral.fromBoolean(left < right, left.position) "<" -> NumericLiteral.fromBoolean(left < right, left.position)
">" -> NumericLiteral.fromBoolean(left > right, left.position) ">" -> NumericLiteral.fromBoolean(left > right, left.position)
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position) "<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
@ -58,57 +55,6 @@ class ConstExprEvaluator {
return NumericLiteral(left.type, result.toDouble(), left.position) return NumericLiteral(left.type, result.toDouble(), left.position)
} }
private fun logicalxor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-bitxor $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicalor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-or $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicaland(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral { private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) { if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypes) {

View File

@ -2,7 +2,6 @@ package prog8.optimizer
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.ExpressionError
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.base.UndefinedSymbolError import prog8.ast.base.UndefinedSymbolError
import prog8.ast.expressions.* import prog8.ast.expressions.*
@ -14,7 +13,6 @@ import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.IntegerDatatypes
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@ -36,59 +34,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
} }
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> { override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
// Try to turn a unary prefix expression into a single constant value. val constValue = expr.constValue(program) ?: return noModifications
// Compile-time constant sub expressions will be evaluated on the spot. return listOf(IAstModification.ReplaceNode(expr, constValue, parent))
// For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
val subexpr = expr.expression
if (subexpr is NumericLiteral) {
// accept prefixed literal values (such as -3, not true)
return when (expr.operator) {
"+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent))
"-" -> when (subexpr.type) {
in IntegerDatatypes -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral.optimalInteger(-subexpr.number.toInt(), subexpr.position),
parent))
}
DataType.FLOAT -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.FLOAT, -subexpr.number, subexpr.position),
parent))
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
"~" -> when (subexpr.type) {
DataType.BYTE -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.BYTE, subexpr.number.toInt().inv().toDouble(), subexpr.position),
parent))
}
DataType.UBYTE -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.UBYTE, (subexpr.number.toInt().inv() and 255).toDouble(), subexpr.position),
parent))
}
DataType.WORD -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.WORD, subexpr.number.toInt().inv().toDouble(), subexpr.position),
parent))
}
DataType.UWORD -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral(DataType.UWORD, (subexpr.number.toInt().inv() and 65535).toDouble(), subexpr.position),
parent))
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
"not" -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteral.fromBoolean(subexpr.number == 0.0, subexpr.position),
parent))
}
else -> throw ExpressionError(expr.operator, subexpr.position)
}
}
return noModifications
} }
/* /*
@ -318,7 +265,6 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
} }
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> { override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
// the args of a fuction are constfolded via recursion already.
val constvalue = functionCallExpr.constValue(program) val constvalue = functionCallExpr.constValue(program)
return if(constvalue!=null) return if(constvalue!=null)
listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent)) listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent))
@ -326,6 +272,14 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
noModifications noModifications
} }
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
val constvalue = bfc.constValue(program)
return if(constvalue!=null)
listOf(IAstModification.ReplaceNode(bfc, constvalue, parent))
else
noModifications
}
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> { override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? { fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
val fromCast = rangeFrom.cast(targetDt) val fromCast = rangeFrom.cast(targetDt)

View File

@ -12,17 +12,15 @@ import prog8.ast.statements.IfElse
import prog8.ast.statements.Jump import prog8.ast.statements.Jump
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators import prog8.code.core.*
import prog8.code.core.DataType import prog8.code.target.VMTarget
import prog8.code.core.IntegerDatatypes
import prog8.code.core.NumericDatatypes
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.pow import kotlin.math.pow
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html // 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) : AstWalker() { class ExpressionSimplifier(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet() private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet() private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
@ -33,8 +31,9 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
val literal = typecast.expression as? NumericLiteral val literal = typecast.expression as? NumericLiteral
if (literal != null) { if (literal != null) {
val newLiteral = literal.cast(typecast.type) val newLiteral = literal.cast(typecast.type)
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast) mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
}
} }
// remove redundant nested typecasts // remove redundant nested typecasts
@ -76,6 +75,22 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
} }
} }
} }
if(compTarget.name!=VMTarget.NAME) {
val booleanCondition = ifElse.condition as? BinaryExpression
if(booleanCondition!=null && booleanCondition.operator=="&") {
// special optimization of WORD & $ff00 -> just and the msb of WORD with $ff
val rightNum = booleanCondition.right as? NumericLiteral
if(rightNum!=null && rightNum.type==DataType.UWORD && (rightNum.number.toInt() and 0x00ff)==0) {
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
return listOf(
IAstModification.ReplaceNode(booleanCondition.left, msb, booleanCondition),
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
}
}
}
return noModifications return noModifications
} }
@ -196,35 +211,19 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
} }
} }
// boolvar & 1 --> boolvar
// boolvar & 2 --> false
if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
if(rightVal?.number==1.0) {
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
} else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) {
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
}
}
// simplify when a term is constant and directly determines the outcome // simplify when a term is constant and directly determines the outcome
val constTrue = NumericLiteral.fromBoolean(true, expr.position)
val constFalse = NumericLiteral.fromBoolean(false, expr.position) val constFalse = NumericLiteral.fromBoolean(false, expr.position)
val newExpr: Expression? = when (expr.operator) { val newExpr: Expression? = when (expr.operator) {
"or" -> {
when {
leftVal != null && leftVal.asBooleanValue || rightVal != null && rightVal.asBooleanValue -> constTrue
leftVal != null && !leftVal.asBooleanValue -> expr.right
rightVal != null && !rightVal.asBooleanValue -> expr.left
else -> null
}
}
"and" -> {
when {
leftVal != null && !leftVal.asBooleanValue || rightVal != null && !rightVal.asBooleanValue -> constFalse
leftVal != null && leftVal.asBooleanValue -> expr.right
rightVal != null && rightVal.asBooleanValue -> expr.left
else -> null
}
}
"xor" -> {
when {
leftVal != null && !leftVal.asBooleanValue -> expr.right
rightVal != null && !rightVal.asBooleanValue -> expr.left
leftVal != null && leftVal.asBooleanValue -> PrefixExpression("not", expr.right, expr.right.position)
rightVal != null && rightVal.asBooleanValue -> PrefixExpression("not", expr.left, expr.left.position)
else -> null
}
}
"|" -> { "|" -> {
when { when {
leftVal?.number==0.0 -> expr.right leftVal?.number==0.0 -> expr.right
@ -268,6 +267,28 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
else -> null else -> null
} }
if(rightVal!=null && leftDt==DataType.BOOL) {
// see if we can replace comparison against true/1 with simpler comparison against zero
if (expr.operator == "==") {
if (rightVal.number == 1.0) {
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
return listOf(
IAstModification.SetExpression({expr.operator="!="}, expr, parent),
IAstModification.ReplaceNode(expr.right, zero, expr)
)
}
}
if (expr.operator == "!=") {
if (rightVal.number == 1.0) {
val zero = NumericLiteral(DataType.UBYTE, 0.0, expr.right.position)
return listOf(
IAstModification.SetExpression({expr.operator="=="}, expr, parent),
IAstModification.ReplaceNode(expr.right, zero, expr)
)
}
}
}
if(newExpr != null) if(newExpr != null)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent)) return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
@ -286,6 +307,12 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent)) return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
} }
} else { } else {
if(arg is IdentifierReference && arg.nameInSource.size==2
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
// lsb(cx16.r0) -> cx16.r0L
val highReg = IdentifierReference(listOf("cx16", arg.nameInSource[1]+'L'), arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program) val argDt = arg.inferType(program)
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) { if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
// useless lsb() of byte value // useless lsb() of byte value
@ -307,6 +334,12 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
parent)) parent))
} }
} else { } else {
if(arg is IdentifierReference && arg.nameInSource.size==2
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
// msb(cx16.r0) -> cx16.r0H
val highReg = IdentifierReference(listOf("cx16", arg.nameInSource[1]+'H'), arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program) val argDt = arg.inferType(program)
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) { if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
// useless msb() of byte value, replace with 0 // useless msb() of byte value, replace with 0
@ -318,25 +351,14 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
} }
} }
return noModifications if(functionCallExpr.target.nameInSource == listOf("mkword")) {
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
// just cast the lsb to uword
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
} }
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
val range = containment.iterable as? RangeExpression
if(range!=null && range.step.constValue(program)?.number==1.0) {
val from = range.from.constValue(program)
val to = range.to.constValue(program)
val value = containment.element
if(from!=null && to!=null && value.isSimple) {
if(to.number-from.number>6.0) {
// replace containment test with X>=from and X<=to
val left = BinaryExpression(value, ">=", from, containment.position)
val right = BinaryExpression(value.copy(), "<=", to, containment.position)
val comparison = BinaryExpression(left, "and", right, containment.position)
return listOf(IAstModification.ReplaceNode(containment, comparison, parent))
}
}
}
return noModifications return noModifications
} }
@ -472,19 +494,14 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
} }
} }
in powersOfTwo -> { in powersOfTwo -> {
if (leftDt in IntegerDatatypes) { if (leftDt==DataType.UBYTE || leftDt==DataType.UWORD) {
// divided by a power of two => shift right // Unsigned number divided by a power of two => shift right
// Signed number can't simply be bitshifted in this case (due to rounding issues for negative values),
// so we leave that as is and let the code generator deal with it.
val numshifts = log2(cv).toInt() val numshifts = log2(cv).toInt()
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position) return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
} }
} }
in negativePowersOfTwo -> {
if (leftDt in IntegerDatatypes) {
// divided by a negative power of two => negate, then shift right
val numshifts = log2(-cv).toInt()
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
}
}
} }
if (leftDt == DataType.UBYTE) { if (leftDt == DataType.UBYTE) {
@ -542,9 +559,10 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
} }
in negativePowersOfTwo -> { in negativePowersOfTwo -> {
if (leftValue.inferType(program).isInteger) { if (leftValue.inferType(program).isInteger) {
// times a negative power of two => negate, then shift left // times a negative power of two => negate, then shift
val numshifts = log2(-cv).toInt() val numshifts = log2(-cv).toInt()
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position) val negation = PrefixExpression("-", expr2.left, expr.position)
return BinaryExpression(negation, "<<", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
} }
} }
} }
@ -574,7 +592,14 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
if (amount >= 16) { if (amount >= 16) {
return NumericLiteral(targetDt, 0.0, expr.position) return NumericLiteral(targetDt, 0.0, expr.position)
} else if (amount > 8) { }
else if(amount==8) {
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(DataType.UBYTE, 0.0, expr.position)), expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position) val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position) val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position) return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
@ -613,12 +638,19 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
if (amount >= 16) { if (amount >= 16) {
return NumericLiteral.optimalInteger(0, expr.position) return NumericLiteral.optimalInteger(0, expr.position)
} }
else if(amount==8) {
// shift right by 8 bits is just a byte operation: msb(X) as uword
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
}
else if (amount > 8) { else if (amount > 8) {
// same as above but with residual shifts.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position) return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
} }
} }
DataType.WORD -> { DataType.WORD -> {
// bit-shifting a signed value shouldn't be allowed by the compiler but here we go...
if (amount > 16) { if (amount > 16) {
expr.right = NumericLiteral.optimalInteger(16, expr.right.position) expr.right = NumericLiteral.optimalInteger(16, expr.right.position)
return null return null

View File

@ -60,8 +60,8 @@ fun Program.inlineSubroutines(): Int {
return inliner.applyModifications() return inliner.applyModifications()
} }
fun Program.simplifyExpressions() : Int { fun Program.simplifyExpressions(target: ICompilationTarget) : Int {
val opti = ExpressionSimplifier(this) val opti = ExpressionSimplifier(this, target)
opti.visit(this) opti.visit(this)
return opti.applyModifications() return opti.applyModifications()
} }

View File

@ -106,7 +106,7 @@ class Inliner(val program: Program): AstWalker() {
} else } else
false false
} }
is Jump, is GoSub -> true is Jump -> true
else -> false else -> false
} }
} }
@ -170,15 +170,6 @@ class Inliner(val program: Program): AstWalker() {
return super.before(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> { private fun possibleInlineFcallStmt(sub: Subroutine, origNode: Node, parent: Node): Iterable<IAstModification> {
if(sub.inline && sub.parameters.isEmpty()) { if(sub.inline && sub.parameters.isEmpty()) {
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))

View File

@ -73,31 +73,6 @@ class StatementOptimizer(private val program: Program,
} }
} }
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
val subroutine = functionCallStatement.target.targetSubroutine(program)
if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Return)
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
}
if(compTarget.name!=VMTarget.NAME) {
// see if we can optimize any complex argument expressions to be just a simple variable
// TODO for now, only works for single-argument functions because we use just 1 temp var: R9
if(functionCallStatement.target.nameInSource !in listOf(listOf("pop"), listOf("popw")) && functionCallStatement.args.size==1) {
val arg = functionCallStatement.args[0]
if(!arg.isSimple && arg !is IFunctionCall) {
val name = getTempRegisterName(arg.inferType(program))
val tempvar = IdentifierReference(name, functionCallStatement.position)
val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, AssignmentOrigin.OPTIMIZER, functionCallStatement.position)
return listOf(
IAstModification.InsertBefore(functionCallStatement, assignTempvar, parent as IStatementContainer),
IAstModification.ReplaceNode(arg, tempvar, functionCallStatement)
)
}
}
}
return noModifications return noModifications
} }
@ -108,7 +83,7 @@ class StatementOptimizer(private val program: Program,
// empty true part? switch with the else part // empty true part? switch with the else part
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) { if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
val invertedCondition = PrefixExpression("not", ifElse.condition, ifElse.condition.position) val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, ifElse.condition.position), ifElse.condition.position)
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position) val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position) val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
return listOf( return listOf(
@ -244,25 +219,6 @@ class StatementOptimizer(private val program: Program,
return noModifications return noModifications
} }
// NOTE: do NOT remove a jump to the next statement, because this will lead to wrong code when this occurs at the end of a subroutine
// if we want to optimize this away, it can be done later at code generation time.
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
// if the next statement is return with no returnvalue, change into a regular jump if there are no parameters as well.
val subroutineParams = gosub.identifier.targetSubroutine(program)?.parameters
if(subroutineParams!=null && subroutineParams.isEmpty()) {
val returnstmt = gosub.nextSibling() as? Return
if(returnstmt!=null && returnstmt.value==null) {
return listOf(
IAstModification.Remove(returnstmt, parent as IStatementContainer),
IAstModification.ReplaceNode(gosub, Jump(null, gosub.identifier, null, gosub.position), parent)
)
}
}
return noModifications
}
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> { override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val binExpr = assignment.value as? BinaryExpression val binExpr = assignment.value as? BinaryExpression
@ -429,31 +385,22 @@ class StatementOptimizer(private val program: Program,
if(compTarget.name==VMTarget.NAME) if(compTarget.name==VMTarget.NAME)
return noModifications return noModifications
fun returnViaIntermediaryVar(value: Expression): Iterable<IAstModification>? { val returnvalue = returnStmt.value
val subr = returnStmt.definingSubroutine!! if (returnvalue!=null) {
val returnDt = subr.returntypes.single() val dt = returnvalue.inferType(program).getOr(DataType.UNDEFINED)
if (returnDt in IntegerDatatypes) { if(dt!=DataType.UNDEFINED) {
if (returnvalue is BinaryExpression || (returnvalue is TypecastExpression && !returnvalue.expression.isSimple)) {
// first assign to intermediary variable, then return that // first assign to intermediary variable, then return that
val (returnVarName, _) = program.getTempVar(returnDt) val (returnVarName, _) = program.getTempVar(dt)
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position) val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position) val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
val assign = Assignment(tgt, value, AssignmentOrigin.OPTIMIZER, returnStmt.position) val assign = Assignment(tgt, returnvalue, AssignmentOrigin.OPTIMIZER, returnStmt.position)
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position) val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
return listOf( return listOf(
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer), IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent) IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
) )
} }
return null
}
// TODO decision when to use intermediary variable to calculate returnvalue seems a bit arbitrary...
val returnvalue = returnStmt.value
if (returnvalue!=null) {
if (returnvalue is BinaryExpression || (returnvalue is TypecastExpression && !returnvalue.expression.isSimple)) {
val mod = returnViaIntermediaryVar(returnvalue)
if(mod!=null)
return mod
} }
} }

View File

@ -27,7 +27,6 @@ compileTestKotlin {
def prog8version = rootProject.file('compiler/res/version.txt').text.trim() def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
dependencies { dependencies {
implementation project(':codeAst')
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':codeOptimizers') implementation project(':codeOptimizers')
implementation project(':compilerAst') implementation project(':compilerAst')
@ -40,7 +39,7 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.2.3' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
} }
configurations.all { configurations.all {
@ -68,7 +67,6 @@ sourceSets {
test { test {
java { java {
srcDir "${project.projectDir}/test" srcDir "${project.projectDir}/test"
srcDir "${project(':compilerAst').projectDir}/test/helpers"
} }
} }
} }

View File

@ -17,7 +17,6 @@
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" /> <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="antlr.antlr4" level="project" /> <orderEntry type="library" name="antlr.antlr4" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="codeAst" />
<orderEntry type="module" module-name="compilerAst" /> <orderEntry type="module" module-name="compilerAst" />
<orderEntry type="module" module-name="codeOptimizers" /> <orderEntry type="module" module-name="codeOptimizers" />
<orderEntry type="module" module-name="codeGenCpu6502" /> <orderEntry type="module" module-name="codeGenCpu6502" />

View File

@ -722,6 +722,7 @@ cx16 {
; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack. ; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack.
; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless ; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless
; you're doing insane nesting of expressions...) ; you're doing insane nesting of expressions...)
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
&uword r0 = $1b00 &uword r0 = $1b00
&uword r1 = $1b02 &uword r1 = $1b02
&uword r2 = $1b04 &uword r2 = $1b04

View File

@ -315,7 +315,7 @@ hline_zero2
; for efficiency of internal algorithms here is the internal plot routine ; for efficiency of internal algorithms here is the internal plot routine
; that takes the plotx coordinate in a separate variable instead of the XY register pair: ; that takes the plotx coordinate in a separate variable instead of the XY register pair:
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot() uword @zp internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
%asm {{ %asm {{

View File

@ -685,6 +685,7 @@ cx16 {
; (because there's no room for them in the zeropage) ; (because there's no room for them in the zeropage)
; they are allocated at the bottom of the eval-stack (should be ample space unless ; they are allocated at the bottom of the eval-stack (should be ample space unless
; you're doing insane nesting of expressions...) ; you're doing insane nesting of expressions...)
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
&uword r0 = $cf00 &uword r0 = $cf00
&uword r1 = $cf02 &uword r1 = $cf02
&uword r2 = $cf04 &uword r2 = $cf04

View File

@ -416,6 +416,9 @@ _loop
beq _stop beq _stop
cmp #7 ; screencode letters A-F are 1-6 cmp #7 ; screencode letters A-F are 1-6
bcc _add_letter bcc _add_letter
and #127
cmp #97
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
cmp #'g' cmp #'g'
bcs _stop bcs _stop
cmp #'a' cmp #'a'
@ -451,6 +454,11 @@ _add_letter
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
pla pla
jmp _calc jmp _calc
_try_iso
cmp #103
bcs _stop
and #63
bne _add_letter
}} }}
} }

View File

@ -102,6 +102,8 @@ cx16diskio {
goto byte_read_loop ; macptr block read not supported, do fallback loop goto byte_read_loop ; macptr block read not supported, do fallback loop
diskio.list_blocks += size diskio.list_blocks += size
bufferpointer += size bufferpointer += size
if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
num_bytes -= size num_bytes -= size
if c64.READST() & $40 { if c64.READST() & $40 {
diskio.f_close() ; end of file, close it diskio.f_close() ; end of file, close it

View File

@ -28,7 +28,7 @@ gfx2 {
uword width = 0 uword width = 0
uword height = 0 uword height = 0
ubyte bpp = 0 ubyte bpp = 0
ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes bool monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
sub screen_mode(ubyte mode) { sub screen_mode(ubyte mode) {
when mode { when mode {
@ -127,7 +127,7 @@ gfx2 {
position(0, 0) position(0, 0)
} }
sub monochrome_stipple(ubyte enable) { sub monochrome_stipple(bool enable) {
monochrome_dont_stipple_flag = not enable monochrome_dont_stipple_flag = not enable
} }
@ -683,7 +683,7 @@ _done
} }
} }
sub position2(uword @zp x, uword y, ubyte also_port_1) { sub position2(uword @zp x, uword y, bool also_port_1) {
position(x, y) position(x, y)
if also_port_1 { if also_port_1 {
when active_mode { when active_mode {
@ -844,25 +844,28 @@ _done
} }
6 -> { 6 -> {
; hires 4c ; hires 4c
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
cx16.r11L = color
cx16.r8 = y
while @(sctextptr) { while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8 chardataptr = charset_addr + (@(sctextptr) as uword)*8
repeat 8 { repeat 8 {
; TODO rewrite this inner loop partly in assembly ; TODO rewrite this inner loop partly in assembly
; requires expanding the charbits to 2-bits per pixel (based on color) ; requires expanding the charbits to 2-bits per pixel (based on color)
; also it's way more efficient to draw whole horizontal spans instead of per-character ; also it's way more efficient to draw whole horizontal spans instead of per-character
ubyte charbits = cx16.vpeek(charset_bank, chardataptr) cx16.r9L = cx16.vpeek(charset_bank, chardataptr) ; get the 8 horizontal character bits
cx16.r7 = x
repeat 8 { repeat 8 {
charbits <<= 1 cx16.r9L <<= 1
if_cs if_cs
plot(x, y, color) plot(cx16.r7, cx16.r8, cx16.r11L)
x++ cx16.r7++
} }
x-=8
chardataptr++ chardataptr++
y++ cx16.r8++
} }
x+=8 x+=8
y-=8 cx16.r8-=8
sctextptr++ sctextptr++
} }
} }

View File

@ -2,15 +2,20 @@
%import textio %import textio
; Bitmap pixel graphics module for the CommanderX16 ; Bitmap pixel graphics module for the CommanderX16
; wraps the graphics functions that are in ROM. ; Wraps the graphics functions that are in ROM.
; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom) ; Only lo-res 320x240 256 color mode for now.
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific) ; Unlike graphics module on the C64, you can use colors() to set new drawing colors for every draw operation.
; For other resolutions or other color modes, use the "gfx2" module instead. (which is Cx16-specific)
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module. ; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
graphics { graphics {
const uword WIDTH = 320 const uword WIDTH = 320
const ubyte HEIGHT = 200 const ubyte HEIGHT = 240
ubyte stroke_color = 1
ubyte background_color = 0
sub enable_bitmap_mode() { sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white. ; enable bitmap screen, erase it and set colors to black/white.
@ -27,10 +32,18 @@ graphics {
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) { sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
stroke_color = pixelcolor
background_color = bgcolor
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor) cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
cx16.GRAPH_clear() cx16.GRAPH_clear()
} }
sub colors(ubyte stroke, ubyte fill) {
; this routine is only available on the cx16, other targets can't change colors on the fly
cx16.GRAPH_set_colors(stroke, fill, background_color)
stroke_color = stroke
}
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) { sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
cx16.GRAPH_draw_line(x1, y1, x2, y2) cx16.GRAPH_draw_line(x1, y1, x2, y2)
} }
@ -71,31 +84,31 @@ graphics {
cx16.r0 = xcenter + xx cx16.r0 = xcenter + xx
cx16.r1 = ycenter + yy cx16.r1 = ycenter + yy
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
cx16.r0 = xcenter - xx cx16.r0 = xcenter - xx
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
cx16.r0 = xcenter + xx cx16.r0 = xcenter + xx
cx16.r1 = ycenter - yy cx16.r1 = ycenter - yy
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
cx16.r0 = xcenter - xx cx16.r0 = xcenter - xx
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
cx16.r0 = xcenter + yy cx16.r0 = xcenter + yy
cx16.r1 = ycenter + xx cx16.r1 = ycenter + xx
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
cx16.r0 = xcenter - yy cx16.r0 = xcenter - yy
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
cx16.r0 = xcenter + yy cx16.r0 = xcenter + yy
cx16.r1 = ycenter - xx cx16.r1 = ycenter - xx
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
cx16.r0 = xcenter - yy cx16.r0 = xcenter - yy
cx16.FB_cursor_position2() cx16.FB_cursor_position2()
cx16.FB_set_pixel(1) cx16.FB_set_pixel(stroke_color)
yy++ yy++
if decisionOver2<=0 { if decisionOver2<=0 {
decisionOver2 += (yy as word)*2+1 decisionOver2 += (yy as word)*2+1

View File

@ -0,0 +1,176 @@
%import syslib
psg {
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
; 00 frequency word LSB
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
; 02 bit 7 =right, bit 6 = left, bits 5-0 = volume 0-63 levels
; 03 bit 7,6 = waveform, bits 5-0 = Pulse width 0-63
; waveform: 0=pulse, 1=sawtooth, 2=triangle, 3=noise
const ubyte PULSE = %00000000
const ubyte SAWTOOTH = %01000000
const ubyte TRIANGLE = %10000000
const ubyte NOISE = %11000000
const ubyte LEFT = %01000000
const ubyte RIGHT = %10000000
sub voice(ubyte voice_num, ubyte channel, ubyte volume, ubyte waveform, ubyte pulsewidth) {
; -- Enables a 'voice' on the PSG.
; voice_num = 0-15, the voice number.
; channel = either LEFT or RIGHT or (LEFT|RIGHT). Specifies the stereo channel(s) to use.
; volume = 0-63, the starting volume for the voice
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
envelope_states[voice_num] = 255
cx16.r0 = $f9c2 + voice_num * 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0)
cx16.VERA_ADDR_M = msb(cx16.r0)
cx16.VERA_ADDR_H = 1
cx16.VERA_DATA0 = channel | volume
cx16.VERA_ADDR_L++
cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume
}
; sub freq_hz(ubyte voice_num, float hertz) {
; ; this would rely on floating point math to convert hertz to vera frequency
; ; could be replaced by integer math maybe with a lookup table?
; uword vera_freq = (hertz / 0.3725290298461914) as uword
; freq(voice_num, vera_freq)
; }
sub freq(ubyte voice_num, uword vera_freq) {
; -- Changes the frequency of the voice's sound.
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
; (https://github.com/commanderx16/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
cx16.r0 = $f9c0 + voice_num * 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0)
cx16.VERA_ADDR_M = msb(cx16.r0)
cx16.VERA_ADDR_H = 1
cx16.VERA_DATA0 = lsb(vera_freq)
cx16.VERA_ADDR_L++
cx16.VERA_DATA0 = msb(vera_freq)
}
sub volume(ubyte voice_num, ubyte vol) {
; -- Modifies the volume of this voice.
; voice_num = 0-15, vol = 0-63 where 0=silent, 63=loudest.
cx16.r0 = $f9c2 + voice_num * 4
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | vol)
envelope_volumes[voice_num] = mkword(vol, 0)
envelope_maxvolumes[voice_num] = vol
}
sub pulse_width(ubyte voice_num, ubyte pw) {
; -- Modifies the pulse width of this voice (when waveform=PULSE)
; voice_num = 0-15, pw = 0-63 where 0=narrow, 63=50%cycle so square wave.
cx16.r0 = $f9c3 + voice_num * 4
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | pw)
}
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
; -- Enables AttackSustainRelease volume envelope for a voice.
; Note: this requires setting up envelopes_irq() as well, read its description.
; voice_num = 0-15 maxvolume = 0-63
; attack, sustain, release = 0-255 that determine the speed of the A/D/R.
; TODO describe how the speeds are calculated. For now, experiment. Higher values means *slower* enveloping.
envelope_states[voice_num] = 255
envelope_attacks[voice_num] = attack
envelope_sustains[voice_num] = sustain
envelope_releases[voice_num] = release
if attack
attack = 0
else
attack = maxvolume ; max volume when no attack is set
envelope_volumes[voice_num] = mkword(attack, 0)
envelope_maxvolumes[voice_num] = maxvolume
envelope_states[voice_num] = 0
}
sub silent() {
; -- Shut down all PSG voices.
for cx16.r1L in 0 to 15 {
envelope_states[cx16.r1L] = 255
envelope_volumes[cx16.r1L] = 0
volume(cx16.r1L, 0)
}
}
sub envelopes_irq() {
; If you want to use real-time volume envelopes (Attack-Sustain-Release),
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
; or just install this routine as the only irq handler if you don't have to do other things there.
; Example: cx16.set_irq(&psg.envelopes_irq, true)
; cx16.r0 = the volume word (volume scaled by 256)
; cx16.r1L = the voice number
; cx16.r2L = attack value
pushw(cx16.r0)
push(cx16.r1L)
push(cx16.r2L)
pushw(cx16.r9)
; calculate new volumes
for cx16.r1L in 0 to 15 {
when envelope_states[cx16.r1L] {
0 -> {
; attack
cx16.r2L = envelope_maxvolumes[cx16.r1L]
cx16.r0 = envelope_volumes[cx16.r1L] + envelope_attacks[cx16.r1L] * $0040
if msb(cx16.r0) > cx16.r2L or envelope_attacks[cx16.r1L]==0 {
cx16.r0 = mkword(cx16.r2L, 0)
envelope_attacks[cx16.r1L] = 0
envelope_states[cx16.r1L] = 1 ; start sustain
}
envelope_volumes[cx16.r1L] = cx16.r0
}
1 -> {
; sustain
if envelope_sustains[cx16.r1L] {
envelope_sustains[cx16.r1L]--
} else {
envelope_states[cx16.r1L] = 2 ; start release
}
}
2 -> {
; release
cx16.r0 = envelope_volumes[cx16.r1L] - envelope_releases[cx16.r1L] * $0040
if msb(cx16.r0) & %11000000 {
cx16.r0 = 0
envelope_releases[cx16.r1L] = 0
}
envelope_volumes[cx16.r1L] = cx16.r0
}
}
}
; set new volumes of all 16 voices, using vera stride of 4
cx16.push_vera_context()
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = $c2
cx16.VERA_ADDR_M = $f9
cx16.VERA_ADDR_H = 1 | %00110000
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_L = $c2
cx16.VERA_ADDR_M = $f9
cx16.VERA_ADDR_H = 1 | %00110000
for cx16.r1L in 0 to 15 {
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
}
cx16.pop_vera_context()
popw(cx16.r9)
pop(cx16.r2L)
pop(cx16.r1L)
popw(cx16.r0)
}
ubyte[16] envelope_states
uword[16] envelope_volumes ; scaled by 256
ubyte[16] envelope_attacks
ubyte[16] envelope_sustains
ubyte[16] envelope_releases
ubyte[16] envelope_maxvolumes
}

View File

@ -98,7 +98,7 @@ cx16 {
&uword ISTOP = $0328 &uword ISTOP = $0328
&uword IGETIN = $032a &uword IGETIN = $032a
&uword ICLALL = $032c &uword ICLALL = $032c
&uword KEYHDL = $032e ; keyboard scan code handler &uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
&uword ILOAD = $0330 &uword ILOAD = $0330
&uword ISAVE = $0332 &uword ISAVE = $0332
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in &uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
@ -306,7 +306,7 @@ romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @Pc
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr() romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y) ; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
romsub $ff6e = jsrfar() romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X) romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X) romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
@ -369,6 +369,7 @@ romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uwor
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time() romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
; keyboard, mouse, joystick ; keyboard, mouse, joystick
; note: also see the kbdbuf_clear() helper routine below!
romsub $febd = kbdbuf_peek() -> ubyte @A, ubyte @X ; key in A, queue length in X romsub $febd = kbdbuf_peek() -> ubyte @A, ubyte @X ; key in A, queue length in X
romsub $febd = kbdbuf_peek2() -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values romsub $febd = kbdbuf_peek2() -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
romsub $fec0 = kbdbuf_get_modifiers() -> ubyte @A romsub $fec0 = kbdbuf_get_modifiers() -> ubyte @A
@ -380,6 +381,15 @@ romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
asmsub kbdbuf_clear() {
; -- convenience helper routine to clear the keyboard buffer
%asm {{
- jsr c64.GETIN
bne -
rts
}}
}
asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) { asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) {
; -- convenience wrapper function that handles the screen resolution for mouse_config() for you ; -- convenience wrapper function that handles the screen resolution for mouse_config() for you
%asm {{ %asm {{
@ -639,8 +649,12 @@ asmsub init_system_phase2() {
asmsub cleanup_at_exit() { asmsub cleanup_at_exit() {
; executed when the main subroutine does rts ; executed when the main subroutine does rts
; just an rts here, nothing special to clean up on cx16
%asm {{ %asm {{
lda #1
sta $00 ; ram bank 1
lda #4
sta $01 ; rom bank 4 (kernal)
stz $2d ; hack to reset machine code monitor bank to 0
rts rts
}} }}
} }
@ -731,6 +745,56 @@ IRQ_SCRATCH_ZPWORD2 .word 0
}} }}
} }
asmsub push_vera_context() clobbers(A) {
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
%asm {{
; note cannot store this on cpu hardware stack because this gets called as a subroutine
lda cx16.VERA_ADDR_L
sta _vera_storage
lda cx16.VERA_ADDR_M
sta _vera_storage+1
lda cx16.VERA_ADDR_H
sta _vera_storage+2
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
lda cx16.VERA_ADDR_M
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
lda cx16.VERA_CTRL
sta _vera_storage+7
rts
_vera_storage: .byte 0,0,0,0,0,0,0,0
}}
}
asmsub pop_vera_context() clobbers(A) {
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
%asm {{
lda cx16.push_vera_context._vera_storage+7
sta cx16.VERA_CTRL
lda cx16.push_vera_context._vera_storage+6
sta cx16.VERA_ADDR_H
lda cx16.push_vera_context._vera_storage+5
sta cx16.VERA_ADDR_M
lda cx16.push_vera_context._vera_storage+4
sta cx16.VERA_ADDR_L
lda cx16.push_vera_context._vera_storage+3
sta cx16.VERA_CTRL
lda cx16.push_vera_context._vera_storage+2
sta cx16.VERA_ADDR_H
lda cx16.push_vera_context._vera_storage+1
sta cx16.VERA_ADDR_M
lda cx16.push_vera_context._vera_storage+0
sta cx16.VERA_ADDR_L
rts
}}
}
asmsub restore_irq() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
@ -815,6 +879,8 @@ sys {
%asm {{ %asm {{
sei sei
stz $01 ; bank the kernal in stz $01 ; bank the kernal in
lda #$80
sta cx16.VERA_CTRL ; reset Vera (kernal doesn't do this?)
jmp (cx16.RESET_VEC) jmp (cx16.RESET_VEC)
}} }}
} }

View File

@ -6,11 +6,12 @@
diskio { diskio {
sub directory(ubyte drivenumber) -> ubyte { sub directory(ubyte drivenumber) -> bool {
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success. ; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
c64.SETNAM(1, "$") c64.SETNAM(1, "$")
c64.SETLFS(13, drivenumber, 0) c64.SETLFS(13, drivenumber, 0)
ubyte status = 1
void c64.OPEN() ; open 13,8,0,"$" void c64.OPEN() ; open 13,8,0,"$"
if_cs if_cs
goto io_error goto io_error
@ -23,8 +24,13 @@ diskio {
} }
; while not key pressed / EOF encountered, read data. ; while not key pressed / EOF encountered, read data.
ubyte status = c64.READST() status = c64.READST()
while not status { if status!=0 {
status = 1
goto io_error
}
while status==0 {
ubyte low = c64.CHRIN() ubyte low = c64.CHRIN()
ubyte high = c64.CHRIN() ubyte high = c64.CHRIN()
txt.print_uw(mkword(high, low)) txt.print_uw(mkword(high, low))
@ -43,9 +49,9 @@ diskio {
if c64.STOP2() if c64.STOP2()
break break
} }
status = c64.READST()
io_error: io_error:
status = c64.READST()
c64.CLRCHN() ; restore default i/o devices c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(13) c64.CLOSE(13)
@ -59,34 +65,69 @@ io_error:
return true return true
} }
sub diskname(ubyte drivenumber) -> uword {
; -- Returns pointer to disk name string or 0 if failure.
c64.SETNAM(1, "$")
c64.SETLFS(13, drivenumber, 0)
ubyte okay = false
void c64.OPEN() ; open 13,8,0,"$"
if_cs
goto io_error
void c64.CHKIN(13) ; use #13 as input channel
if_cs
goto io_error
repeat 6 {
void c64.CHRIN() ; skip the 6 prologue bytes
}
if c64.READST()!=0
goto io_error
; while not key pressed / EOF encountered, read data.
cx16.r0 = &list_filename
repeat {
@(cx16.r0) = c64.CHRIN()
if @(cx16.r0)==0
break
cx16.r0++
}
okay = true
io_error:
c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(13)
if okay
return &list_filename
return 0
}
; internal variables for the iterative file lister / loader ; internal variables for the iterative file lister / loader
ubyte list_skip_disk_name bool list_skip_disk_name
uword list_pattern uword list_pattern
uword list_blocks uword list_blocks
ubyte iteration_in_progress = false bool iteration_in_progress = false
ubyte @zp first_byte ubyte @zp first_byte
ubyte have_first_byte bool have_first_byte
str list_filename = "?" * 32 str list_filename = "?" * 50
; ----- get a list of files (uses iteration functions internally) ----- ; ----- get a list of files (uses iteration functions internally) -----
sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte { sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte {
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested. ; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested. Returns number of files.
const uword names_buf_size = 800 const uword filenames_buf_size = 800
uword names_buffer = memory("filenames", names_buf_size, 0) uword filenames_buffer = memory("filenames", filenames_buf_size, 0)
uword buffer_start = names_buffer uword buffer_start = filenames_buffer
ubyte files_found = 0 ubyte files_found = 0
if lf_start_list(drivenumber, pattern_ptr) { if lf_start_list(drivenumber, pattern_ptr) {
while lf_next_entry() { while lf_next_entry() {
@(name_ptrs) = lsb(names_buffer) @(name_ptrs) = lsb(filenames_buffer)
name_ptrs++ name_ptrs++
@(name_ptrs) = msb(names_buffer) @(name_ptrs) = msb(filenames_buffer)
name_ptrs++ name_ptrs++
names_buffer += string.copy(diskio.list_filename, names_buffer) + 1 filenames_buffer += string.copy(diskio.list_filename, filenames_buffer) + 1
files_found++ files_found++
if names_buffer - buffer_start > names_buf_size-18 if filenames_buffer - buffer_start > filenames_buf_size-18
break break
if files_found == max_names if files_found == max_names
break break
@ -98,7 +139,7 @@ io_error:
; ----- iterative file lister functions (uses io channel 12) ----- ; ----- iterative file lister functions (uses io channel 12) -----
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> ubyte { sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> bool {
; -- start an iterative file listing with optional pattern matching. ; -- start an iterative file listing with optional pattern matching.
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
lf_end_list() lf_end_list()
@ -127,7 +168,7 @@ io_error:
return false return false
} }
sub lf_next_entry() -> ubyte { sub lf_next_entry() -> bool {
; -- retrieve the next entry from an iterative file listing session. ; -- retrieve the next entry from an iterative file listing session.
; results will be found in list_blocks and list_filename. ; results will be found in list_blocks and list_filename.
; if it returns false though, there are no more entries (or an error occurred). ; if it returns false though, there are no more entries (or an error occurred).
@ -199,7 +240,7 @@ close_end:
; ----- iterative file loader functions (uses io channel 11) ----- ; ----- iterative file loader functions (uses io channel 11) -----
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte { sub f_open(ubyte drivenumber, uword filenameptr) -> bool {
; -- open a file for iterative reading with f_read ; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
f_close() f_close()
@ -208,6 +249,7 @@ close_end:
c64.SETLFS(11, drivenumber, 0) c64.SETLFS(11, drivenumber, 0)
void c64.OPEN() ; open 11,8,0,"filename" void c64.OPEN() ; open 11,8,0,"filename"
if_cc { if_cc {
if c64.READST()==0 {
iteration_in_progress = true iteration_in_progress = true
have_first_byte = false have_first_byte = false
void c64.CHKIN(11) ; use #11 as input channel void c64.CHKIN(11) ; use #11 as input channel
@ -219,6 +261,7 @@ close_end:
} }
} }
} }
}
f_close() f_close()
return false return false
} }
@ -226,6 +269,8 @@ close_end:
sub f_read(uword bufferpointer, uword num_bytes) -> uword { sub f_read(uword bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes. ; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions) ; returns the actual number of bytes read. (checks for End-of-file and error conditions)
; NOTE: on systems with banked ram (such as Commander X16) this routine DOES NOT
; automatically load into subsequent banks if it reaches a bank boundary!
if not iteration_in_progress or not num_bytes if not iteration_in_progress or not num_bytes
return 0 return 0
@ -341,7 +386,7 @@ _end rts
; ----- iterative file saver functions (uses io channel 14) ----- ; ----- iterative file saver functions (uses io channel 14) -----
sub f_open_w(ubyte drivenumber, uword filenameptr) -> ubyte { sub f_open_w(ubyte drivenumber, uword filenameptr) -> bool {
; -- open a file for iterative writing with f_write ; -- open a file for iterative writing with f_write
f_close_w() f_close_w()
@ -356,7 +401,7 @@ _end rts
return false return false
} }
sub f_write(uword bufferpointer, uword num_bytes) -> ubyte { sub f_write(uword bufferpointer, uword num_bytes) -> bool {
; -- write the given umber of bytes to the currently open file ; -- write the given umber of bytes to the currently open file
if num_bytes!=0 { if num_bytes!=0 {
void c64.CHKOUT(14) ; use #14 as input channel again void c64.CHKOUT(14) ; use #14 as input channel again
@ -380,8 +425,8 @@ _end rts
sub status(ubyte drivenumber) -> uword { sub status(ubyte drivenumber) -> uword {
; -- retrieve the disk drive's current status message ; -- retrieve the disk drive's current status message
uword messageptr = &filename uword messageptr = &list_filename
c64.SETNAM(0, filename) c64.SETNAM(0, list_filename)
c64.SETLFS(15, drivenumber, 15) c64.SETLFS(15, drivenumber, 15)
void c64.OPEN() ; open 15,8,15 void c64.OPEN() ; open 15,8,15
if_cs if_cs
@ -391,22 +436,25 @@ _end rts
goto io_error goto io_error
while not c64.READST() { while not c64.READST() {
@(messageptr) = c64.CHRIN() first_byte = c64.CHRIN()
if first_byte=='\r' or first_byte=='\n'
break
@(messageptr) = first_byte
messageptr++ messageptr++
} }
@(messageptr) = 0 @(messageptr) = 0
done: done:
c64.CLRCHN() ; restore default i/o devices c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(15) c64.CLOSE(15)
return filename return list_filename
io_error: io_error:
filename = "?disk error" list_filename = "?disk error"
goto done goto done
} }
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte { sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> bool {
c64.SETNAM(string.length(filenameptr), filenameptr) c64.SETNAM(string.length(filenameptr), filenameptr)
c64.SETLFS(1, drivenumber, 0) c64.SETLFS(1, drivenumber, 0)
uword @shared end_address = address + size uword @shared end_address = address + size
@ -476,7 +524,7 @@ io_error:
; Internal routine, only to be used on Commander X16 platform if headerless=true, ; Internal routine, only to be used on Commander X16 platform if headerless=true,
; because this routine uses kernal support for that to load headerless files. ; because this routine uses kernal support for that to load headerless files.
sub load_headerless_cx16(ubyte drivenumber, uword filenameptr, uword address_override, ubyte headerless) -> uword { sub load_headerless_cx16(ubyte drivenumber, uword filenameptr, uword address_override, bool headerless) -> uword {
c64.SETNAM(string.length(filenameptr), filenameptr) c64.SETNAM(string.length(filenameptr), filenameptr)
ubyte secondary = 1 ubyte secondary = 1
cx16.r1 = 0 cx16.r1 = 0
@ -504,10 +552,10 @@ io_error:
sub delete(ubyte drivenumber, uword filenameptr) { sub delete(ubyte drivenumber, uword filenameptr) {
; -- delete a file on the drive ; -- delete a file on the drive
filename[0] = 's' list_filename[0] = 's'
filename[1] = ':' list_filename[1] = ':'
ubyte flen = string.copy(filenameptr, &filename+2) ubyte flen = string.copy(filenameptr, &list_filename+2)
c64.SETNAM(flen+2, filename) c64.SETNAM(flen+2, list_filename)
c64.SETLFS(1, drivenumber, 15) c64.SETLFS(1, drivenumber, 15)
void c64.OPEN() void c64.OPEN()
c64.CLRCHN() c64.CLRCHN()
@ -516,17 +564,24 @@ io_error:
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) { sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
; -- rename a file on the drive ; -- rename a file on the drive
filename[0] = 'r' list_filename[0] = 'r'
filename[1] = ':' list_filename[1] = ':'
ubyte flen_new = string.copy(newfileptr, &filename+2) ubyte flen_new = string.copy(newfileptr, &list_filename+2)
filename[flen_new+2] = '=' list_filename[flen_new+2] = '='
ubyte flen_old = string.copy(oldfileptr, &filename+3+flen_new) ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
c64.SETNAM(3+flen_new+flen_old, filename) c64.SETNAM(3+flen_new+flen_old, list_filename)
c64.SETLFS(1, drivenumber, 15) c64.SETLFS(1, drivenumber, 15)
void c64.OPEN() void c64.OPEN()
c64.CLRCHN() c64.CLRCHN()
c64.CLOSE(1) c64.CLOSE(1)
} }
str filename = "0:??????????????????????????????????????" sub send_command(ubyte drivenumber, uword commandptr) {
; -- send a dos command to the drive
c64.SETNAM(string.length(commandptr), commandptr)
c64.SETLFS(15, drivenumber, 15)
void c64.OPEN()
c64.CLRCHN()
c64.CLOSE(15)
}
} }

View File

@ -37,60 +37,6 @@ _sinecos8 .char trunc(127.0 * sin(range(256+64) * rad(360.0/256.0)))
}} }}
} }
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 { asmsub sinr8u(ubyte radians @A) clobbers(Y) -> ubyte @A {
%asm {{ %asm {{
tay tay
@ -125,57 +71,4 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
}} }}
} }
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

@ -86,9 +86,9 @@ func_all_w_stack .proc
abs_b_stack .proc abs_b_stack .proc
; -- push abs(A) on stack (as unsigned word) ; -- push abs(A) on stack (as unsigned word)
jsr abs_b_into_A jsr abs_b_into_AY
sta P8ESTACK_LO,x sta P8ESTACK_LO,x
stz p8ESTACK_HI,x stz P8ESTACK_HI,x
dex dex
rts rts
.pend .pend

View File

@ -58,27 +58,6 @@ inv_word .proc
rts rts
.pend .pend
not_byte .proc
lda P8ESTACK_LO+1,x
beq +
lda #1
+ eor #1
sta P8ESTACK_LO+1,x
rts
.pend
not_word .proc
lda P8ESTACK_LO + 1,x
ora P8ESTACK_HI + 1,x
beq +
lda #1
+ eor #1
sta P8ESTACK_LO + 1,x
lsr a
sta P8ESTACK_HI + 1,x
rts
.pend
bitand_b .proc bitand_b .proc
; -- bitwise and (of 2 bytes) ; -- bitwise and (of 2 bytes)
lda P8ESTACK_LO+2,x lda P8ESTACK_LO+2,x
@ -142,98 +121,6 @@ bitxor_w .proc
rts rts
.pend .pend
and_b .proc
; -- logical and (of 2 bytes)
lda P8ESTACK_LO+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
beq +
lda #1
+ and P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
rts
.pend
or_b .proc
; -- logical or (of 2 bytes)
lda P8ESTACK_LO+2,x
ora P8ESTACK_LO+1,x
beq +
lda #1
+ inx
sta P8ESTACK_LO+1,x
rts
.pend
xor_b .proc
; -- logical xor (of 2 bytes)
lda P8ESTACK_LO+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
beq +
lda #1
+ eor P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
rts
.pend
and_w .proc
; -- logical and (word and word -> byte)
lda P8ESTACK_LO+2,x
ora P8ESTACK_HI+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
ora P8ESTACK_HI+1,x
beq +
lda #1
+ and P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
rts
.pend
or_w .proc
; -- logical or (word or word -> byte)
lda P8ESTACK_LO+2,x
ora P8ESTACK_LO+1,x
ora P8ESTACK_HI+2,x
ora P8ESTACK_HI+1,x
beq +
lda #1
+ inx
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
rts
.pend
xor_w .proc
; -- logical xor (word xor word -> byte)
lda P8ESTACK_LO+2,x
ora P8ESTACK_HI+2,x
beq +
lda #1
+ sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
ora P8ESTACK_HI+1,x
beq +
lda #1
+ eor P8ZP_SCRATCH_B1
inx
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
rts
.pend
add_w .proc add_w .proc
; -- push word+word / uword+uword ; -- push word+word / uword+uword
@ -778,13 +665,10 @@ greaterequalzero_sb .proc
greaterequalzero_sw .proc greaterequalzero_sw .proc
lda P8ESTACK_HI+1,x lda P8ESTACK_HI+1,x
bpl equalzero_b._true
bmi equalzero_b._false bmi equalzero_b._false
ora P8ESTACK_LO+1,x
beq equalzero_b._true
bne equalzero_b._false
.pend .pend
memcopy16_up .proc memcopy16_up .proc
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi) ; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
; clobbers register A,X,Y ; clobbers register A,X,Y

View File

@ -129,7 +129,7 @@ _startloop dey
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc { asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc {
; Locates the first position of the given character in the string, ; 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. ; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
%asm {{ %asm {{
; need to copy the the cx16 virtual registers to zeropage to make this run on C64... ; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
@ -144,7 +144,8 @@ _startloop dey
beq _found beq _found
iny iny
bne - bne -
_notfound clc _notfound lda #0
clc
rts rts
_found tya _found tya
sec sec
@ -223,6 +224,26 @@ _done rts
}} }}
} }
sub startswith(str st, str prefix) -> bool {
ubyte prefix_len = length(prefix)
ubyte str_len = length(st)
if prefix_len > str_len
return false
cx16.r9L = st[prefix_len]
st[prefix_len] = 0
cx16.r9H = compare(st, prefix) as ubyte
st[prefix_len] = cx16.r9L
return cx16.r9H==0
}
sub endswith(str st, str suffix) -> bool {
ubyte suffix_len = length(suffix)
ubyte str_len = length(st)
if suffix_len > str_len
return false
return compare(st + str_len - suffix_len, suffix) == 0
}
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A { asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
%asm {{ %asm {{
; pattern matching of a string. ; pattern matching of a string.
@ -293,5 +314,4 @@ fail clc ; yes, no match found, return with c=0
rts rts
}} }}
} }
} }

View File

@ -196,7 +196,7 @@ sub str2uword(str string) -> uword {
; the number may NOT be preceded by a + sign and may NOT contain spaces ; 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) ; (any non-digit character will terminate the number string that is parsed)
%asm {{ %asm {{
loadm.w r0, {conv.str2uword.string} loadm.w r0,conv.str2uword.string
syscall 11 syscall 11
return return
}} }}
@ -207,7 +207,7 @@ sub str2word(str string) -> word {
; the number may be preceded by a + or - sign but may NOT contain spaces ; 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) ; (any non-digit character will terminate the number string that is parsed)
%asm {{ %asm {{
loadm.w r0, {conv.str2word.string} loadm.w r0,conv.str2word.string
syscall 12 syscall 12
return return
}} }}
@ -251,4 +251,13 @@ sub bin2uword(str string) -> uword {
} }
} }
sub any2uword(str string) -> uword {
; -- convert any number string (any prefix allowed) to uword.
when string[0] {
'$' -> return hex2uword(string)
'%' -> return bin2uword(string)
else -> return str2uword(string)
}
}
} }

View File

@ -10,7 +10,7 @@ floats {
sub print_f(float value) { sub print_f(float value) {
; ---- prints the floating point value (without a newline). ; ---- prints the floating point value (without a newline).
%asm {{ %asm {{
loadm.f fr0,{floats.print_f.value} loadm.f fr0,floats.print_f.value
syscall 25 syscall 25
return return
}} }}
@ -18,8 +18,8 @@ sub print_f(float value) {
sub pow(float value, float power) -> float { sub pow(float value, float power) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.pow.value} loadm.f fr0,floats.pow.value
loadm.f fr1,{floats.pow.power} loadm.f fr1,floats.pow.power
fpow.f fr0,fr1 fpow.f fr0,fr1
return return
}} }}
@ -27,7 +27,7 @@ sub pow(float value, float power) -> float {
sub fabs(float value) -> float { sub fabs(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.fabs.value} loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0 fabs.f fr0,fr0
return return
}} }}
@ -35,7 +35,7 @@ sub fabs(float value) -> float {
sub sin(float angle) -> float { sub sin(float angle) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.sin.angle} loadm.f fr0,floats.sin.angle
fsin.f fr0,fr0 fsin.f fr0,fr0
return return
}} }}
@ -43,7 +43,7 @@ sub sin(float angle) -> float {
sub cos(float angle) -> float { sub cos(float angle) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.cos.angle} loadm.f fr0,floats.cos.angle
fcos.f fr0,fr0 fcos.f fr0,fr0
return return
}} }}
@ -51,7 +51,7 @@ sub cos(float angle) -> float {
sub tan(float value) -> float { sub tan(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.tan.value} loadm.f fr0,floats.tan.value
ftan.f fr0,fr0 ftan.f fr0,fr0
return return
}} }}
@ -59,7 +59,7 @@ sub tan(float value) -> float {
sub atan(float value) -> float { sub atan(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.atan.value} loadm.f fr0,floats.atan.value
fatan.f fr0,fr0 fatan.f fr0,fr0
return return
}} }}
@ -67,7 +67,7 @@ sub atan(float value) -> float {
sub ln(float value) -> float { sub ln(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.ln.value} loadm.f fr0,floats.ln.value
fln.f fr0,fr0 fln.f fr0,fr0
return return
}} }}
@ -75,7 +75,7 @@ sub ln(float value) -> float {
sub log2(float value) -> float { sub log2(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.log2.value} loadm.f fr0,floats.log2.value
flog.f fr0,fr0 flog.f fr0,fr0
return return
}} }}
@ -83,8 +83,8 @@ sub log2(float value) -> float {
sub sqrt(float value) -> float { sub sqrt(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.sqrt.value} loadm.f fr0,floats.sqrt.value
fsqrt.f fr0,fr0 sqrt.f fr0,fr0
return return
}} }}
} }
@ -101,7 +101,7 @@ sub deg(float angle) -> float {
sub round(float value) -> float { sub round(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.round.value} loadm.f fr0,floats.round.value
fround.f fr0,fr0 fround.f fr0,fr0
return return
}} }}
@ -109,7 +109,7 @@ sub round(float value) -> float {
sub floor(float value) -> float { sub floor(float value) -> float {
%asm {{ %asm {{
loadm.f fr0,{floats.floor.value} loadm.f fr0,floats.floor.value
ffloor.f fr0,fr0 ffloor.f fr0,fr0
return return
}} }}
@ -118,7 +118,7 @@ sub floor(float value) -> float {
sub ceil(float value) -> float { sub ceil(float value) -> float {
; -- ceil: tr = int(f); if tr==f -> return else return tr+1 ; -- ceil: tr = int(f); if tr==f -> return else return tr+1
%asm {{ %asm {{
loadm.f fr0,{floats.ceil.value} loadm.f fr0,floats.ceil.value
fceil.f fr0,fr0 fceil.f fr0,fr0
return return
}} }}

View File

@ -44,27 +44,49 @@ math {
} }
sub sin8(ubyte angle) -> byte { sub sin8(ubyte angle) -> byte {
return 42 ; TODO ubyte[256] sintab = [
$00, $03, $06, $09,
$0c, $0f, $12, $15, $18, $1b, $1e, $21, $24, $27, $2a, $2d, $30, $33, $36, $39,
$3b, $3e, $41, $43, $46, $49, $4b, $4e, $50, $52, $55, $57, $59, $5b, $5e, $60,
$62, $64, $66, $67, $69, $6b, $6c, $6e, $70, $71, $72, $74, $75, $76, $77, $78,
$79, $7a, $7b, $7b, $7c, $7d, $7d, $7e, $7e, $7e, $7e, $7e, $7f, $7e, $7e, $7e,
$7e, $7e, $7d, $7d, $7c, $7b, $7b, $7a, $79, $78, $77, $76, $75, $74, $72, $71,
$70, $6e, $6c, $6b, $69, $67, $66, $64, $62, $60, $5e, $5b, $59, $57, $55, $52,
$50, $4e, $4b, $49, $46, $43, $41, $3e, $3b, $39, $36, $33, $30, $2d, $2a, $27,
$24, $21, $1e, $1b, $18, $15, $12, $0f, $0c, $09, $06, $03, $00, $fd, $fa, $f7,
$f4, $f1, $ee, $eb, $e8, $e5, $e2, $df, $dc, $d9, $d6, $d3, $d0, $cd, $ca, $c7,
$c5, $c2, $bf, $bd, $ba, $b7, $b5, $b2, $b0, $ae, $ab, $a9, $a7, $a5, $a2, $a0,
$9e, $9c, $9a, $99, $97, $95, $94, $92, $90, $8f, $8e, $8c, $8b, $8a, $89, $88,
$87, $86, $85, $85, $84, $83, $83, $82, $82, $82, $82, $82, $81, $82, $82, $82,
$82, $82, $83, $83, $84, $85, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8e, $8f,
$90, $92, $94, $95, $97, $99, $9a, $9c, $9e, $a0, $a2, $a5, $a7, $a9, $ab, $ae,
$b0, $b2, $b5, $b7, $ba, $bd, $bf, $c2, $c5, $c7, $ca, $cd, $d0, $d3, $d6, $d9,
$dc, $df, $e2, $e5, $e8, $eb, $ee, $f1, $f4, $f7, $fa, $fd
]
return sintab[angle] as byte
} }
sub cos8(ubyte angle) -> byte { sub cos8(ubyte angle) -> byte {
return 42 ; TODO ubyte[256] costab = [
} $7f, $7e, $7e, $7e,
$7e, $7e, $7d, $7d, $7c, $7b, $7b, $7a, $79, $78, $77, $76, $75, $74, $72, $71,
sub sin16(ubyte angle) -> word { $70, $6e, $6c, $6b, $69, $67, $66, $64, $62, $60, $5e, $5b, $59, $57, $55, $52,
return 4242 ; TODO $50, $4e, $4b, $49, $46, $43, $41, $3e, $3b, $39, $36, $33, $30, $2d, $2a, $27,
} $24, $21, $1e, $1b, $18, $15, $12, $0f, $0c, $09, $06, $03, $00, $fd, $fa, $f7,
$f4, $f1, $ee, $eb, $e8, $e5, $e2, $df, $dc, $d9, $d6, $d3, $d0, $cd, $ca, $c7,
sub cos16(ubyte angle) -> word { $c5, $c2, $bf, $bd, $ba, $b7, $b5, $b2, $b0, $ae, $ab, $a9, $a7, $a5, $a2, $a0,
return 4242 ; TODO $9e, $9c, $9a, $99, $97, $95, $94, $92, $90, $8f, $8e, $8c, $8b, $8a, $89, $88,
} $87, $86, $85, $85, $84, $83, $83, $82, $82, $82, $82, $82, $81, $82, $82, $82,
$82, $82, $83, $83, $84, $85, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8e, $8f,
sub sin16u(ubyte angle) -> uword { $90, $92, $94, $95, $97, $99, $9a, $9c, $9e, $a0, $a2, $a5, $a7, $a9, $ab, $ae,
return 4242 ; TODO $b0, $b2, $b5, $b7, $ba, $bd, $bf, $c2, $c5, $c7, $ca, $cd, $d0, $d3, $d6, $d9,
} $dc, $df, $e2, $e5, $e8, $eb, $ee, $f1, $f4, $f7, $fa, $fd, $00, $03, $06, $09,
$0c, $0f, $12, $15, $18, $1b, $1e, $21, $24, $27, $2a, $2d, $30, $33, $36, $39,
sub cos16u(ubyte angle) -> uword { $3b, $3e, $41, $43, $46, $49, $4b, $4e, $50, $52, $55, $57, $59, $5b, $5e, $60,
return 4242 ; TODO $62, $64, $66, $67, $69, $6b, $6c, $6e, $70, $71, $72, $74, $75, $76, $77, $78,
$79, $7a, $7b, $7b, $7c, $7d, $7d, $7e, $7e, $7e, $7e, $7e
]
return costab[angle] as byte
} }
sub sinr8u(ubyte radians) -> ubyte { sub sinr8u(ubyte radians) -> ubyte {
@ -102,26 +124,39 @@ math {
} }
sub sinr8(ubyte radians) -> byte { sub sinr8(ubyte radians) -> byte {
return 42 ; TODO ubyte[180] sintab = [
$00, $04, $08, $0d,
$11, $16, $1a, $1e, $23, $27, $2b, $2f, $33, $37, $3b, $3f, $43, $47, $4a, $4e,
$51, $54, $58, $5b, $5e, $61, $64, $66, $69, $6b, $6d, $70, $72, $74, $75, $77,
$78, $7a, $7b, $7c, $7d, $7d, $7e, $7e, $7e, $7f, $7e, $7e, $7e, $7d, $7d, $7c,
$7b, $7a, $78, $77, $75, $74, $72, $70, $6d, $6b, $69, $66, $64, $61, $5e, $5b,
$58, $54, $51, $4e, $4a, $47, $43, $3f, $3b, $37, $33, $2f, $2b, $27, $23, $1e,
$1a, $16, $11, $0d, $08, $04, $00, $fc, $f8, $f3, $ef, $ea, $e6, $e2, $dd, $d9,
$d5, $d1, $cd, $c9, $c5, $c1, $bd, $b9, $b6, $b2, $af, $ac, $a8, $a5, $a2, $9f,
$9c, $9a, $97, $95, $93, $90, $8e, $8c, $8b, $89, $88, $86, $85, $84, $83, $83,
$82, $82, $82, $81, $82, $82, $82, $83, $83, $84, $85, $86, $88, $89, $8b, $8c,
$8e, $90, $93, $95, $97, $9a, $9c, $9f, $a2, $a5, $a8, $ac, $af, $b2, $b6, $b9,
$bd, $c1, $c5, $c9, $cd, $d1, $d5, $d9, $dd, $e2, $e6, $ea, $ef, $f3, $f8, $fc
]
return sintab[radians] as byte
} }
sub cosr8(ubyte radians) -> byte { sub cosr8(ubyte radians) -> byte {
return 42 ; TODO ubyte[180] costab = [
$7f, $7e, $7e, $7e, $7d, $7d, $7c,
$7b, $7a, $78, $77, $75, $74, $72, $70, $6d, $6b, $69, $66, $64, $61, $5e, $5b,
$58, $54, $51, $4e, $4a, $47, $43, $3f, $3b, $37, $33, $2f, $2b, $27, $23, $1e,
$1a, $16, $11, $0d, $08, $04, $00, $fc, $f8, $f3, $ef, $ea, $e6, $e2, $dd, $d9,
$d5, $d1, $cd, $c9, $c5, $c1, $bd, $b9, $b6, $b2, $af, $ac, $a8, $a5, $a2, $9f,
$9c, $9a, $97, $95, $93, $90, $8e, $8c, $8b, $89, $88, $86, $85, $84, $83, $83,
$82, $82, $82, $81, $82, $82, $82, $83, $83, $84, $85, $86, $88, $89, $8b, $8c,
$8e, $90, $93, $95, $97, $9a, $9c, $9f, $a2, $a5, $a8, $ac, $af, $b2, $b6, $b9,
$bd, $c1, $c5, $c9, $cd, $d1, $d5, $d9, $dd, $e2, $e6, $ea, $ef, $f3, $f8, $fc,
$00, $04, $08, $0d, $11, $16, $1a, $1e, $23, $27, $2b, $2f, $33, $37, $3b, $3f,
$43, $47, $4a, $4e, $51, $54, $58, $5b, $5e, $61, $64, $66, $69, $6b, $6d, $70,
$72, $74, $75, $77, $78, $7a, $7b, $7c, $7d, $7d, $7e, $7e, $7e
]
return costab[radians] as byte
} }
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

@ -42,8 +42,8 @@ prog8_lib {
; Note that you can also directly compare strings and string values with eachother using ; 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). ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
%asm {{ %asm {{
loadm.w r0, {prog8_lib.string_compare.st1} loadm.w r0,prog8_lib.string_compare.st1
loadm.w r1, {prog8_lib.string_compare.st2} loadm.w r1,prog8_lib.string_compare.st2
syscall 29 syscall 29
return return
}} }}

View File

@ -113,4 +113,24 @@ string {
ix++ ix++
} }
} }
sub startswith(str st, str prefix) -> bool {
ubyte prefix_len = length(prefix)
ubyte str_len = length(st)
if prefix_len > str_len
return false
cx16.r9L = st[prefix_len]
st[prefix_len] = 0
cx16.r9H = compare(st, prefix) as ubyte
st[prefix_len] = cx16.r9L
return cx16.r9H==0
}
sub endswith(str st, str suffix) -> bool {
ubyte suffix_len = length(suffix)
ubyte str_len = length(st)
if suffix_len > str_len
return false
return compare(st + str_len - suffix_len, suffix) == 0
}
} }

View File

@ -15,7 +15,7 @@ sys {
sub wait(uword jiffies) { sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds) ; --- wait approximately the given number of jiffies (1/60th seconds)
%asm {{ %asm {{
loadm.w r0, {sys.wait.jiffies} loadm.w r0,sys.wait.jiffies
syscall 13 syscall 13
}} }}
} }
@ -62,7 +62,7 @@ sys {
sub exit(ubyte returnvalue) { sub exit(ubyte returnvalue) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
loadm.b r0,{sys.exit.returnvalue} loadm.b r0,sys.exit.returnvalue
syscall 1 syscall 1
}} }}
} }
@ -82,125 +82,140 @@ sys {
sub gfx_enable(ubyte mode) { sub gfx_enable(ubyte mode) {
%asm {{ %asm {{
loadm.b r0, {sys.gfx_enable.mode} loadm.b r0,sys.gfx_enable.mode
syscall 8 syscall 8
}} }}
} }
sub gfx_clear(ubyte color) {
%asm {{
loadm.b r0,sys.gfx_clear.color
syscall 9
}}
}
sub gfx_plot(uword xx, uword yy, ubyte color) { sub gfx_plot(uword xx, uword yy, ubyte color) {
%asm {{ %asm {{
loadm.w r0, {sys.gfx_plot.xx} loadm.w r0,sys.gfx_plot.xx
loadm.w r1, {sys.gfx_plot.yy} loadm.w r1,sys.gfx_plot.yy
loadm.b r2, {sys.gfx_plot.color} loadm.b r2,sys.gfx_plot.color
syscall 10 syscall 10
}} }}
} }
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%asm {{
loadm.w r0,sys.gfx_getpixel.xx
loadm.w r1,sys.gfx_getpixel.yy
syscall 30
return
}}
}
} }
cx16 { cx16 {
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the VirtualMachine as well but their location in memory is different ; they are simulated on the VirtualMachine as well but their location in memory is different
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s) &uword r0 = $ff02
&uword r0 = $0002 &uword r1 = $ff04
&uword r1 = $0004 &uword r2 = $ff06
&uword r2 = $0006 &uword r3 = $ff08
&uword r3 = $0008 &uword r4 = $ff0a
&uword r4 = $000a &uword r5 = $ff0c
&uword r5 = $000c &uword r6 = $ff0e
&uword r6 = $000e &uword r7 = $ff10
&uword r7 = $0010 &uword r8 = $ff12
&uword r8 = $0012 &uword r9 = $ff14
&uword r9 = $0014 &uword r10 = $ff16
&uword r10 = $0016 &uword r11 = $ff18
&uword r11 = $0018 &uword r12 = $ff1a
&uword r12 = $001a &uword r13 = $ff1c
&uword r13 = $001c &uword r14 = $ff1e
&uword r14 = $001e &uword r15 = $ff20
&uword r15 = $0020
&word r0s = $0002 &word r0s = $ff02
&word r1s = $0004 &word r1s = $ff04
&word r2s = $0006 &word r2s = $ff06
&word r3s = $0008 &word r3s = $ff08
&word r4s = $000a &word r4s = $ff0a
&word r5s = $000c &word r5s = $ff0c
&word r6s = $000e &word r6s = $ff0e
&word r7s = $0010 &word r7s = $ff10
&word r8s = $0012 &word r8s = $ff12
&word r9s = $0014 &word r9s = $ff14
&word r10s = $0016 &word r10s = $ff16
&word r11s = $0018 &word r11s = $ff18
&word r12s = $001a &word r12s = $ff1a
&word r13s = $001c &word r13s = $ff1c
&word r14s = $001e &word r14s = $ff1e
&word r15s = $0020 &word r15s = $ff20
&ubyte r0L = $0002 &ubyte r0L = $ff02
&ubyte r1L = $0004 &ubyte r1L = $ff04
&ubyte r2L = $0006 &ubyte r2L = $ff06
&ubyte r3L = $0008 &ubyte r3L = $ff08
&ubyte r4L = $000a &ubyte r4L = $ff0a
&ubyte r5L = $000c &ubyte r5L = $ff0c
&ubyte r6L = $000e &ubyte r6L = $ff0e
&ubyte r7L = $0010 &ubyte r7L = $ff10
&ubyte r8L = $0012 &ubyte r8L = $ff12
&ubyte r9L = $0014 &ubyte r9L = $ff14
&ubyte r10L = $0016 &ubyte r10L = $ff16
&ubyte r11L = $0018 &ubyte r11L = $ff18
&ubyte r12L = $001a &ubyte r12L = $ff1a
&ubyte r13L = $001c &ubyte r13L = $ff1c
&ubyte r14L = $001e &ubyte r14L = $ff1e
&ubyte r15L = $0020 &ubyte r15L = $ff20
&ubyte r0H = $0003 &ubyte r0H = $ff03
&ubyte r1H = $0005 &ubyte r1H = $ff05
&ubyte r2H = $0007 &ubyte r2H = $ff07
&ubyte r3H = $0009 &ubyte r3H = $ff09
&ubyte r4H = $000b &ubyte r4H = $ff0b
&ubyte r5H = $000d &ubyte r5H = $ff0d
&ubyte r6H = $000f &ubyte r6H = $ff0f
&ubyte r7H = $0011 &ubyte r7H = $ff11
&ubyte r8H = $0013 &ubyte r8H = $ff13
&ubyte r9H = $0015 &ubyte r9H = $ff15
&ubyte r10H = $0017 &ubyte r10H = $ff17
&ubyte r11H = $0019 &ubyte r11H = $ff19
&ubyte r12H = $001b &ubyte r12H = $ff1b
&ubyte r13H = $001d &ubyte r13H = $ff1d
&ubyte r14H = $001f &ubyte r14H = $ff1f
&ubyte r15H = $0021 &ubyte r15H = $ff21
&byte r0sL = $0002 &byte r0sL = $ff02
&byte r1sL = $0004 &byte r1sL = $ff04
&byte r2sL = $0006 &byte r2sL = $ff06
&byte r3sL = $0008 &byte r3sL = $ff08
&byte r4sL = $000a &byte r4sL = $ff0a
&byte r5sL = $000c &byte r5sL = $ff0c
&byte r6sL = $000e &byte r6sL = $ff0e
&byte r7sL = $0010 &byte r7sL = $ff10
&byte r8sL = $0012 &byte r8sL = $ff12
&byte r9sL = $0014 &byte r9sL = $ff14
&byte r10sL = $0016 &byte r10sL = $ff16
&byte r11sL = $0018 &byte r11sL = $ff18
&byte r12sL = $001a &byte r12sL = $ff1a
&byte r13sL = $001c &byte r13sL = $ff1c
&byte r14sL = $001e &byte r14sL = $ff1e
&byte r15sL = $0020 &byte r15sL = $ff20
&byte r0sH = $0003 &byte r0sH = $ff03
&byte r1sH = $0005 &byte r1sH = $ff05
&byte r2sH = $0007 &byte r2sH = $ff07
&byte r3sH = $0009 &byte r3sH = $ff09
&byte r4sH = $000b &byte r4sH = $ff0b
&byte r5sH = $000d &byte r5sH = $ff0d
&byte r6sH = $000f &byte r6sH = $ff0f
&byte r7sH = $0011 &byte r7sH = $ff11
&byte r8sH = $0013 &byte r8sH = $ff13
&byte r9sH = $0015 &byte r9sH = $ff15
&byte r10sH = $0017 &byte r10sH = $ff17
&byte r11sH = $0019 &byte r11sH = $ff19
&byte r12sH = $001b &byte r12sH = $ff1b
&byte r13sH = $001d &byte r13sH = $ff1d
&byte r14sH = $001f &byte r14sH = $ff1f
&byte r15sH = $0021 &byte r15sH = $ff21
} }

View File

@ -7,7 +7,7 @@ txt {
sub clear_screen() { sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H" str @shared sequence = "\x1b[2J\x1B[H"
%asm {{ %asm {{
load.w r0, {txt.clear_screen.sequence} load.w r0,txt.clear_screen.sequence
syscall 3 syscall 3
}} }}
} }
@ -30,14 +30,14 @@ sub uppercase() {
sub chrout(ubyte char) { sub chrout(ubyte char) {
%asm {{ %asm {{
loadm.b r0, {txt.chrout.char} loadm.b r0,txt.chrout.char
syscall 2 syscall 2
}} }}
} }
sub print (str text) { sub print (str text) {
%asm {{ %asm {{
loadm.w r0, {txt.print.text} loadm.w r0,txt.print.text
syscall 3 syscall 3
}} }}
} }
@ -114,7 +114,7 @@ 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) ; ---- 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! ; It assumes the keyboard is selected as I/O channel!
%asm {{ %asm {{
loadm.w r0,{txt.input_chars.buffer} loadm.w r0,txt.input_chars.buffer
syscall 6 syscall 6
return return
}} }}

View File

@ -1 +1 @@
8.2 8.5

View File

@ -2,22 +2,19 @@ package prog8
import kotlinx.cli.* import kotlinx.cli.*
import prog8.ast.base.AstException import prog8.ast.base.AstException
import prog8.code.core.CbmPrgLauncherType import prog8.code.core.*
import prog8.code.target.* import prog8.code.target.*
import prog8.code.target.virtual.VirtualMachineDefinition import prog8.code.target.virtual.VirtualMachineDefinition
import prog8.codegen.virtual.VmCodeGen
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import java.io.File import java.io.File
import java.nio.file.FileSystems import java.nio.file.*
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardWatchEventKinds
import java.time.LocalDateTime import java.time.LocalDateTime
import kotlin.system.exitProcess import kotlin.system.exitProcess
fun main(args: Array<String>) { fun main(args: Array<String>) {
val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim() val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim()
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)") println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
@ -34,22 +31,24 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
private fun compileMain(args: Array<String>): Boolean { private fun compileMain(args: Array<String>): Boolean {
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM) val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
val evalStackAddrString by cli.option(ArgType.String, fullName = "esa", description = "override the eval-stack base address (must be page aligned)")
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation") val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation") val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".") val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
val keepIR by cli.option(ArgType.Boolean, fullName = "keepIR", description = "keep the IR code file (for targets that use it)")
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code") val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations") val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
val dontReinitGlobals by cli.option(ArgType.Boolean, fullName = "noreinit", description = "don't create code to reinitialize globals on multiple runs of the program (experimental!)") val dontReinitGlobals by cli.option(ArgType.Boolean, fullName = "noreinit", description = "don't create code to reinitialize globals on multiple runs of the program (experimental!)")
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)") val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results") val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well") val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')")
.default(C64Target.NAME)
val 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 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 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 startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a p8-virt or p8-ir listing in the VM instead")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999) val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try { try {
@ -84,6 +83,27 @@ private fun compileMain(args: Array<String>): Boolean {
return runVm(moduleFiles.first()) return runVm(moduleFiles.first())
} }
var evalStackAddr: UInt? = null
if(evalStackAddrString!=null) {
try {
evalStackAddr = if (evalStackAddrString!!.startsWith("0x"))
evalStackAddrString!!.substring(2).toUInt(16)
else if (evalStackAddrString!!.startsWith("$"))
evalStackAddrString!!.substring(1).toUInt(16)
else
evalStackAddrString!!.toUInt()
} catch(nx: NumberFormatException) {
System.err.println("invalid address for evalstack: $evalStackAddrString")
return false
}
if(evalStackAddr !in 256u..65536u-512u || (evalStackAddr and 255u != 0u)) {
System.err.println("invalid address for evalstack: ${evalStackAddr.toHex()}")
return false
}
}
val processedSymbols = processSymbolDefs(symbolDefs) ?: return false
if(watchMode==true) { if(watchMode==true) {
val watchservice = FileSystems.getDefault().newWatchService() val watchservice = FileSystems.getDefault().newWatchService()
val allImportedFiles = mutableSetOf<Path>() val allImportedFiles = mutableSetOf<Path>()
@ -103,7 +123,10 @@ private fun compileMain(args: Array<String>): Boolean {
quietAssembler == true, quietAssembler == true,
asmListfile == true, asmListfile == true,
experimentalCodegen == true, experimentalCodegen == true,
keepIR == true,
compilationTarget, compilationTarget,
evalStackAddr,
processedSymbols,
srcdirs, srcdirs,
outputPath outputPath
) )
@ -124,16 +147,26 @@ private fun compileMain(args: Array<String>): Boolean {
} }
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.") println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
var recompile=false fun determineRecompilationNeeded(event: WatchKey): Boolean {
while(!recompile) { if(event.isValid) {
val event = watchservice.take()
for (changed in event.pollEvents()) { for (changed in event.pollEvents()) {
if (changed.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
val changedPath = changed.context() as Path val changedPath = changed.context() as Path
if (allImportedFiles.any { it.fileName == changedPath.fileName }) { if (allImportedFiles.any { it.fileName == changedPath.fileName }) {
println(" change detected: $changedPath") println(" change detected: $changedPath")
recompile = true return true
} }
} }
}
}
return false
}
var recompile=false
while(!recompile) {
val event = watchservice.take()
Thread.sleep(50) // avoid multiple events on same file
recompile = determineRecompilationNeeded(event)
event.reset() event.reset()
} }
@ -155,7 +188,10 @@ private fun compileMain(args: Array<String>): Boolean {
quietAssembler == true, quietAssembler == true,
asmListfile == true, asmListfile == true,
experimentalCodegen == true, experimentalCodegen == true,
keepIR == true,
compilationTarget, compilationTarget,
evalStackAddr,
processedSymbols,
srcdirs, srcdirs,
outputPath outputPath
) )
@ -193,13 +229,43 @@ private fun compileMain(args: Array<String>): Boolean {
return true return true
} }
private fun processSymbolDefs(symbolDefs: List<String>): Map<String, String>? {
val result = mutableMapOf<String, String>()
val defPattern = """(.+)\s*=\s*(.+)""".toRegex()
for(def in symbolDefs) {
val match = defPattern.matchEntire(def.trim())
if(match==null) {
System.err.println("invalid symbol definition (expected NAME=VALUE): $def")
return null
}
val (_, name, value) = match.groupValues
result[name.trim()] = value.trim()
}
return result
}
fun runVm(listingFilename: String): Boolean { fun runVm(listingFilename: String): Boolean {
val name = if(listingFilename.endsWith(".p8ir")) {
if(listingFilename.endsWith(".p8virt")) val withoutSuffix = listingFilename.substring(0, listingFilename.length-5)
listingFilename.substring(0, listingFilename.length-7) val compiled = VmCodeGen.compileIR(withoutSuffix)
else if (!compiled.assemble(CompilationOptions( // these are just dummy options, the actual options are inside the .p8ir file itself:
listingFilename OutputType.PRG,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
emptyList(),
floats = true,
noSysInit = true,
compTarget = VMTarget(),
loadAddress = VMTarget().machine.PROGRAM_LOAD_ADDRESS
))
) {
return false
}
val vmdef = VirtualMachineDefinition() val vmdef = VirtualMachineDefinition()
vmdef.launchEmulator(0, Paths.get(name)) vmdef.launchEmulator(0, Paths.get(withoutSuffix))
return true
}
val vmdef = VirtualMachineDefinition()
vmdef.launchEmulator(0, Paths.get(listingFilename))
return true return true
} }

View File

@ -37,7 +37,10 @@ class CompilerArguments(val filepath: Path,
val quietAssembler: Boolean, val quietAssembler: Boolean,
val asmListfile: Boolean, val asmListfile: Boolean,
val experimentalCodegen: Boolean, val experimentalCodegen: Boolean,
val keepIR: Boolean,
val compilationTarget: String, val compilationTarget: String,
val evalStackBaseAddress: UInt?,
val symbolDefs: Map<String, String>,
val sourceDirs: List<String> = emptyList(), val sourceDirs: List<String> = emptyList(),
val outputDir: Path = Path(""), val outputDir: Path = Path(""),
val errors: IErrorReporter = ErrorReporter()) val errors: IErrorReporter = ErrorReporter())
@ -77,10 +80,18 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
asmQuiet = args.quietAssembler asmQuiet = args.quietAssembler
asmListfile = args.asmListfile asmListfile = args.asmListfile
experimentalCodegen = args.experimentalCodegen experimentalCodegen = args.experimentalCodegen
keepIR = args.keepIR
evalStackBaseAddress = args.evalStackBaseAddress
outputDir = args.outputDir.normalize() outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs
} }
program = programresult program = programresult
importedFiles = imported importedFiles = imported
if(compilationOptions.evalStackBaseAddress!=null) {
compTarget.machine.overrideEvalStack(compilationOptions.evalStackBaseAddress!!)
}
processAst(program, args.errors, compilationOptions) processAst(program, args.errors, compilationOptions)
if (compilationOptions.optimize) { if (compilationOptions.optimize) {
// println("*********** AST RIGHT BEFORE OPTIMIZING *************") // println("*********** AST RIGHT BEFORE OPTIMIZING *************")
@ -319,7 +330,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) { private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
println("Analyzing code...") println("Analyzing code...")
program.preprocessAst(errors, compilerOptions.compTarget) program.preprocessAst(errors, compilerOptions)
program.checkIdentifiers(errors, compilerOptions) program.checkIdentifiers(errors, compilerOptions)
errors.report() errors.report()
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors) program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)
@ -330,6 +341,8 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
errors.report() errors.report()
program.reorderStatements(errors, compilerOptions) program.reorderStatements(errors, compilerOptions)
errors.report() errors.report()
program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
errors.report()
program.addTypecasts(errors, compilerOptions) program.addTypecasts(errors, compilerOptions)
errors.report() errors.report()
program.variousCleanups(errors, compilerOptions) program.variousCleanups(errors, compilerOptions)
@ -347,7 +360,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
remover.applyModifications() remover.applyModifications()
while (true) { while (true) {
// keep optimizing expressions and statements until no more steps remain // keep optimizing expressions and statements until no more steps remain
val optsDone1 = program.simplifyExpressions() val optsDone1 = program.simplifyExpressions(compTarget)
val optsDone2 = program.splitBinaryExpressions(compilerOptions) val optsDone2 = program.splitBinaryExpressions(compilerOptions)
val optsDone3 = program.optimizeStatements(errors, functions, compTarget) val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
val optsDone4 = program.inlineSubroutines() val optsDone4 = program.inlineSubroutines()
@ -434,18 +447,15 @@ internal fun asmGeneratorFor(program: Program,
options: CompilationOptions): IAssemblyGenerator options: CompilationOptions): IAssemblyGenerator
{ {
if(options.experimentalCodegen) { 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() val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.experimental.AsmGen(intermediateAst, symbolTable, options, errors) return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors)
}
} else { } else {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
// TODO rewrite 6502 codegen on new Intermediary Ast // TODO rewrite 6502 codegen on new Intermediary Ast or on new Intermediate Representation
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors) return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
if (options.compTarget.name == VMTarget.NAME) { if (options.compTarget.name == VMTarget.NAME) {
val intermediateAst = IntermediateAstMaker(program).transform() val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.virtual.CodeGen(intermediateAst, symbolTable, options, errors) return prog8.codegen.virtual.VmCodeGen(intermediateAst, symbolTable, options, errors)
} }
} }

View File

@ -78,16 +78,30 @@ internal class AstChecker(private val program: Program,
if(!valueDt.isKnown) { if(!valueDt.isKnown) {
errors.err("return value type mismatch or unknown symbol", returnStmt.value!!.position) errors.err("return value type mismatch or unknown symbol", returnStmt.value!!.position)
} else { } else {
if (expectedReturnValues[0] != valueDt.getOr(DataType.UNDEFINED)) if (expectedReturnValues[0] != valueDt.getOr(DataType.UNDEFINED)) {
if(valueDt istype DataType.BOOL && expectedReturnValues[0] == DataType.UBYTE) {
// if the return value is a bool and the return type is ubyte, allow this. But give a warning.
errors.warn("return type of the subroutine should probably be bool instead of ubyte", returnStmt.position)
} else if(valueDt istype DataType.UBYTE && expectedReturnValues[0] == DataType.BOOL) {
// if the return value is ubyte and the return type is bool, allow this only if value is 0 or 1
val returnValue = returnStmt.value as? NumericLiteral
if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position) errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
} }
} }
else {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
}
}
}
}
super.visit(returnStmt) super.visit(returnStmt)
} }
override fun visit(ifElse: IfElse) { override fun visit(ifElse: IfElse) {
if(!ifElse.condition.inferType(program).isInteger) val dt = ifElse.condition.inferType(program)
errors.err("condition value should be an integer type", ifElse.condition.position) if(!dt.isInteger && !dt.istype(DataType.BOOL))
errors.err("condition value should be an integer type or bool", ifElse.condition.position)
super.visit(ifElse) super.visit(ifElse)
} }
@ -119,6 +133,10 @@ internal class AstChecker(private val program: Program,
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression) checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
} }
DataType.BOOL -> {
if(iterableDt != DataType.ARRAY_BOOL)
errors.err("bool loop variable can only loop over boolean array", forLoop.position)
}
DataType.UWORD -> { DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR && if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW) iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
@ -184,16 +202,6 @@ internal class AstChecker(private val program: Program,
super.visit(jump) super.visit(jump)
} }
override fun visit(gosub: GoSub) {
val targetStatement = checkFunctionOrLabelExists(gosub.identifier, gosub)
if(targetStatement!=null) {
if(targetStatement is BuiltinFunctionPlaceholder)
errors.err("can't gosub to a builtin function", gosub.position)
}
super.visit(gosub)
}
override fun visit(block: Block) { override fun visit(block: Block) {
val addr = block.address val addr = block.address
if(addr!=null && addr>65535u) { if(addr!=null && addr>65535u) {
@ -251,10 +259,7 @@ internal class AstChecker(private val program: Program,
) )
count++ count++
} else { } else {
if(" return" in assembly || "\treturn" in assembly if(" return" in assembly || "\treturn" in assembly || " jump" in assembly || "\tjump" in assembly)
|| " jump" in assembly || "\tjump" in assembly
|| " jumpi" in assembly || "\tjumpi" in assembly
)
count++ count++
} }
} }
@ -295,7 +300,7 @@ internal class AstChecker(private val program: Program,
if (subroutine.returntypes.isNotEmpty()) { if (subroutine.returntypes.isNotEmpty()) {
// for asm subroutines with an address, no statement check is possible. // for asm subroutines with an address, no statement check is possible.
if (subroutine.asmAddress == null && !subroutine.inline) if (subroutine.asmAddress == null && !subroutine.inline)
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or rts/jmp/bra in case of %asm)") err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or the assembler equivalent in case of %asm)")
} }
} }
@ -309,8 +314,8 @@ internal class AstChecker(private val program: Program,
err("number of return registers is not the isSameAs as number of return values") err("number of return registers is not the isSameAs as number of return values")
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) { for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) { if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE) if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL)
err("parameter '${param.first.name}' should be (u)byte") err("parameter '${param.first.name}' should be (u)byte or bool")
} }
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
@ -324,17 +329,17 @@ internal class AstChecker(private val program: Program,
} }
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair -> subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
if(pair.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) { if(pair.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (pair.first != DataType.UBYTE && pair.first != DataType.BYTE) if (pair.first != DataType.UBYTE && pair.first != DataType.BYTE && pair.first != DataType.BOOL)
err("return value #${index + 1} should be (u)byte") err("return type #${index + 1} should be (u)byte")
} }
else if(pair.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { else if(pair.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (pair.first != DataType.UWORD && pair.first != DataType.WORD if (pair.first != DataType.UWORD && pair.first != DataType.WORD
&& pair.first != DataType.STR && pair.first !in ArrayDatatypes && pair.first != DataType.FLOAT) && pair.first != DataType.STR && pair.first !in ArrayDatatypes && pair.first != DataType.FLOAT)
err("return value #${index + 1} should be (u)word/address") err("return type #${index + 1} should be (u)word/address")
} }
else if(pair.second.statusflag!=null) { else if(pair.second.statusflag!=null) {
if (pair.first != DataType.UBYTE) if (pair.first != DataType.UBYTE)
err("return value #${index + 1} should be ubyte") err("return type #${index + 1} should be ubyte")
} }
} }
@ -418,14 +423,16 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(untilLoop: UntilLoop) { override fun visit(untilLoop: UntilLoop) {
if(!untilLoop.condition.inferType(program).isInteger) val dt = untilLoop.condition.inferType(program)
errors.err("condition value should be an integer type", untilLoop.condition.position) if(!dt.isInteger && !dt.istype(DataType.BOOL))
errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
super.visit(untilLoop) super.visit(untilLoop)
} }
override fun visit(whileLoop: WhileLoop) { override fun visit(whileLoop: WhileLoop) {
if(!whileLoop.condition.inferType(program).isInteger) val dt = whileLoop.condition.inferType(program)
errors.err("condition value should be an integer type", whileLoop.condition.position) if(!dt.isInteger && !dt.istype(DataType.BOOL))
errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
super.visit(whileLoop) super.visit(whileLoop)
} }
@ -462,6 +469,13 @@ internal class AstChecker(private val program: Program,
if(numvalue!=null && targetDt.isKnown) if(numvalue!=null && targetDt.isKnown)
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue) checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
// for now, don't enforce bool type with only logical operators...
// if(assignment.isAugmentable && targetDt istype DataType.BOOL) {
// val operator = (assignment.value as? BinaryExpression)?.operator
// if(operator in InvalidOperatorsForBoolean)
// errors.err("can't use boolean operand with this operator $operator", assignment.position)
// }
super.visit(assignment) super.visit(assignment)
} }
@ -603,7 +617,7 @@ internal class AstChecker(private val program: Program,
err("memory address must be valid integer 0..\$ffff") err("memory address must be valid integer 0..\$ffff")
} }
} else { } else {
err("value of memory mapped variable can only be a fixed number, perhaps you meant to use an address pointer type instead?") err("value of memory mapped variable can only be a constant, maybe use an address pointer type instead?")
} }
} }
} }
@ -772,8 +786,11 @@ internal class AstChecker(private val program: Program,
fun isPassByReferenceElement(e: Expression): Boolean { fun isPassByReferenceElement(e: Expression): Boolean {
if(e is IdentifierReference) { if(e is IdentifierReference) {
val decl = e.targetVarDecl(program)!! val decl = e.targetVarDecl(program)
return decl.datatype in PassByReferenceDatatypes return if(decl!=null)
decl.datatype in PassByReferenceDatatypes
else
true // is probably a symbol that needs addr-of
} }
return e is StringLiteral return e is StringLiteral
} }
@ -823,10 +840,6 @@ internal class AstChecker(private val program: Program,
errors.err("can only take negative of a signed number type", expr.position) errors.err("can only take negative of a signed number type", expr.position)
} }
} }
else if(expr.operator == "not") {
if(dt !in IntegerDatatypes)
errors.err("can only use boolean not on integer types", expr.position)
}
else if(expr.operator == "~") { else if(expr.operator == "~") {
if(dt !in IntegerDatatypes) if(dt !in IntegerDatatypes)
errors.err("can only use bitwise invert on integer types", expr.position) errors.err("can only use bitwise invert on integer types", expr.position)
@ -863,11 +876,6 @@ internal class AstChecker(private val program: Program,
errors.err("remainder can only be used on unsigned integer operands", expr.right.position) errors.err("remainder can only be used on unsigned integer operands", expr.right.position)
} }
} }
"and", "or", "xor" -> {
// 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)
}
"&", "|", "^" -> { "&", "|", "^" -> {
// only integer numeric operands accepted // only integer numeric operands accepted
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes) if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
@ -876,15 +884,17 @@ internal class AstChecker(private val program: Program,
"in" -> throw FatalAstException("in expression should have been replaced by containmentcheck") "in" -> throw FatalAstException("in expression should have been replaced by containmentcheck")
} }
if(leftDt !in NumericDatatypes && leftDt != DataType.STR) if(leftDt !in NumericDatatypes && leftDt != DataType.STR && leftDt != DataType.BOOL)
errors.err("left operand is not numeric or str", expr.left.position) errors.err("left operand is not numeric or str", expr.left.position)
if(rightDt!in NumericDatatypes && rightDt != DataType.STR) if(rightDt!in NumericDatatypes && rightDt != DataType.STR && rightDt != DataType.BOOL)
errors.err("right operand is not numeric or str", expr.right.position) errors.err("right operand is not numeric or str", expr.right.position)
if(leftDt!=rightDt) { if(leftDt!=rightDt) {
if(leftDt==DataType.STR && rightDt in IntegerDatatypes && expr.operator=="*") { if(leftDt==DataType.STR && rightDt in IntegerDatatypes && expr.operator=="*") {
// only exception allowed: str * constvalue // 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) errors.err("can only use string repeat with a constant number value", expr.left.position)
} else if(leftDt==DataType.BOOL && rightDt in ByteDatatypes || leftDt in ByteDatatypes && rightDt==DataType.BOOL) {
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} else { } else {
errors.err("left and right operands aren't the same type", expr.left.position) errors.err("left and right operands aren't the same type", expr.left.position)
} }
@ -893,10 +903,31 @@ internal class AstChecker(private val program: Program,
if(expr.operator !in ComparisonOperators) { if(expr.operator !in ComparisonOperators) {
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) { if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
// str+str and str*number have already been const evaluated before we get here. // str+str and str*number have already been const evaluated before we get here.
errors.err("no computational expressions with strings or arrays are possible", expr.position) errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
} }
} }
if(leftDt==DataType.BOOL || rightDt==DataType.BOOL ||
(expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true ||
(expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true) {
if(expr.operator in setOf("<", "<=", ">", ">=")) {
errors.err("can't use boolean operand with this comparison operator", expr.position)
}
// for now, don't enforce bool type with only logical operators...
// if(expr.operator in InvalidOperatorsForBoolean && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
// errors.err("can't use boolean operand with this operator ${expr.operator}", expr.left.position)
// }
if(expr.operator == "==" || expr.operator == "!=") {
val leftNum = expr.left.constValue(program)?.number ?: 0.0
val rightNum = expr.right.constValue(program)?.number ?: 0.0
if(leftNum>1.0 || rightNum>1.0 || leftNum<0.0 || rightNum<0.0) {
errors.warn("expression is always false", expr.position)
}
}
if((expr.operator == "/" || expr.operator == "%") && ( rightDt==DataType.BOOL || (expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
errors.err("can't use boolean operand with this operator ${expr.operator}", expr.right.position)
}
}
} }
override fun visit(typecast: TypecastExpression) { override fun visit(typecast: TypecastExpression) {
@ -1081,8 +1112,14 @@ internal class AstChecker(private val program: Program,
} }
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) { if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
var regname = ident.nameInSource[1].uppercase() var regname = ident.nameInSource[1].uppercase()
if(regname.endsWith('L') || regname.endsWith('H') || regname.endsWith('s')) val lastLetter = regname.last().lowercaseChar()
if(lastLetter in setOf('l', 'h', 's')) {
regname = regname.substring(0, regname.length - 1) regname = regname.substring(0, regname.length - 1)
val lastLetter2 = regname.last().lowercaseChar()
if(lastLetter2 in setOf('l', 'h', 's')) {
regname = regname.substring(0, regname.length - 1)
}
}
val reg = RegisterOrPair.valueOf(regname) val reg = RegisterOrPair.valueOf(regname)
val same = params.filter { it.value.registerOrPair==reg } val same = params.filter { it.value.registerOrPair==reg }
for(s in same) { for(s in same) {
@ -1122,6 +1159,11 @@ internal class AstChecker(private val program: Program,
} }
} }
// else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/-- // else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/--
if(postIncrDecr.target.inferType(program) istype DataType.BOOL) {
errors.err("can't use boolean operand with this operator ${postIncrDecr.operator}", postIncrDecr.position)
}
super.visit(postIncrDecr) super.visit(postIncrDecr)
} }
@ -1379,6 +1421,9 @@ internal class AstChecker(private val program: Program,
if (number < -32768 || number > 32767) if (number < -32768 || number > 32767)
return err("value '$number' out of range for word") return err("value '$number' out of range for word")
} }
DataType.BOOL -> {
return true
}
else -> return err("value of type ${value.type} not compatible with $targetDt") else -> return err("value of type ${value.type} not compatible with $targetDt")
} }
return true return true
@ -1438,10 +1483,11 @@ internal class AstChecker(private val program: Program,
} }
val result = when(targetDatatype) { val result = when(targetDatatype) {
DataType.BYTE -> sourceDatatype== DataType.BYTE DataType.BOOL -> sourceDatatype in NumericDatatypes
DataType.UBYTE -> sourceDatatype== DataType.UBYTE DataType.BYTE -> sourceDatatype == DataType.BYTE || sourceDatatype == DataType.BOOL
DataType.WORD -> sourceDatatype== DataType.BYTE || sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.WORD DataType.UBYTE -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.BOOL
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD DataType.WORD -> sourceDatatype in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.BOOL)
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD || sourceDatatype == DataType.BOOL
DataType.FLOAT -> sourceDatatype in NumericDatatypes DataType.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype == DataType.STR DataType.STR -> sourceDatatype == DataType.STR
else -> { else -> {

View File

@ -6,10 +6,13 @@ import prog8.ast.expressions.CharLiteral
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDeclOrigin import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) { internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
@ -21,6 +24,9 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
} }
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) { internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
val boolRemover = BoolRemover(this)
boolRemover.visit(this)
boolRemover.applyModifications()
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors) val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
fixer.visit(this) fixer.visit(this)
while (errors.noErrors() && fixer.applyModifications() > 0) { while (errors.noErrors() && fixer.applyModifications() > 0) {
@ -44,6 +50,14 @@ internal fun Program.reorderStatements(errors: IErrorReporter, options: Compilat
} }
} }
internal fun Program.changeNotExpressionAndIfComparisonExpr(errors: IErrorReporter, target: ICompilationTarget) {
val changer = NotExpressionAndIfComparisonExprChanger(this, errors, target)
changer.visit(this)
while(errors.noErrors() && changer.applyModifications()>0) {
changer.visit(this)
}
}
internal fun Program.charLiteralsToUByteLiterals(target: ICompilationTarget, errors: IErrorReporter) { internal fun Program.charLiteralsToUByteLiterals(target: ICompilationTarget, errors: IErrorReporter) {
val walker = object : AstWalker() { val walker = object : AstWalker() {
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> { override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
@ -80,8 +94,8 @@ internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter) {
fixer.visit(this) fixer.visit(this)
} }
internal fun Program.preprocessAst(errors: IErrorReporter, target: ICompilationTarget) { internal fun Program.preprocessAst(errors: IErrorReporter, options: CompilationOptions) {
val transforms = AstPreprocessor(this, errors, target) val transforms = AstPreprocessor(this, errors, options)
transforms.visit(this) transforms.visit(this)
var mods = transforms.applyModifications() var mods = transforms.applyModifications()
while(mods>0) while(mods>0)
@ -154,3 +168,17 @@ internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolea
} }
return false return false
} }
internal fun Subroutine.hasRtsInAsm(compTarget: ICompilationTarget): Boolean {
val instructions =
if(compTarget.name == VMTarget.NAME)
listOf(" return", "\treturn", " jump", "\tjump")
else
listOf(" rti", "\trti", " rts", "\trts", " jmp", "\tjmp", " bra", "\tbra")
return statements
.asSequence()
.filterIsInstance<InlineAssembly>()
.any {
instructions.any { instr->instr in it.assembly }
}
}

View File

@ -7,23 +7,68 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.Encoding import prog8.code.core.*
import prog8.code.core.ICompilationTarget import prog8.code.target.C64Target
import prog8.code.core.IErrorReporter import prog8.code.target.Cx16Target
import prog8.code.core.NumericDatatypes import prog8.code.target.VMTarget
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() { class AstPreprocessor(val program: Program,
val errors: IErrorReporter,
val options: CompilationOptions) : AstWalker() {
override fun before(program: Program): Iterable<IAstModification> {
if(options.compTarget.name==C64Target.NAME) {
if(options.zeropage==ZeropageType.KERNALSAFE || options.zeropage==ZeropageType.FULL) {
// there is enough space in the zero page to put the cx16 virtual registers there.
// unfortunately, can't be the same address as CommanderX16.
relocateCx16VirtualRegisters(program, 0x0004u)
}
}
else if(options.compTarget.name !in setOf(Cx16Target.NAME, VMTarget.NAME)) {
relocateCx16VirtualRegisters(program, options.compTarget.machine.ESTACK_HI)
}
return noModifications
}
private fun relocateCx16VirtualRegisters(program: Program, baseAddress: UInt) {
// reset the address of the virtual registers to be inside the evaluation stack.
// (we don't do this on CommanderX16 itself as the registers have a fixed location in Zeropage there)
val cx16block = program.allBlocks.single { it.name == "cx16" }
val memVars = cx16block.statements
.filterIsInstance<VarDecl>()
.associateBy { it.name }
for (regnum in 0u..15u) {
val rX = memVars.getValue("r$regnum")
val rXL = memVars.getValue("r${regnum}L")
val rXH = memVars.getValue("r${regnum}H")
val rXs = memVars.getValue("r${regnum}s")
val rXsL = memVars.getValue("r${regnum}sL")
val rXsH = memVars.getValue("r${regnum}sH")
setAddress(rX, baseAddress + 2u * regnum)
setAddress(rXL, baseAddress + 2u * regnum)
setAddress(rXH, baseAddress + 2u * regnum + 1u)
setAddress(rXs, baseAddress + 2u * regnum)
setAddress(rXsL, baseAddress + 2u * regnum)
setAddress(rXsH, baseAddress + 2u * regnum + 1u)
}
}
private fun setAddress(vardecl: VarDecl, address: UInt) {
val oldAddr = vardecl.value as NumericLiteral
vardecl.value = NumericLiteral(oldAddr.type, address.toDouble(), oldAddr.position)
}
override fun before(char: CharLiteral, parent: Node): Iterable<IAstModification> { override fun before(char: CharLiteral, parent: Node): Iterable<IAstModification> {
if(char.encoding== Encoding.DEFAULT) if(char.encoding== Encoding.DEFAULT)
char.encoding = compTarget.defaultEncoding char.encoding = options.compTarget.defaultEncoding
return noModifications return noModifications
} }
override fun before(string: StringLiteral, parent: Node): Iterable<IAstModification> { override fun before(string: StringLiteral, parent: Node): Iterable<IAstModification> {
if(string.encoding==Encoding.DEFAULT) if(string.encoding==Encoding.DEFAULT)
string.encoding = compTarget.defaultEncoding string.encoding = options.compTarget.defaultEncoding
return super.before(string, parent) return super.before(string, parent)
} }
@ -98,6 +143,7 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
val containment = ContainmentCheck(expr.left, expr.right, expr.position) val containment = ContainmentCheck(expr.left, expr.right, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent)) return listOf(IAstModification.ReplaceNode(expr, containment, parent))
} }
return noModifications return noModifications
} }

View File

@ -1,8 +1,11 @@
package prog8.compiler.astprocessing package prog8.compiler.astprocessing
import prog8.ast.* import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.getTempVar
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
@ -67,6 +70,10 @@ internal class BeforeAsmAstChanger(val program: Program,
// Try to replace A = B <operator> Something by A= B, A = A <operator> Something // Try to replace A = B <operator> Something by A= B, A = A <operator> Something
// this triggers the more efficent augmented assignment code generation more often. // this triggers the more efficent augmented assignment code generation more often.
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF. // But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimization for Vm target
return noModifications
if(!assignment.isAugmentable if(!assignment.isAugmentable
&& assignment.target.identifier != null && assignment.target.identifier != null
&& !assignment.target.isIOAddress(options.compTarget.machine)) { && !assignment.target.isIOAddress(options.compTarget.machine)) {
@ -120,7 +127,6 @@ internal class BeforeAsmAstChanger(val program: Program,
} }
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> { override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
// Most code generation targets only support subroutine inlining on asmsub subroutines // 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. // 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) if(!subroutine.isAsmSubroutine && options.compTarget.name!=VMTarget.NAME)
@ -132,7 +138,7 @@ internal class BeforeAsmAstChanger(val program: Program,
// and if an assembly block doesn't contain a rts/rti, and some other situations. // and if an assembly block doesn't contain a rts/rti, and some other situations.
if (!subroutine.isAsmSubroutine) { if (!subroutine.isAsmSubroutine) {
if(subroutine.statements.isEmpty() || if(subroutine.statements.isEmpty() ||
(subroutine.amountOfRtsInAsm() == 0 (!subroutine.hasRtsInAsm(options.compTarget)
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return && subroutine.statements.lastOrNull { it !is VarDecl } !is Return
&& subroutine.statements.last() !is Subroutine && subroutine.statements.last() !is Subroutine
&& subroutine.statements.last() !is Return)) { && subroutine.statements.last() !is Return)) {
@ -158,10 +164,11 @@ internal class BeforeAsmAstChanger(val program: Program,
} }
if (!subroutine.inline || !options.optimize) { if (!subroutine.inline || !options.optimize) {
if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && subroutine.amountOfRtsInAsm() == 0) { if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && !subroutine.hasRtsInAsm(options.compTarget)) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end // make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above) // (non-asm routines get a Return statement as needed, above)
mods += IAstModification.InsertLast(InlineAssembly(" rts\n", Position.DUMMY), subroutine) val instruction = if(options.compTarget.name==VMTarget.NAME) " return\n" else " rts\n"
mods += IAstModification.InsertLast(InlineAssembly(instruction, Position.DUMMY), subroutine)
} }
} }
@ -169,21 +176,36 @@ internal class BeforeAsmAstChanger(val program: Program,
} }
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> { override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
val prefixExpr = ifElse.condition as? PrefixExpression val binExpr = ifElse.condition as? BinaryExpression
if(prefixExpr!=null && prefixExpr.operator=="not") { if(binExpr==null) {
// if not x -> if x==0 // if x -> if x!=0
val booleanExpr = BinaryExpression( val booleanExpr = BinaryExpression(
prefixExpr.expression, ifElse.condition,
"==", "!=",
NumericLiteral.optimalInteger(0, ifElse.condition.position), NumericLiteral.optimalInteger(0, ifElse.condition.position),
ifElse.condition.position ifElse.condition.position
) )
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse)) return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
} }
val binExpr = ifElse.condition as? BinaryExpression if(binExpr.operator !in ComparisonOperators) {
if(binExpr==null || binExpr.operator !in ComparisonOperators) { val constRight = binExpr.right.constValue(program)
// if x -> if x!=0, if x+5 -> if x+5 != 0 if(constRight!=null) {
if (binExpr.operator == "+") {
// if x+5 -> if x != -5
val number = NumericLiteral(constRight.type, -constRight.number, constRight.position)
val booleanExpr = BinaryExpression(binExpr.left,"!=", number, ifElse.condition.position)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
}
else if (binExpr.operator == "-") {
// if x-5 -> if x != 5
val number = NumericLiteral(constRight.type, constRight.number, constRight.position)
val booleanExpr = BinaryExpression(binExpr.left,"!=", number, ifElse.condition.position)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
}
}
// if x*5 -> if x*5 != 0
val booleanExpr = BinaryExpression( val booleanExpr = BinaryExpression(
ifElse.condition, ifElse.condition,
"!=", "!=",
@ -197,84 +219,7 @@ internal class BeforeAsmAstChanger(val program: Program,
(binExpr.right as? NumericLiteral)?.number!=0.0) (binExpr.right as? NumericLiteral)?.number!=0.0)
throw InternalCompilerException("0==X should have been swapped to if X==0") throw InternalCompilerException("0==X should have been swapped to if X==0")
// simplify the conditional expression, introduce simple assignments if required. return noModifications
// NOTE: sometimes this increases code size because additional stores/loads are generated for the
// intermediate variables. We assume these are optimized away from the resulting assembly code later.
val simplify = simplifyConditionalExpression(binExpr)
val modifications = mutableListOf<IAstModification>()
if(simplify.rightVarAssignment!=null) {
modifications += IAstModification.ReplaceNode(binExpr.right, simplify.rightOperandReplacement!!, binExpr)
modifications += IAstModification.InsertBefore(
ifElse,
simplify.rightVarAssignment,
parent as IStatementContainer
)
}
if(simplify.leftVarAssignment!=null) {
modifications += IAstModification.ReplaceNode(binExpr.left, simplify.leftOperandReplacement!!, binExpr)
modifications += IAstModification.InsertBefore(
ifElse,
simplify.leftVarAssignment,
parent as IStatementContainer
)
}
return modifications
}
private class CondExprSimplificationResult(
val leftVarAssignment: Assignment?,
val leftOperandReplacement: Expression?,
val rightVarAssignment: Assignment?,
val rightOperandReplacement: Expression?
)
private fun simplifyConditionalExpression(expr: BinaryExpression): CondExprSimplificationResult {
// 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)
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimization for Vm target
return CondExprSimplificationResult(null, null, null, null)
var leftAssignment: Assignment? = null
var leftOperandReplacement: Expression? = null
var rightAssignment: Assignment? = null
var rightOperandReplacement: Expression? = null
val separateLeftExpr = !expr.left.isSimple && expr.left !is IFunctionCall && expr.left !is ContainmentCheck
val separateRightExpr = !expr.right.isSimple && expr.right !is IFunctionCall && expr.right !is ContainmentCheck
val leftDt = expr.left.inferType(program)
val rightDt = expr.right.inferType(program)
if(!leftDt.isInteger || !rightDt.isInteger) {
// we can't reasonably simplify non-integer expressions
return CondExprSimplificationResult(null, null, null, null)
}
if(separateLeftExpr) {
val name = getTempRegisterName(leftDt)
leftOperandReplacement = IdentifierReference(name, expr.position)
leftAssignment = Assignment(
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
expr.left,
AssignmentOrigin.BEFOREASMGEN, expr.position
)
}
if(separateRightExpr) {
val (tempVarName, _) = program.getTempVar(rightDt.getOrElse { throw FatalAstException("invalid dt") }, true)
rightOperandReplacement = IdentifierReference(tempVarName, expr.position)
rightAssignment = Assignment(
AssignTarget(IdentifierReference(tempVarName, expr.position), null, null, expr.position),
expr.right,
AssignmentOrigin.BEFOREASMGEN, expr.position
)
}
return CondExprSimplificationResult(
leftAssignment, leftOperandReplacement,
rightAssignment, rightOperandReplacement
)
} }
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> { override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {

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