Compare commits

...
157 Commits
Author SHA1 Message Date
Irmen de Jong f874942075 version 10.1 2024-02-04 16:22:43 +01:00
Irmen de Jong 2cadb546d5 optimized in-place memory/pointervar operations some more 2024-02-04 15:17:24 +01:00
Irmen de Jong 344a1b9eb8 cx16: added cx16.get_program_args() and cx16.set_program_args() 2024-02-04 11:35:13 +01:00
Irmen de Jong 3c77f8a020 IR: optimize pointer access 2024-02-04 07:51:50 +01:00
Irmen de Jong 8e00408e3e optimize pointer indexing a bit 2024-02-03 23:24:33 +01:00
Irmen de Jong abcdfd8e28 optimize postincrdecr on memory a bit 2024-02-03 22:33:27 +01:00
Irmen de Jong b0f5b6925d getting rid of pointer[idx] in ast, instead always use @(ptr+idx) 2024-02-03 19:22:04 +01:00
Irmen de Jong ef79d0c43e fix temp variable name clash resulting in faulty code in certain common subexpression optimization 2024-02-03 19:20:28 +01:00
Irmen de Jong 78b4288005 avoid git's line ending translation for this test... 2024-02-03 14:57:25 +01:00
Irmen de Jong 680f5d21ee normalize DOS line endings to just \n, fixes source lines in asm for \r\n source files 2024-02-03 12:53:21 +01:00
Irmen de Jong c71aa0895f get rid of certain redundant !=0 comparisons in logical expressions 2024-02-01 21:50:01 +01:00
Irmen de Jong 9f8e61789a fix 65c02 codegen for certain in-place byte bitwise invert 2024-01-31 23:55:43 +01:00
Irmen de Jong 932035cdc5 flag "returning a statement" as a syntax error 2024-01-31 21:36:39 +01:00
Irmen de Jong ef198f1493 fix for certain invalid expression evaluation code.
it no longer reuses the same temporary variable all the time.

todo
2024-01-31 21:18:21 +01:00
Irmen de Jong 48ef856c0b identified wrong expr eval code - still need solution. Crash for now. 2024-01-30 23:00:50 +01:00
Irmen de Jong 9aea2b22c4 doc improvement, fixes #120 2024-01-30 19:07:18 +01:00
Irmen de Jong e0055bc431 IR: working on inplace +/- 2024-01-29 22:08:47 +01:00
Irmen de Jong 9553248ed6 IR: integrate inplace assignment ops 2024-01-28 22:33:16 +01:00
Irmen de Jong 39d2194d8f IR: implemented inplace prefix op on split array
VM: NEG instructions also set N and Z flags
2024-01-28 22:33:13 +01:00
Irmen de Jong 0800033b47 fixed split-word array in-place element +/- (other operators not yet...) 2024-01-28 22:30:45 +01:00
Irmen de Jong 64d8943b7d fix error when trying to post-inc/decrement a character in a string 2024-01-28 16:47:55 +01:00
Irmen de Jong 444e97b00b todo 2024-01-27 12:31:36 +01:00
Irmen de Jong 1816bda7ea extra check 2024-01-26 00:12:31 +01:00
Irmen de Jong d4a2031c07 fix certain assignment data type mismatch crash 2024-01-25 21:14:20 +01:00
Irmen de Jong 8cf0b6cf51 IR: optimize code for ==0 and !=0 augmented assigns 2024-01-25 00:30:47 +01:00
Irmen de Jong f2010bf7a5 IR: better code for array element assignments (w.i.p.) 2024-01-23 00:56:06 +01:00
Irmen de Jong 8f56a7fe69 IR: use INV instead of XOR for bitwise invert 2024-01-22 22:47:54 +01:00
Irmen de Jong 64c132ee0a changed -breakinstr option so that you now specify the exact instruction to use for a %breakpoint.
also fixed a IR issue with x=not x.
2024-01-22 22:01:47 +01:00
Irmen de Jong 84a7e86fe3 clarify progend() 2024-01-22 18:57:25 +01:00
Irmen de Jong a8c09d6144 removed a problematic bitshift replacement in the const evaluator 2024-01-21 23:05:51 +01:00
Irmen de Jong 87c46ba730 check boolean array size mismatch.
check for weird string assignment.
check for X16 problematic cpu instructions rmb, smb, bbr, bbs.
tweak number node equality wrt bool type
2024-01-21 19:15:22 +01:00
Irmen de Jong 0f83dc6491 sponsorship link.
error sorting.
version.
2024-01-19 20:04:29 +01:00
Irmen de Jong cc22861719 Create FUNDING.yml 2024-01-19 19:26:41 +01:00
Irmen de Jong a14c192ea3 also other targets 2024-01-18 22:31:34 +01:00
Irmen de Jong b3d98be862 oops, removed a bit too much when getting rid of the noshortcircuit code 2024-01-18 22:00:57 +01:00
Irmen de Jong 43027a4728 IR: optimize rol ror 2024-01-18 21:51:44 +01:00
Irmen de Jong 03831a7394 added cx16.cpu_is_65816() 2024-01-18 19:30:18 +01:00
Irmen de Jong fdbbd181ea fixes for address-of uword pointer array expressions 2024-01-17 22:51:15 +01:00
Irmen de Jong 69075376dc get rid of the noshortcircuit fallback 2024-01-17 21:24:41 +01:00
Irmen de Jong 504d1440cc fixed rol(),rol2(),ror(),ror2() 2024-01-17 21:02:17 +01:00
OODLER 9e33b8b8da Added a couple of for examples using descending ranges. (#119) 2024-01-16 20:19:05 +01:00
Irmen de Jong 0cfcc5cd29 fix VM sgn() function for floats 2024-01-16 01:34:55 +01:00
Irmen de Jong e0de662f8e fix signed word bitshift right (>8 shifts) 2024-01-16 01:08:16 +01:00
Irmen de Jong 66a836d094 added support for reverse() on split word arrays 2024-01-16 00:52:09 +01:00
Irmen de Jong 80095f4962 added support for any() on split word arrays 2024-01-15 23:51:19 +01:00
Irmen de Jong 828d83dbef loadScaledArrayIndexIntoRegister(): useless type arg removed 2024-01-15 22:19:52 +01:00
Irmen de Jong 7de665d1e4 support for split wordarrays rol/ror and rol2/ror2
optimized rol and ror codegen

optimize/fix ror/ror2/rol/rol2 on word arrays
2024-01-15 03:22:37 +01:00
Irmen de Jong 0a356ba73a added containment check of float arrays 2024-01-14 14:14:09 +01:00
Irmen de Jong 41de8caa13 added sprites.set_mousepointer_image(), sprites.set_mousepointer_hand() and sprites.get_data_ptr() 2024-01-14 00:38:56 +01:00
Irmen de Jong 968609d06d IR: fix problems with symbol offsets and unused subroutines/chunks 2024-01-13 16:43:41 +01:00
Irmen de Jong 3b199a2a87 added cx16 example: automatons.
added debug mode and RTC to cx16 emulator launchers.
dt error details.
2024-01-12 21:40:30 +01:00
Irmen de Jong 0c1018ec61 dt error details 2024-01-12 17:34:19 +01:00
Irmen de Jong bc3f2db3de Fix call graph to no longer mark subroutines unused, that still get their variables referenced somewhere.
Revert palette.default_colors_16[] back to palette.set_default16.colors.
2024-01-11 22:12:01 +01:00
Irmen de Jong 06bedb7adb added palette.get_color() and palette.default_colors[] 2024-01-11 21:27:18 +01:00
Irmen de Jong 45a9751217 fix type of optimized lsb() / mkword() arguments when signed.
printast1 command line option now also works in case of compilation errors.
2024-01-10 23:57:44 +01:00
Irmen de Jong e8da62aa29 update Kotlin version and libs 2024-01-10 20:31:30 +01:00
Irmen de Jong ddb2ff4216 IR: use SCS opcode to set carry status flag into register 2024-01-09 23:46:27 +01:00
Irmen de Jong f27e3478b9 fix const value of AddressOf for certain types 2024-01-09 22:10:25 +01:00
Irmen de Jong 38dc7fb7bd IR: added SCC and SCS instructions 2024-01-09 01:41:37 +01:00
Irmen de Jong aa4cd13c31 where to place vscode syntax files. 2024-01-08 00:09:21 +01:00
Irmen de Jong 4a4b6c04a1 tweak common subexpression optimization for array lookups 2024-01-07 22:37:15 +01:00
Irmen de Jong 37fa3b34a2 tweak IR 2024-01-07 22:12:09 +01:00
Irmen de Jong f8084e7955 fix const replacement optimization error on memory mapped variable 2024-01-07 18:48:18 +01:00
Irmen de Jong 4d5119ce3e VM: also set N+Z flags on AND/OR/XOR instructions 2024-01-07 15:26:36 +01:00
Irmen de Jong d85c347a6c optimize /256 more, and fixed a unsigned byte word cast error 2024-01-07 02:34:05 +01:00
Irmen de Jong 7dd758a753 better optimize WORD & $xx00 , WORD & $00xx 2024-01-06 22:01:21 +01:00
Irmen de Jong 806654fc44 fix invalid const optimization with multiplication 2024-01-06 13:59:13 +01:00
Irmen de Jong 8e6b91cb9e some optimizations 2024-01-06 00:44:00 +01:00
Irmen de Jong 334e6dca28 added string.contains().
fixed string and array containment check for length 1.
2024-01-05 20:46:26 +01:00
Irmen de Jong f2daa17b92 tweak some not optimizations
cleanup IR typestring
2024-01-05 17:49:56 +01:00
Irmen de Jong 6d9fccacb1 boolean not expression tweaks and optimizations 2024-01-05 13:32:16 +01:00
Irmen de Jong 37638e7ed0 added Absorption laws optimization 2024-01-05 00:36:47 +01:00
Irmen de Jong 8a0e650511 apply De Morgan's laws for logical not, results in smaller code 2024-01-04 23:45:46 +01:00
Irmen de Jong 8ba5a0d90c tweak planet name display in starmap 2024-01-04 21:52:52 +01:00
Irmen de Jong bfd3edb617 fix expression evaluation bug where intermediate values were overwritten, yielding the wrong result 2024-01-04 21:04:11 +01:00
Irmen de Jong 56ba24962c fixed 'not not x' optimization to just 'x' 2024-01-04 15:02:21 +01:00
Irmen de Jong 19a2110ba2 fix exit() now actually correctly setting the return code in A
also, moved some cleanup stuff such as CLRCHN from exit() to the cleanup routine that is always called.
finally, also call the cleanup routine when  %option no_sysinit is used
2024-01-04 00:43:35 +01:00
Irmen de Jong 242a3eec63 fix data type difference error on range from and to values 2024-01-03 21:46:22 +01:00
Irmen de Jong fee46f2e54 todo 2024-01-03 15:06:27 +01:00
Irmen de Jong 6aed7e429a allow containment check in a range expression ("run time" range expression) 2024-01-03 01:17:13 +01:00
Irmen de Jong 517ea82b99 fixed todos in Ast printer 2024-01-01 23:52:03 +01:00
Irmen de Jong 99c29343de added -printast1 and -printast2 command line options 2024-01-01 22:48:19 +01:00
Irmen de Jong 892fa76883 remove debug output 2024-01-01 20:48:41 +01:00
Irmen de Jong d446b57d05 fix unittest 2024-01-01 16:11:50 +01:00
Irmen de Jong 0e086d788b removed chained comparisons again, because they caused invalid expression evaluations due to changed semantics. 2024-01-01 16:00:11 +01:00
Irmen de Jong 498841d45d remove binexpr operand rotation that didn't help much at all 2024-01-01 15:12:15 +01:00
Irmen de Jong d1f8ee1e56 replace most common subexpressions by a single temp variable 2024-01-01 14:57:24 +01:00
Irmen de Jong 07feb5c925 extra check 2023-12-31 17:04:28 +01:00
Irmen de Jong 75fd263e85 fix expericodegen crash related to shortcircuiting 2023-12-31 01:28:17 +01:00
Irmen de Jong 1e1f444cab cleanups.
also checked that value(12) < x < value(100) is indeed properly shortcircuited if x is 12 or less
2023-12-30 04:34:07 +01:00
Irmen de Jong 89cc7e5fa9 finalize short-circuit eval in IR codegen 2023-12-30 04:26:29 +01:00
Irmen de Jong 265e7aefbf clean up unused codegen for logical ops on words, also fix no-shortcircuit exception 2023-12-30 04:06:02 +01:00
Irmen de Jong 1c55a6c6dc shortcutting part one 2023-12-30 03:54:12 +01:00
Irmen de Jong 8f18b5b8a7 keep distinction between logical and bitwise boolean operators 2023-12-30 01:08:42 +01:00
Irmen de Jong f790182f0b adding short-circuit boolean expression evaluation (in IR codegen) also -noshortcircuit cli option 2023-12-30 01:08:41 +01:00
Irmen de Jong 813007a5d8 adjusted options of library modules 2023-12-29 22:21:44 +01:00
Irmen de Jong d03ff1e4d0 improved var -> const replacement, now done in constfolding already (fixes some obscure problems later on)
Also fixed some directive parenting errors
2023-12-29 19:48:40 +01:00
Irmen de Jong 932bbd0381 allow casting of byte<->ubyte and word<->uword 2023-12-29 16:23:24 +01:00
Irmen de Jong 01bd648cb2 added math.crc16() and math.crc32() 2023-12-29 08:00:02 +01:00
Irmen de Jong 779a5606a7 add unittest for aa%bb (without space) to be parsed correctly as modulo, not directive 2023-12-29 05:11:50 +01:00
Irmen de Jong ccc11e49d2 fix asmgen for uword shift right 8 or more bits 2023-12-29 05:06:09 +01:00
Irmen de Jong d28c994ecd directive really needs to be listed out in the parser otherwise it confuses it with % modulo :-(
Also fix missing const fold pass in optimizer
2023-12-29 03:45:20 +01:00
Irmen de Jong 5d88717f32 fix non-existing instructions txy/tyx, oops 2023-12-29 03:27:35 +01:00
Irmen de Jong e35cfd4971 get rid of the redundant 'f' suffix of several funtions in floats module (breaking change!)
Unfortunately a few routines (minf, maxf, clampf) remain unchanged, because removing the 'f' would make them clash with a builtin function.

floats.rndf -> floats.rnd
floats.parse_f -> floats.parse
floats.rndseedf -> floats.rndseed
floats.print_f -> floats.print
floats.str_f -> floats.tostr
2023-12-29 03:12:44 +01:00
Irmen de Jong bcc4bf5c2b almost forgot 2023-12-28 20:39:27 +01:00
Irmen de Jong a0594cbce3 const optimizer now knows about a bunch of library functions, such as math.* 2023-12-28 20:14:13 +01:00
Irmen de Jong 078bfefe41 clarify scoped names a bit more 2023-12-28 16:36:29 +01:00
Irmen de Jong 9c1b11d605 some WARN messages are now INFO 2023-12-28 14:20:47 +01:00
Irmen de Jong 44d82f9190 add unit tests 2023-12-28 13:30:07 +01:00
Irmen de Jong 37fcde30d6 constants have p8c_ prefix instead of p8v_ in the asm 2023-12-28 05:28:32 +01:00
Irmen de Jong 09c6cb4d6b replace unwritten vars by consts. Improved const eval.
Fixed some slight bugs in library code
2023-12-28 05:17:15 +01:00
Irmen de Jong b428343c2a tweak chained comparisons 2023-12-28 02:31:39 +01:00
Irmen de Jong dfce292294 allow chained comparisons i<x<j (desugared into: i<x and x<j) 2023-12-28 01:18:59 +01:00
Irmen de Jong 2b8f613a00 added %option ignore_unused to suppress warnings about unused vars and subs in that module/block.
Also improved error for invalid directive.
2023-12-26 23:37:59 +01:00
Irmen de Jong 2eb137618e refactor block options 2023-12-26 22:13:08 +01:00
Irmen de Jong 4bb2b8ca9b make isArray a computed property by simply checking the datatype 2023-12-26 19:58:08 +01:00
Irmen de Jong 5179562fb2 can be val 2023-12-26 19:39:02 +01:00
Irmen de Jong 0a4de45453 get rid of vardecl.declareddatatype 2023-12-26 19:33:58 +01:00
Irmen de Jong ffdc658dc8 type error tweaks 2023-12-26 18:49:01 +01:00
Irmen de Jong 7530f4407b ir tag change INLINEASM->ASM 2023-12-26 16:15:19 +01:00
Irmen de Jong 73864c8101 added -check command line option 2023-12-26 15:45:55 +01:00
Irmen de Jong f948917124 added floats.push() and floats.pop()
fixed vm pop.f
2023-12-26 15:19:49 +01:00
Irmen de Jong 0d44492086 push,pushw,pop and popw are no longer built-in functions but regular routines in sys 2023-12-26 14:47:31 +01:00
Irmen de Jong 38a22fbc99 allow %option no_symbol_prefixing also on module scope 2023-12-26 12:31:18 +01:00
Irmen de Jong 8ae435549d added -slabshigh N and -slabsgolden for memory() slabs 2023-12-23 20:45:30 +01:00
Irmen de Jong 9b113c0cbb added -varsgolden to put BSS into Golden Ram at $0400 2023-12-23 20:11:50 +01:00
Irmen de Jong 0e0fac8c4b BSSHIGHRAM_END more clearly defined (to be inclusive) 2023-12-23 19:05:06 +01:00
Irmen de Jong 4cd9bb8f99 allow Python-style negative array indexing to count from the end 2023-12-23 16:37:28 +01:00
Irmen de Jong ad9eaeafeb call now returns a word value 2023-12-22 22:24:11 +01:00
Irmen de Jong 6cd392909c added verafx.copy() routine for fast vram-to-vram copying ('blitting') 2023-12-22 17:52:43 +01:00
Irmen de Jong 49ec430592 cx16: added several word Vera-registers as memory-mapped vars as well 2023-12-21 00:28:09 +01:00
OODLER 09f3fbeb38 doc tweak, adding qualified name example for goto (#116) 2023-12-20 23:51:46 +01:00
OODLER e7698686fa fixed a glitch in the prog8/vtui example (#117)
When the program starts, before the screen is modified, save_rec
is called to capture the cx16 logo.

The fix was to not do the second call to save_rec until after
vtui is used to draw the boxes on the screen.

Before the this fix, the initial replacement issue was of the
logo and not the portion of the screen originally modified by
vtui.
2023-12-20 23:49:47 +01:00
Irmen de Jong 66d939df0d docs about new asm symbol prefixes 2023-12-20 22:37:46 +01:00
Irmen de Jong 6bc079c7b7 more asm symbol prefixing: variables with p8v_, subroutines with p8s_ etc
labels with p8l_ . All this to avoid symbol clashes in the generated assembly code.
Everything got its own distinguishing prefix so we're done with it once and for all and have only 1 breaking change moment.
2023-12-20 22:20:59 +01:00
Irmen de Jong 299419917e added symbol ambiguity error (variable vs block name for scoped symbols)
fixes #114
2023-12-20 00:28:15 +01:00
Irmen de Jong 69f6afe420 block names in asm now prefixed with p8b_ (instead of p8_)
as part of fixing var versus block symbol conflict handling
2023-12-19 23:00:20 +01:00
Irmen de Jong b7279a3d9e fix 'not in' parsing error
fixes #115
2023-12-19 19:49:25 +01:00
Irmen de Jong e14b854d7b cx16: added audio kernal routines example 2023-12-19 01:47:05 +01:00
Irmen de Jong 8bd7c601c0 cx16: added all remaining audio kernal routines. added the three x16edit kernal entry points. 2023-12-18 22:16:44 +01:00
Irmen de Jong 997288fa03 added cbm.CLEARST() to reset ST to 0 2023-12-18 01:20:24 +01:00
Irmen de Jong 0f26b39997 improve diskio error handling and device not found errors
for instance if you set drivenumber to 9 without having a second drive connected, it used to hang in various routines
2023-12-17 22:39:08 +01:00
Irmen de Jong ae66fcac1e added call builtin function for indirect JSR 2023-12-17 15:45:28 +01:00
Irmen de Jong 43944a94eb doc tweaks 2023-12-17 14:47:20 +01:00
Irmen de Jong eba0bde6f3 Merge branch 'optimize-st'
# Conflicts:
#	examples/test.p8
2023-12-17 02:11:01 +01:00
Irmen de Jong 4544af441b doc tweaks, explain str a bit more 2023-12-17 02:02:59 +01:00
Irmen de Jong a8be94de6b better error message when attempting to cast a float to integer 2023-12-15 22:28:06 +01:00
Irmen de Jong b24df31c2b IR: fix codegen for routines returning in CPU Status register flag 2023-12-14 21:16:14 +01:00
Irmen de Jong 332ba8ed7e don't give error when returning uword value in subroutine that returns STR 2023-12-14 02:48:21 +01:00
Irmen de Jong 58400f53bc optimize: flip if true/else blocks if the else block only contains a jump (inverting the condition) 2023-12-13 22:06:53 +01:00
Irmen de Jong 01c2112881 remove PtJump label, just use identifier with dummy 2023-12-13 04:16:49 +01:00
Irmen de Jong a546c2247d optimize if-else handling of asmsub boolean result in status flags 2023-12-13 04:03:21 +01:00
Irmen de Jong 0da9142009 asm: also work for asmsub that return N or Z flag (Carry already worked) 2023-12-13 02:11:15 +01:00
Irmen de Jong 796add0ee2 add string.isspace and string.isprint 2023-12-13 00:28:34 +01:00
Irmen de Jong 00b32f64e6 tweak for,sort,reverse st use 2023-12-12 20:26:45 +01:00
Irmen de Jong f97b3f23e2 optimize symbol table for IR 2023-12-12 19:26:27 +01:00
Irmen de Jong 08a079a96e concerns with in for strings 2023-12-11 21:15:48 +01:00
218 changed files with 8548 additions and 3870 deletions

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
# These are supported funding model platforms
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: irmen
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+21 -24
View File
@@ -1,26 +1,23 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.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.9.0/kotlin-stdlib-jdk7-1.9.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0-sources.jar!/" />
</SOURCES>
</library>
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.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.9.22/kotlin-stdlib-jdk7-1.9.22.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.22/kotlin-stdlib-jdk7-1.9.22-sources.jar!/" />
</SOURCES>
</library>
</component>
+3 -3
View File
@@ -1,9 +1,9 @@
<component name="libraryTable">
<library name="slf4j.simple" type="repository">
<properties maven-id="org.slf4j:slf4j-simple:2.0.9" />
<properties maven-id="org.slf4j:slf4j-simple:2.0.11" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.9/slf4j-simple-2.0.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/2.0.11/slf4j-simple-2.0.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/2.0.11/slf4j-api-2.0.11.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
+8
View File
@@ -1,3 +1,4 @@
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H6S0FFF)
[![Documentation](https://readthedocs.org/projects/prog8/badge/?version=latest)](https://prog8.readthedocs.io/)
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
@@ -9,6 +10,13 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro
as used in many home computers from that era. It is a medium to low level programming language,
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
**Want to buy me a coffee or a pizza perhaps?**
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen).
Documentation
-------------
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
+1 -1
View File
@@ -174,7 +174,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
// }
// VarDeclType.MEMORY -> {
// val numElements =
// if(decl.datatype in ArrayDatatypes)
// if(decl.isArray)
// decl.arraysize!!.constIndex()
// else null
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
+15 -9
View File
@@ -31,7 +31,10 @@ sealed class PtNode(val position: Position) {
}
class PtNodeGroup : PtNode(Position.DUMMY)
sealed interface IPtStatementContainer
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
@@ -64,27 +67,30 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" || it.name=="p8_main" }
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
?.children
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8_start" || it.name=="p8_main.p8_start") } as PtSub?
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?
}
class PtBlock(name: String,
val address: UInt?,
val library: Boolean,
val forceOutput: Boolean,
val noSymbolPrefixing: Boolean,
val veraFxMuls: Boolean,
val alignment: BlockAlignment,
val source: SourceCode, // taken from the module the block is defined in.
val options: Options,
position: Position
) : PtNamedNode(name, position) {
) : PtNamedNode(name, position), IPtStatementContainer {
enum class BlockAlignment {
NONE,
WORD,
PAGE
}
class Options(val address: UInt? = null,
val forceOutput: Boolean = false,
val noSymbolPrefixing: Boolean = false,
val veraFxMuls: Boolean = false,
val ignoreUnused: Boolean = false,
val alignment: BlockAlignment = BlockAlignment.NONE)
}
+26 -11
View File
@@ -3,7 +3,7 @@ package prog8.code.ast
import prog8.code.core.*
import java.util.*
import kotlin.math.abs
import kotlin.math.round
import kotlin.math.truncate
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
@@ -36,7 +36,14 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
return arrayIndexExpr!! isSameAs other.arrayIndexExpr!!
}
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtBinaryExpression -> {
if(other !is PtBinaryExpression || other.operator!=operator)
false
else if(operator in AssociativeOperators)
(other.left isSameAs left && other.right isSameAs right) || (other.left isSameAs right && other.right isSameAs left)
else
other.left isSameAs left && other.right isSameAs right
}
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
@@ -72,7 +79,12 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtArray -> true
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
is PtBinaryExpression -> false
is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
is PtBuiltinFunctionCall -> {
when (name) {
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
else -> false
}
}
is PtContainmentCheck -> false
is PtFunctionCall -> false
is PtIdentifier -> true
@@ -126,16 +138,16 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
val variable: PtIdentifier
get() = children[0] as PtIdentifier
get() {
require((children[0] as? PtIdentifier)?.type in ArrayDatatypes+DataType.STR) // TODO remove
return children[0] as PtIdentifier
}
val index: PtExpression
get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
val usesPointerVariable: Boolean
get() = variable.type==DataType.UWORD
init {
require(elementType in NumericDatatypes)
}
@@ -227,9 +239,9 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type!=DataType.FLOAT) {
val rounded = round(number)
if (rounded != number)
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
val trunc = truncate(number)
if (trunc != number)
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
}
}
@@ -238,7 +250,10 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtNumber)
return false
return number==other.number
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
return number==other.number
else
return type==other.type && number==other.number
}
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
+16 -7
View File
@@ -53,15 +53,24 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"goto ${node.identifier.name}"
else if(node.address!=null)
"goto ${node.address.toHex()}"
else if(node.generatedLabel!=null)
"goto ${node.generatedLabel}"
else
"???"
}
is PtAsmSub -> {
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
val params = node.parameters.map {
val register = it.first.registerOrPair
val statusflag = it.first.statusflag
"${it.second.type} ${it.second.name} @${register ?: statusflag}"
}.joinToString(", ")
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}")
val returns = if (node.returns.isEmpty()) "" else {
"-> ${node.returns.map {
val register = it.first.registerOrPair
val statusflag = it.first.statusflag
"${it.second} @${register ?: statusflag}"}
.joinToString(", ")
}"
}
val str = if (node.inline) "inline " else ""
if(node.address==null) {
str + "asmsub ${node.name}($params) $clobbers $returns"
@@ -70,8 +79,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
}
}
is PtBlock -> {
val addr = if(node.address==null) "" else "@${node.address.toHex()}"
val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}"
val addr = if(node.options.address==null) "" else "@${node.options.address.toHex()}"
val align = if(node.options.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.options.alignment}"
"\nblock '${node.name}' $addr $align"
}
is PtConstant -> {
@@ -89,7 +98,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
}
}
is PtSub -> {
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
val params = node.parameters.map { "${it.type} ${it.name}" }.joinToString(", ")
var str = "sub ${node.name}($params) "
if(node.returntype!=null)
str += "-> ${node.returntype.name.lowercase()}"
+2 -3
View File
@@ -23,7 +23,7 @@ class PtSub(
val parameters: List<PtSubroutineParameter>,
val returntype: DataType?,
position: Position
) : PtNamedNode(name, position), IPtSubroutine {
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes })
@@ -101,9 +101,8 @@ class PtIfElse(position: Position) : PtNode(position) {
}
class PtJump(val identifier: PtIdentifier?,
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
val address: UInt?,
val generatedLabel: String?,
position: Position) : PtNode(position) {
init {
identifier?.let {it.parent = this }
@@ -125,14 +125,11 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
"rsave" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
)
val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse")
@@ -21,8 +21,11 @@ class CompilationOptions(val output: OutputType,
var includeSourcelines: Boolean = false,
var experimentalCodegen: Boolean = false,
var varsHighBank: Int? = null,
var varsGolden: Boolean = false,
var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false,
var breakpointCpuInstruction: Boolean = false,
var breakpointCpuInstruction: String? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()
) {
+3 -2
View File
@@ -120,7 +120,7 @@ enum class BranchCondition {
PL, // PL == POS
POS,
VS,
VC,
VC
}
@@ -160,7 +160,8 @@ val ElementToArrayTypes = mapOf(
DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL
DataType.BOOL to DataType.ARRAY_BOOL,
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
)
val Cx16VirtualRegisters = arrayOf(
@@ -3,11 +3,12 @@ package prog8.code.core
interface IErrorReporter {
fun err(msg: String, position: Position)
fun warn(msg: String, position: Position)
fun info(msg: String, position: Position)
fun undefined(symbol: List<String>, position: Position)
fun noErrors(): Boolean
fun report()
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
if(numErrors>0)
throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.")
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
}
}
@@ -16,6 +16,8 @@ interface IMachineDefinition {
val PROGRAM_LOAD_ADDRESS : UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
val BSSGOLDENRAM_START: UInt
val BSSGOLDENRAM_END: UInt
val cpu: CpuType
var zeropage: Zeropage
+1 -3
View File
@@ -1,12 +1,10 @@
package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor")
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not")
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
fun invertedComparisonOperator(operator: String) =
when (operator) {
+6 -3
View File
@@ -69,7 +69,8 @@ sealed class SourceCode {
* Turn a plain String into a [SourceCode] object.
* [origin] will be something like `string:44c56085`.
*/
class Text(override val text: String): SourceCode() {
class Text(origText: String): SourceCode() {
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
override val isFromResources = false
override val isFromFilesystem = false
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
@@ -95,7 +96,8 @@ sealed class SourceCode {
val normalized = path.normalize()
origin = relative(normalized).toString()
try {
text = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
name = normalized.toFile().nameWithoutExtension
} catch (nfx: java.nio.file.NoSuchFileException) {
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
@@ -127,7 +129,8 @@ sealed class SourceCode {
)
}
val stream = object {}.javaClass.getResourceAsStream(normalized)
text = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
name = Path(pathString).toFile().nameWithoutExtension
}
}
@@ -0,0 +1,130 @@
package prog8.code.optimize
import prog8.code.ast.*
import prog8.code.core.*
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, errors: IErrorReporter) {
if (!options.optimize)
return
while(errors.noErrors() && optimizeCommonSubExpressions(program, errors)>0) {
// keep rolling
}
}
private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
fun recurse(node: PtNode, depth: Int) {
if(act(node, depth))
node.children.forEach { recurse(it, depth+1) }
}
recurse(root, 0)
}
private var tempVarCounter = 0
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
fun extractableSubExpr(expr: PtExpression): Boolean {
val result = if(expr is PtBinaryExpression)
expr.type !in ByteDatatypes ||
!expr.left.isSimple() ||
!expr.right.isSimple() ||
(expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes)
true
else
!expr.isSimple()
return result
}
// for each Binaryexpression, recurse to find a common subexpression pair therein.
val commons = mutableMapOf<PtBinaryExpression, Pair<PtExpression, PtExpression>>()
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtBinaryExpression) {
val subExpressions = mutableListOf<PtExpression>()
walkAst(node.left) { subNode: PtNode, subDepth: Int ->
if (subNode is PtExpression) {
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
true
} else false
}
walkAst(node.right) { subNode: PtNode, subDepth: Int ->
if (subNode is PtExpression) {
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
true
} else false
}
outer@for (first in subExpressions) {
for (second in subExpressions) {
if (first!==second && first isSameAs second) {
commons[node] = first to second
break@outer // do only 1 replacement at a time per binaryexpression
}
}
}
false
} else true
}
// replace common subexpressions by a temp variable that is assigned only once.
// TODO: check for commonalities across multiple separate expressions in the current scope, not only inside a single line
commons.forEach { binexpr, (occurrence1, occurrence2) ->
val (stmtContainer, stmt) = findContainingStatements(binexpr)
val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1)
val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2)
val containerScopedName = findScopeName(stmtContainer)
tempVarCounter++
val tempvarName = "prog8_subexprvar_$tempVarCounter"
// TODO: some tempvars could be reused, if they are from different lines
val datatype = occurrence1.type
val singleReplacement1 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence1.position)
val singleReplacement2 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence2.position)
occurrence1.parent.children[occurrence1idx] = singleReplacement1
singleReplacement1.parent = occurrence1.parent
occurrence2.parent.children[occurrence2idx] = singleReplacement2
singleReplacement2.parent = occurrence2.parent
val tempassign = PtAssignment(binexpr.position).also { assign ->
assign.add(PtAssignTarget(binexpr.position).also { tgt->
tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
})
assign.add(occurrence1)
occurrence1.parent = assign
}
stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign)
tempassign.parent = stmtContainer
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.NOT_IN_ZEROPAGE, null, null, binexpr.position)
stmtContainer.add(0, tempvar)
tempvar.parent = stmtContainer
// errors.info("common subexpressions replaced by a tempvar, maybe simplify the expression manually", binexpr.position)
}
return commons.size
}
internal fun findScopeName(node: PtNode): String {
var parent=node
while(parent !is PtNamedNode)
parent = parent.parent
return parent.scopedName
}
internal fun findContainingStatements(node: PtNode): Pair<PtNode, PtNode> { // returns (parentstatementcontainer, childstatement)
var parent = node.parent
var child = node
while(true) {
if(parent is IPtStatementContainer) {
return parent to child
}
child=parent
parent=parent.parent
}
}
@@ -18,7 +18,7 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
else -> throw IllegalArgumentException("invalid datatype")
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
else -> throw IllegalArgumentException("invalid datatype")
}
}
@@ -15,6 +15,8 @@ class AtariMachineDefinition: IMachineDefinition {
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
@@ -17,6 +17,8 @@ class C128MachineDefinition: IMachineDefinition {
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
@@ -17,7 +17,9 @@ class C64MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val BSSHIGHRAM_START = 0xc000u
override val BSSHIGHRAM_END = 0xd000u
override val BSSHIGHRAM_END = 0xcfffu
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
@@ -9,7 +9,7 @@ internal object CbmMemorySizer: IMemSizer {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
else -> throw IllegalArgumentException("invalid datatype")
}
}
@@ -16,7 +16,9 @@ class CX16MachineDefinition: IMachineDefinition {
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
override val BSSGOLDENRAM_START = 0x0400u
override val BSSGOLDENRAM_END = 0x07ffu
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
@@ -36,7 +38,7 @@ class CX16MachineDefinition: IMachineDefinition {
when(selectedEmulator) {
1 -> {
emulator = "x16emu"
extraArgs = emptyList()
extraArgs = listOf("-debug")
}
2 -> {
emulator = "box16"
@@ -49,7 +51,7 @@ class CX16MachineDefinition: IMachineDefinition {
}
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline).inheritIO()
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
val process: Process = processb.start()
@@ -15,8 +15,10 @@ class PETMachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0401u
override val BSSHIGHRAM_START = 0xffffu
override val BSSHIGHRAM_END = 0xffffu
override val BSSHIGHRAM_START = 0u
override val BSSHIGHRAM_END = 0u
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
@@ -17,6 +17,8 @@ class VirtualMachineDefinition: IMachineDefinition {
override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used
override val BSSGOLDENRAM_START = 0u // not actually used
override val BSSGOLDENRAM_END = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used
override lateinit var golden: GoldenRam // not actually used
@@ -1,11 +1,13 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import prog8.code.StNode
import prog8.code.StNodeType
import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.assignment.*
import java.util.*
import kotlin.io.path.Path
@@ -32,7 +34,14 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
fun prefixNamedNode(node: PtNamedNode) {
node.name = "p8_${node.name}"
when(node) {
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
is PtBlock -> node.name = "p8b_${node.name}"
is PtLabel -> node.name = "p8l_${node.name}"
is PtConstant -> node.name = "p8c_${node.name}"
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
else -> node.name = "p8_${node.name}"
}
}
fun prefixSymbols(node: PtNode) {
@@ -47,7 +56,7 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
}
is PtFunctionCall -> {
val stNode = st.lookup(node.name)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
functionCallsToPrefix += node.parent to index
}
@@ -58,25 +67,16 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
lookupName = lookupName.dropLast(4)
}
val stNode = st.lookup(lookupName)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
is PtJump -> {
if(node.identifier!=null) {
val stNode = st.lookup(node.identifier!!.name)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
else if(node.generatedLabel!=null) {
val stNode = st.lookup(node.generatedLabel!!)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
val stNode = st.lookup(node.identifier!!.name) ?: throw AssemblyError("name not found ${node.identifier}")
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
is PtBlock -> prefixNamedNode(node)
@@ -94,7 +94,7 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
}
program.allBlocks().forEach { block ->
if (!block.noSymbolPrefixing) {
if (!block.options.noSymbolPrefixing) {
prefixSymbols(block)
}
}
@@ -124,8 +124,20 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
}
}
private fun prefixScopedName(name: String, type: Char): String {
if('.' !in name)
return "p8${type}_$name"
val parts = name.split('.')
val firstPrefixed = "p8b_${parts[0]}"
val lastPrefixed = "p8${type}_${parts.last()}"
// the parts in between are assumed to be subroutine scopes.
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
return prefixed.joinToString(".")
}
private fun PtVariable.prefix(st: SymbolTable): PtVariable {
name = name.split('.').map {"p8_$it" }.joinToString(".")
name = prefixScopedName(name, 'v')
if(value==null)
return this
@@ -137,7 +149,7 @@ private fun PtVariable.prefix(st: SymbolTable): PtVariable {
is PtIdentifier -> newValue.add(elt.prefix(arrayValue, st))
is PtNumber -> newValue.add(elt)
is PtAddressOf -> {
if(elt.definingBlock()?.noSymbolPrefixing==true)
if(elt.definingBlock()?.options?.noSymbolPrefixing==true)
newValue.add(elt)
else {
val newAddr = PtAddressOf(elt.position)
@@ -155,19 +167,14 @@ private fun PtVariable.prefix(st: SymbolTable): PtVariable {
}
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
val jump = if(identifier!=null) {
val prefixedIdent = identifier!!.prefix(this, st)
PtJump(prefixedIdent, address, generatedLabel, position)
} else {
val prefixedLabel = generatedLabel!!.split('.').map {"p8_$it" }.joinToString(".")
PtJump(null, address, prefixedLabel, position)
}
val prefixedIdent = identifier!!.prefix(this, st)
val jump = PtJump(prefixedIdent, address, position)
jump.parent = parent
return jump
}
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
val newName = name.split('.').map {"p8_$it" }.joinToString(".")
val newName = prefixScopedName(name, 's')
val call = PtFunctionCall(newName, void, type, position)
call.children.addAll(children)
call.children.forEach { it.parent = call }
@@ -176,11 +183,29 @@ private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
}
private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
val target = st.lookup(name)
if(target?.astNode?.definingBlock()?.noSymbolPrefixing==true)
var target = st.lookup(name)
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
return this
val newName = name.split('.').map { "p8_$it" }.joinToString(".")
if(target==null) {
if(name.endsWith("_lsb") || name.endsWith("_msb")) {
target = st.lookup(name.dropLast(4))
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
return this
}
}
val prefixType = when(target!!.type) {
StNodeType.BLOCK -> 'b'
StNodeType.SUBROUTINE, StNodeType.ROMSUB -> 's'
StNodeType.LABEL -> 'l'
StNodeType.STATICVAR, StNodeType.MEMVAR -> 'v'
StNodeType.CONSTANT -> 'c'
StNodeType.BUILTINFUNC -> 's'
StNodeType.MEMORYSLAB -> 'v'
else -> '?'
}
val newName = prefixScopedName(name, prefixType)
val node = PtIdentifier(newName, type, position)
node.parent = parent
return node
@@ -199,7 +224,7 @@ class AsmGen6502Internal (
internal val loopEndLabels = ArrayDeque<String>()
private val zeropage = options.compTarget.machine.zeropage
private val allocator = VariableAllocator(symbolTable, options, errors)
private val assemblyLines = mutableListOf<String>()
private val assembly = mutableListOf<String>()
private val breakpointLabels = mutableListOf<String>()
private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
@@ -211,7 +236,7 @@ class AsmGen6502Internal (
fun compileToAssembly(): IAssemblyProgram? {
assemblyLines.clear()
assembly.clear()
loopEndLabels.clear()
println("Generating assembly code... ")
@@ -219,15 +244,22 @@ class AsmGen6502Internal (
if(errors.noErrors()) {
val output = options.outputDir.resolve("${program.name}.asm")
val asmLines = assembly.asSequence().flatMapTo(mutableListOf()) { it.split('\n') }
if(options.compTarget.name==Cx16Target.NAME) {
scanInvalid65816instructions(asmLines)
if(!errors.noErrors()) {
errors.report()
return null
}
}
if(options.optimize) {
val separateLines = assemblyLines.flatMapTo(mutableListOf()) { it.split('\n') }
assemblyLines.clear()
while(optimizeAssembly(separateLines, options.compTarget.machine, symbolTable)>0) {
while(optimizeAssembly(asmLines, options.compTarget.machine, symbolTable)>0) {
// optimize the assembly source code
}
output.writeLines(separateLines)
output.writeLines(asmLines)
} else {
output.writeLines(assemblyLines)
// write the unmodified code
output.writeLines(assembly)
}
return AssemblyProgram(program.name, options.outputDir, options.compTarget)
} else {
@@ -236,6 +268,22 @@ class AsmGen6502Internal (
}
}
private fun scanInvalid65816instructions(asmLines: MutableList<String>) {
// The CommanderX16 ships with a WDC 65C02 CPU or a WDC 65816 CPU
// The latter is compatible with the 65C02 except for 4 instructions: RMB, SMB, BBS, BBR.
// We cannot set a different 6502 CPU target for the 64tass assembler, because we still need to support the STP and WAI instructions...
// so we have to scan for these instructions ourselves.
val invalid = Regex("""\s*((rmb\s|smb\s|bbs\s|bbr\s)|(rmb[0-7]|smb[0-7]|bbs[0-7]|bbr[0-7]))""", RegexOption.IGNORE_CASE)
for((index, line) in asmLines.withIndex()) {
if(line.length>=4 && invalid.matchesAt(line, 0)) {
errors.err(
"invalid assembly instruction used (not compatible with the 65816 CPU): ${line.trim()}",
Position("<output-assemblycode>", index, 1, 1)
)
}
}
}
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
private var lastSourceLineNumber: Int = -1
@@ -259,9 +307,9 @@ class AsmGen6502Internal (
for (line in fragment.splitToSequence('\n')) {
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
assemblyLines.add(trimmed)
assembly.add(trimmed)
}
} else assemblyLines.add(fragment)
} else assembly.add(fragment)
}
fun asmSymbolName(regs: RegisterOrPair): String =
@@ -296,15 +344,32 @@ class AsmGen6502Internal (
name
}
fun asmVariableName(st: StNode, scope: PtSub?): String {
val name = asmVariableName(st.scopedName)
if(scope==null)
return name
// remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block)
val subName = scope.scopedName
return if (name.length>subName.length && name.startsWith(subName) && name[subName.length] == '.')
name.drop(subName.length+1)
else
name
}
internal val tempVarsCounters = mutableMapOf(
DataType.BYTE to 0,
DataType.UBYTE to 0,
DataType.WORD to 0,
DataType.UWORD to 0,
DataType.FLOAT to 0
)
internal fun buildTempVarName(dt: DataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
internal fun getTempVarName(dt: DataType): String {
return when(dt) {
DataType.UBYTE -> "cx16.r9L"
DataType.BYTE -> "cx16.r9sL"
DataType.UWORD -> "cx16.r9"
DataType.WORD -> "cx16.r9s"
DataType.FLOAT -> "floats.floats_temp_var"
else -> throw AssemblyError("invalid dt $dt")
}
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
}
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
@@ -487,18 +552,14 @@ class AsmGen6502Internal (
}
}
internal fun loadScaledArrayIndexIntoRegister(
expr: PtArrayIndexer,
elementDt: DataType,
register: CpuRegister
) {
internal fun loadScaledArrayIndexIntoRegister(expr: PtArrayIndexer, register: CpuRegister) {
val reg = register.toString().lowercase()
val indexnum = expr.index.asConstInteger()
if (indexnum != null) {
val indexValue = if(expr.splitWords)
indexnum
else
indexnum * options.compTarget.memorySize(elementDt)
indexnum * options.compTarget.memorySize(expr.type)
out(" ld$reg #$indexValue")
return
}
@@ -508,7 +569,7 @@ class AsmGen6502Internal (
return
}
when (elementDt) {
when (expr.type) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
}
@@ -684,7 +745,6 @@ class AsmGen6502Internal (
if (jump is PtJump) {
// jump somewhere if X!=0
val label = when {
jump.generatedLabel!=null -> jump.generatedLabel!!
jump.identifier!=null -> asmSymbolName(jump.identifier!!)
jump.address!=null -> jump.address!!.toHex()
else -> throw AssemblyError("weird jump")
@@ -984,7 +1044,6 @@ $repeatLabel""")
private fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
val ident = jump.identifier
val label = jump.generatedLabel
val addr = jump.address
return when {
ident!=null -> {
@@ -995,7 +1054,6 @@ $repeatLabel""")
else
Pair(asmSymbolName(ident), false)
}
label!=null -> Pair(label, false)
addr!=null -> Pair(addr.toHex(), false)
else -> Pair("????", false)
}
@@ -1027,7 +1085,7 @@ $repeatLabel""")
if(asm.isIR)
throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly")
else
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
assembly.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
}
private fun translate(incbin: PtIncludeBinary) {
@@ -1049,14 +1107,8 @@ $repeatLabel""")
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
breakpointLabels.add(label)
out(label)
if(options.breakpointCpuInstruction) {
val instruction =
when(options.compTarget.machine.cpu) {
CpuType.CPU6502 -> "brk"
CpuType.CPU65c02 -> "stp"
else -> throw AssemblyError("invalid cpu type")
}
out(" $instruction")
if(options.breakpointCpuInstruction!=null) {
out(" ${options.breakpointCpuInstruction}")
}
}
@@ -1140,7 +1192,7 @@ $repeatLabel""")
return null
}
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtExpression, operator: String, write: Boolean): Boolean {
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtBinaryExpression, write: Boolean): Boolean {
// optimize pointer,indexregister if possible
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
@@ -1156,7 +1208,7 @@ $repeatLabel""")
}
}
if(operator=="+") {
if(addressExpr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? PtIdentifier
@@ -1245,7 +1297,6 @@ $repeatLabel""")
val rightConstVal = right as? PtNumber
val label = when {
jump.generatedLabel!=null -> jump.generatedLabel!!
jump.identifier!=null -> asmSymbolName(jump.identifier!!)
jump.address!=null -> jump.address!!.toHex()
else -> throw AssemblyError("weird jump")
@@ -2868,7 +2919,7 @@ $repeatLabel""")
}
is PtBinaryExpression -> {
val addrExpr = expr.address as PtBinaryExpression
if(!tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(!tryOptimizedPointerAccessWithA(addrExpr, false)) {
assignViaExprEval()
}
}
@@ -3029,30 +3080,16 @@ $repeatLabel""")
}
}
internal fun popCpuStack(dt: DataType, target: IPtVariable, scope: IPtSubroutine?) {
// note: because A is pushed first so popped last, saving A is often not required here.
val targetAsmSub = (target as PtNode).definingAsmSub()
if(targetAsmSub != null) {
val parameter = targetAsmSub.parameters.first { it.second.name==target.name }
popCpuStack(targetAsmSub, parameter.second, parameter.first)
return
}
val scopedName = when(target) {
is PtConstant -> target.scopedName
is PtMemMapped -> target.scopedName
is PtVariable -> target.scopedName
else -> throw AssemblyError("weird target var")
}
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, this, target.type, scope, target.position, variableAsmName = asmVariableName(scopedName))
internal fun popCpuStack(dt: DataType) {
if (dt in ByteDatatypes) {
out(" pla")
assignRegister(RegisterOrPair.A, tgt)
} else {
} else if (dt in WordDatatypes) {
if (isTargetCpu(CpuType.CPU65c02))
out(" ply | pla")
else
out(" pla | tay | pla")
assignRegister(RegisterOrPair.AY, tgt)
} else {
throw AssemblyError("can't pop type $dt")
}
}
@@ -3104,7 +3141,7 @@ $repeatLabel""")
internal fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++
return "prog8_label_asm_${generatedLabelSequenceNumber}_$postfix"
return "label_asm_${generatedLabelSequenceNumber}_$postfix"
}
fun assignConstFloatToPointerAY(number: PtNumber) {
@@ -381,15 +381,9 @@ private fun optimizeStoreLoadSame(
} else if(first=="phx" && second=="pla") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " txa"))
} else if(first=="phx" && second=="ply") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " txy"))
} else if(first=="phy" && second=="pla") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tya"))
} else if(first=="phy" && second=="plx") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tyx"))
}
}
return mods
@@ -1,5 +1,6 @@
package prog8.codegen.cpu6502
import prog8.code.StMemVar
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.*
@@ -62,28 +63,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" pla")
}
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
"pop" -> {
require(fcall.args[0] is PtIdentifier) {
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
}
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub())
}
"popw" -> {
require(fcall.args[0] is PtIdentifier) {
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
}
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
}
"rsave" -> funcRsave()
"rrestore" -> funcRrestore()
"cmp" -> funcCmp(fcall)
"callfar" -> funcCallFar(fcall, resultRegister)
"call" -> funcCall(fcall)
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
@@ -193,6 +177,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
plp""")
}
private fun funcCall(fcall: PtBuiltinFunctionCall) {
val constAddr = fcall.args[0].asConstInteger()
if(constAddr!=null) {
asmgen.out(" jsr ${constAddr.toHex()}")
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) // jump address
asmgen.out("""
sta (+)+1
sty (+)+2
+ jsr 0 ; modified""")
}
// note: the routine can return a word value (in AY)
}
private fun funcCallFar(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callfar only works on cx16 target at this time")
@@ -311,106 +310,112 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcReverse(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single()
if (variable is PtIdentifier) {
val symbol = asmgen.symbolTable.lookup(variable.name)
val decl = symbol!!.astNode as IPtVariable
val numElements = when(decl) {
is PtConstant -> throw AssemblyError("cannot reverse a constant")
is PtMemMapped -> decl.arraySize
is PtVariable -> decl.arraySize
val variable = fcall.args.single() as PtIdentifier
val symbol = asmgen.symbolTable.lookup(variable.name)
val (dt, numElements) = when(symbol) {
is StStaticVariable -> symbol.dt to symbol.length!!
is StMemVar -> symbol.dt to symbol.length!!
else -> DataType.UNDEFINED to 0
}
val varName = asmgen.asmVariableName(variable)
when (dt) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b""")
}
val varName = asmgen.asmVariableName(variable)
when (decl.type) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b""")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_w""")
}
DataType.STR -> {
val stringLength = (symbol as StStaticVariable).length!!-1
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$stringLength
jsr prog8_lib.func_reverse_b""")
}
DataType.ARRAY_F -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr floats.func_reverse_f""")
}
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw AssemblyError("weird type")
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_w""")
}
DataType.STR -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #${numElements-1}
jsr prog8_lib.func_reverse_b""")
}
DataType.ARRAY_F -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr floats.func_reverse_f""")
}
in SplitWordArrayTypes -> {
// reverse the lsb and msb arrays both, independently
asmgen.out("""
lda #<${varName}_lsb
ldy #>${varName}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b
lda #<${varName}_msb
ldy #>${varName}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b""")
}
else -> throw AssemblyError("weird type")
}
}
private fun funcSort(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single()
if (variable is PtIdentifier) {
val symbol = asmgen.symbolTable.lookup(variable.name)
val decl = symbol!!.astNode as IPtVariable
val varName = asmgen.asmVariableName(variable)
val numElements = when(decl) {
is PtConstant -> throw AssemblyError("cannot sort a constant")
is PtMemMapped -> decl.arraySize
is PtVariable -> decl.arraySize
val variable = fcall.args.single() as PtIdentifier
val symbol = asmgen.symbolTable.lookup(variable.name)
val varName = asmgen.asmVariableName(variable)
val (dt, numElements) = when(symbol) {
is StStaticVariable -> symbol.dt to symbol.length!!
is StMemVar -> symbol.dt to symbol.length!!
else -> DataType.UNDEFINED to 0
}
when (dt) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (dt == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
}
when (decl.type) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (decl.type == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
}
DataType.STR -> {
val stringLength = (symbol as StStaticVariable).length!!-1
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$stringLength
jsr prog8_lib.func_sort_ub""")
}
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw AssemblyError("weird type")
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (dt == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
}
} else
throw AssemblyError("weird type")
DataType.STR -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #${numElements-1}
jsr prog8_lib.func_sort_ub""")
}
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
in SplitWordArrayTypes -> TODO("split words sort")
else -> throw AssemblyError("weird type")
}
}
private fun funcRor2(fcall: PtBuiltinFunctionCall) {
@@ -419,8 +424,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
translateRolRorArrayArgs(what.variable, what, "ror2", 'b')
asmgen.out(" jsr prog8_lib.ror2_array_ub")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #\$80 |+ | sta ${varname},x")
}
is PtMemoryByte -> {
if (what.address is PtNumber) {
@@ -441,10 +447,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
if(what.splitWords)
TODO("ror2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw")
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #\$80 | sta ${varname}_msb,x |+ ")
else
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #\$80 | sta ${varname}+1,x |+ ")
}
is PtIdentifier -> {
val variable = asmgen.asmVariableName(what)
@@ -463,8 +471,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
translateRolRorArrayArgs(what.variable, what, "ror", 'b')
asmgen.out(" jsr prog8_lib.ror_array_ub")
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
asmgen.out(" ror ${varname},x")
}
is PtMemoryByte -> {
if (what.address is PtNumber) {
@@ -473,15 +484,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) {
asmgen.out(" php")
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.restoreRegisterStack(CpuRegister.X, false)
asmgen.out("""
plp
+ ror ${'$'}ffff,x ; modified""")
} else {
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
if(!what.address.isSimple()) asmgen.out(" plp")
asmgen.out("""
sta (+) + 1
sty (+) + 2
@@ -499,10 +514,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
if(what.splitWords)
TODO("ror split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw")
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
else
asmgen.out(" ror ${varname}+1,x | ror ${varname},x")
}
is PtIdentifier -> {
val variable = asmgen.asmVariableName(what)
@@ -521,8 +540,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
translateRolRorArrayArgs(what.variable, what, "rol2", 'b')
asmgen.out(" jsr prog8_lib.rol2_array_ub")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
asmgen.out(" lda ${varname},x | cmp #\$80 | rol a | sta ${varname},x")
}
is PtMemoryByte -> {
if (what.address is PtNumber) {
@@ -543,10 +563,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
if(what.splitWords)
TODO("rol2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw")
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb |+")
else
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
}
is PtIdentifier -> {
val variable = asmgen.asmVariableName(what)
@@ -565,8 +587,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
translateRolRorArrayArgs(what.variable, what, "rol", 'b')
asmgen.out(" jsr prog8_lib.rol_array_ub")
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
asmgen.out(" rol ${varname},x")
}
is PtMemoryByte -> {
if (what.address is PtNumber) {
@@ -575,15 +600,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) {
asmgen.out(" php")
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.restoreRegisterStack(CpuRegister.X, false)
asmgen.out("""
plp
+ rol ${'$'}ffff,x ; modified""")
} else {
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
if(!what.address.isSimple()) asmgen.out(" plp")
asmgen.out("""
sta (+) + 1
sty (+) + 2
@@ -601,10 +630,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
if(what.splitWords)
TODO("rol split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw")
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
else
asmgen.out(" rol ${varname},x | rol ${varname}+1,x")
}
is PtIdentifier -> {
val variable = asmgen.asmVariableName(what)
@@ -617,23 +650,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
if(indexer.splitWords)
TODO("rol/ror split words access ${indexer.position}")
if(arrayvar.type==DataType.UWORD) {
if(dt!='b')
throw AssemblyError("non-array var indexing requires bytes dt")
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
} else {
val p = arrayvar.parent
val addressOf = PtAddressOf(arrayvar.position)
addressOf.add(arrayvar)
addressOf.parent = p
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
}
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
}
private fun funcSetLsbMsb(fcall: PtBuiltinFunctionCall, msb: Boolean) {
val target: AsmAssignTarget
when(fcall.args[0]) {
@@ -661,7 +677,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
is PtArrayIndexer -> {
val indexer = fcall.args[0] as PtArrayIndexer
require(!indexer.usesPointerVariable)
val elementSize: Int
val msbAdd: Int
if(indexer.splitWords) {
@@ -728,13 +743,57 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().type
val array = fcall.args[0] as PtIdentifier
when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
in SplitWordArrayTypes -> TODO("split word any/all")
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A")
}
DataType.ARRAY_F -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr floats.func_${fcall.name}_f_into_A")
}
in SplitWordArrayTypes -> {
val numElements = (asmgen.symbolTable.lookup(array.name) as StStaticVariable).length
when(fcall.name) {
"any" -> {
// any(lsb-array) or any(msb-array)
val arrayName = asmgen.asmVariableName(array)
asmgen.out("""
lda #<${arrayName}_lsb
ldy #>${arrayName}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
""")
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
asmgen.out(" bne +") // shortcircuit
asmgen.out("""
pha
lda #<${arrayName}_msb
ldy #>${arrayName}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
""")
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
asmgen.out("""
sta P8ZP_SCRATCH_REG
pla
ora P8ZP_SCRATCH_REG
+""")
}
"all" -> {
TODO("split words all")
}
else -> throw AssemblyError("weird call")
}
}
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes, true)
@@ -786,8 +845,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
else -> {
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
asmgen.assignExpressionTo(fcall.args[1],
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, tempvar, null, null, null, null))
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out("""
pha
@@ -1187,15 +1245,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" lda $arrayVar,y")
}
RegisterOrPair.Y -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
asmgen.out(" lda $arrayVar,x")
}
RegisterOrPair.X -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, arg.type, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" ldx $arrayVar,y")
}
else -> throw AssemblyError("invalid reg")
@@ -1241,16 +1299,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun outputAddressAndLenghtOfArray(arg: PtExpression) {
// address in P8ZP_SCRATCH_W1, number of elements in A
arg as PtIdentifier
private fun outputAddressAndLengthOfArray(arg: PtIdentifier) {
// address goes in P8ZP_SCRATCH_W1, number of elements in A
val symbol = asmgen.symbolTable.lookup(arg.name)
val arrayVar = symbol!!.astNode as IPtVariable
val numElements = when(arrayVar) {
is PtConstant -> null
is PtMemMapped -> arrayVar.arraySize
is PtVariable -> arrayVar.arraySize
} ?: throw AssemblyError("length of non-array requested")
val numElements = when(symbol) {
is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!!
else -> 0
}
val identifierName = asmgen.asmVariableName(arg)
asmgen.out("""
lda #<$identifierName
@@ -1,7 +1,11 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import prog8.code.ast.*
import prog8.code.StMemVar
import prog8.code.StStaticVariable
import prog8.code.ast.PtForLoop
import prog8.code.ast.PtIdentifier
import prog8.code.ast.PtRange
import prog8.code.core.*
import kotlin.math.absoluteValue
@@ -332,11 +336,10 @@ $endLabel""")
asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident)
val symbol = asmgen.symbolTable.lookup(ident.name)
val decl = symbol!!.astNode as IPtVariable
val numElements = when(decl) {
is PtConstant -> throw AssemblyError("length of non-array requested")
is PtMemMapped -> decl.arraySize
is PtVariable -> decl.arraySize
val numElements = when(symbol) {
is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!!
else -> 0
}
when(iterableDt) {
DataType.STR -> {
@@ -364,7 +367,7 @@ $loopLabel sty $indexVar
lda $iterableName,y
sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements)
if(numElements!!<=255u) {
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
@@ -379,7 +382,7 @@ $loopLabel sty $indexVar
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16u) {
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
@@ -392,7 +395,7 @@ $loopLabel sty $indexVar
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
val length = numElements!! * 2u
val length = numElements * 2
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
@@ -403,7 +406,7 @@ $loopLabel sty $indexVar
lda $iterableName+1,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(length<=127u) {
if(length<=127) {
asmgen.out("""
ldy $indexVar
iny
@@ -420,7 +423,7 @@ $loopLabel sty $indexVar
bne $loopLabel
beq $endLabel""")
}
if(length>=16u) {
if(length>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
@@ -433,7 +436,6 @@ $loopLabel sty $indexVar
asmgen.out(endLabel)
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
numElements!!
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
@@ -444,7 +446,7 @@ $loopLabel sty $indexVar
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255u) {
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
@@ -459,7 +461,7 @@ $loopLabel sty $indexVar
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16u) {
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
@@ -26,7 +26,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol!!.astNode as IPtSubroutine
val sub = symbol?.astNode as IPtSubroutine
val subAsmName = asmgen.asmSymbolName(call.name)
if(sub is PtAsmSub) {
@@ -1,9 +1,6 @@
package prog8.codegen.cpu6502
import prog8.code.ast.PtIdentifier
import prog8.code.ast.PtNumber
import prog8.code.ast.PtPostIncrDecr
import prog8.code.ast.PtProgram
import prog8.code.ast.*
import prog8.code.core.*
@@ -37,6 +34,55 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
}
}
targetMemory!=null -> {
fun incDecViaExprEval() {
asmgen.assignExpressionToRegister(targetMemory.address, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
fun tryOptimizedPointerIncDec(address: PtBinaryExpression): Boolean {
if(address.operator=="+") {
val offset = address.right.asConstInteger()
if(offset!=null && offset<256) {
// we have @(ptr + 255) ++ , or @(ptr+255)--
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.out("""
sta (+) + 1
sty (+) + 2
ldx #$offset""")
if(incr)
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
return true
} else if(address.right.type in ByteDatatypes) {
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.X, false)
if(incr)
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
return true
} else if((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.X, false)
if(incr)
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
return true
}
}
return false
}
when (val addressExpr = targetMemory.address) {
is PtNumber -> {
val what = addressExpr.number.toHex()
@@ -50,13 +96,13 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
is PtBinaryExpression -> {
if(!tryOptimizedPointerIncDec(addressExpr)) {
incDecViaExprEval()
}
}
else -> {
asmgen.assignExpressionToRegister(addressExpr, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
incDecViaExprEval()
}
}
}
@@ -75,7 +121,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
dec ${asmArrayvarname}_msb+$constIndex
+ dec ${asmArrayvarname}_lsb+$constIndex""")
} else {
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
else
@@ -91,22 +137,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> {
if(targetArrayIdx.usesPointerVariable) {
asmgen.out("""
lda $asmArrayvarname
clc
adc #$indexValue
sta (+) +1
lda $asmArrayvarname+1
adc #0
sta (+) +2""")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
} else {
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
}
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
}
in WordDatatypes -> {
if(incr)
@@ -127,25 +158,10 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
}
else
{
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
when(elementDt) {
in ByteDatatypes -> {
if(targetArrayIdx.usesPointerVariable) {
asmgen.out("""
txa
clc
adc $asmArrayvarname
sta (+) +1
lda $asmArrayvarname+1
adc #0
sta (+) +2""")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
} else {
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
}
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
}
in WordDatatypes -> {
if(incr)
@@ -34,8 +34,8 @@ internal class ProgramAndVarsGen(
header()
val allBlocks = program.allBlocks()
if(allBlocks.first().name != "p8_main" && allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main' or 'p8_main'")
if(allBlocks.first().name != "p8b_main" && allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main' or 'p8b_main'")
if(errors.noErrors()) {
program.allBlocks().forEach { block2asm(it) }
@@ -49,6 +49,7 @@ internal class ProgramAndVarsGen(
}
memorySlabs()
tempVars()
footer()
}
}
@@ -133,29 +134,22 @@ internal class ProgramAndVarsGen(
pha""")
}
// make sure that on the cx16 and c64, basic rom is banked in again when we exit the program
when(compTarget.name) {
"cx16" -> {
if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr p8_main.p8_start")
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp sys.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr p8_main.p8_start | lda #31 | sta $01")
if(!options.noSysInit)
asmgen.out(" jmp sys.cleanup_at_exit")
else
asmgen.out(" rts")
asmgen.out(" jsr p8b_main.p8s_start | lda #31 | sta $01")
asmgen.out(" jmp sys.cleanup_at_exit")
}
"c128" -> {
asmgen.out(" jsr p8_main.p8_start | lda #0 | sta ${"$"}ff00")
if(!options.noSysInit)
asmgen.out(" jmp sys.cleanup_at_exit")
else
asmgen.out(" rts")
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00")
asmgen.out(" jmp sys.cleanup_at_exit")
}
else -> asmgen.jmp("p8_main.p8_start")
else -> asmgen.jmp("p8b_main.p8s_start")
}
}
@@ -172,48 +166,119 @@ internal class ProgramAndVarsGen(
}
}
private fun tempVars() {
asmgen.out("; expression temp vars\n .section BSS")
for((dt, count) in asmgen.tempVarsCounters) {
if(count>0) {
for(num in 1..count) {
val name = asmgen.buildTempVarName(dt, num)
when (dt) {
DataType.BYTE -> asmgen.out("$name .char ?")
DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.WORD -> asmgen.out("$name .sint ?")
DataType.UWORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
}
}
asmgen.out(" .send BSS")
}
private fun footer() {
var relocateBssVars = false
var relocateBssSlabs = false
var relocatedBssStart = 0u
var relocatedBssEnd = 0u
if(options.varsGolden) {
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
}
relocateBssVars = true
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
}
else if(options.varsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
}
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
throw AssemblyError("slabs and vars high bank must be the same")
relocateBssVars = true
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
}
if(options.slabsGolden) {
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) {
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
}
relocateBssSlabs = true
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END
}
else if(options.slabsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u ||
options.compTarget.machine.BSSHIGHRAM_END == 0u ||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
}
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
throw AssemblyError("slabs and vars high bank must be the same")
relocateBssSlabs = true
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END
}
asmgen.out("; bss sections")
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
if(options.varsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
}
// BSS vars in high ram area, memory() slabs just concatenated at the end of the program.
if(symboltable.allMemorySlabs.isNotEmpty()) {
if(relocateBssVars) {
if(!relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS")
}
asmgen.out("prog8_program_end\t; end of program label for progend()")
asmgen.out(" * = ${options.compTarget.machine.BSSHIGHRAM_START.toHex()}")
asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS")
asmgen.out(" .cerror * >= ${options.compTarget.machine.BSSHIGHRAM_END.toHex()}, \"too many variables for BSS section\"")
if(relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
} else {
// BSS vars followed by memory() slabs, concatenated at the end of the program.
asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
if(symboltable.allMemorySlabs.isNotEmpty()) {
if(!relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS")
}
asmgen.out("prog8_program_end\t; end of program label for progend()")
if(relocateBssSlabs) {
asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
}
}
}
private fun block2asm(block: PtBlock) {
asmgen.out("")
asmgen.out("; ---- block: '${block.name}' ----")
if(block.address!=null)
asmgen.out("* = ${block.address!!.toHex()}")
if(block.options.address!=null)
asmgen.out("* = ${block.options.address!!.toHex()}")
else {
if(block.alignment==PtBlock.BlockAlignment.WORD)
if(block.options.alignment==PtBlock.BlockAlignment.WORD)
asmgen.out("\t.align 2")
else if(block.alignment==PtBlock.BlockAlignment.PAGE)
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE)
asmgen.out("\t.align $100")
}
asmgen.out("${block.name}\t" + (if(block.forceOutput) ".block" else ".proc"))
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
asmgen.outputSourceLine(block)
createBlockVariables(block)
@@ -236,7 +301,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" rts\n .bend")
}
asmgen.out(if(block.forceOutput) "\n\t.bend" else "\n\t.pend")
asmgen.out(if(block.options.forceOutput) "\n\t.bend" else "\n\t.pend")
}
private fun getVars(scope: StNode): Map<String, StNode> =
@@ -276,7 +341,7 @@ internal class ProgramAndVarsGen(
val asmStartScope: String
val asmEndScope: String
if(sub.definingBlock()!!.forceOutput) {
if(sub.definingBlock()!!.options.forceOutput) {
asmStartScope = ".block"
asmEndScope = ".bend"
} else {
@@ -299,7 +364,7 @@ internal class ProgramAndVarsGen(
val asmStartScope: String
val asmEndScope: String
if(sub.definingBlock()!!.forceOutput) {
if(sub.definingBlock()!!.options.forceOutput) {
asmStartScope = ".block"
asmEndScope = ".bend"
} else {
@@ -331,7 +396,7 @@ internal class ProgramAndVarsGen(
asmsubs2asm(sub.children)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
if((sub.name=="start" || sub.name=="p8_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8_main"))
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
@@ -29,7 +29,7 @@ internal class AnyExprAsmGen(
require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign)
}
throw AssemblyError("weird expr operand types: ${expr.left.type} and {${expr.right.type}")
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
}
in WordDatatypes -> {
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
@@ -49,54 +49,24 @@ internal class AnyExprAsmGen(
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
when(expr.operator) {
"+" -> {
TODO("word + at ${expr.position}")
}
"-" -> {
TODO("word - at ${expr.position}")
}
"*" -> {
TODO("word * at ${expr.position}")
}
"/" -> {
TODO("word / at ${expr.position}")
}
"<<" -> {
TODO("word << at ${expr.position}")
}
">>" -> {
TODO("word >> at ${expr.position}")
}
"%" -> {
TODO("word % at ${expr.position}")
}
"&", "and" -> {
TODO("word and at ${expr.position}")
}
"|", "or" -> {
TODO("word or at ${expr.position}")
}
"^", "xor" -> {
TODO("word xor at ${expr.position}")
}
"==" -> {
TODO("word == at ${expr.position}")
}
"!=" -> {
TODO("word != at ${expr.position}")
}
"<" -> {
TODO("word < at ${expr.position}")
}
"<=" -> {
TODO("word <= at ${expr.position}")
}
">" -> {
TODO("word > at ${expr.position}")
}
">=" -> {
TODO("word >= at ${expr.position}")
}
"+" -> TODO("word + at ${expr.position}")
"-" -> TODO("word - at ${expr.position}")
"*" -> TODO("word * at ${expr.position}")
"/" -> TODO("word / at ${expr.position}")
"<<" -> TODO("word << at ${expr.position}")
">>" -> TODO("word >> at ${expr.position}")
"%" -> TODO("word % at ${expr.position}")
"and" -> TODO("word logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("word logical or (with optional shortcircuit) ${expr.position}")
"&" -> TODO("word and at ${expr.position}")
"|" -> TODO("word or at ${expr.position}")
"^", "xor" -> TODO("word xor at ${expr.position}")
"==" -> TODO("word == at ${expr.position}")
"!=" -> TODO("word != at ${expr.position}")
"<" -> TODO("word < at ${expr.position}")
"<=" -> TODO("word <= at ${expr.position}")
">" -> TODO("word > at ${expr.position}")
">=" -> TODO("word >= at ${expr.position}")
else -> return false
}
}
@@ -134,7 +104,9 @@ internal class AnyExprAsmGen(
"%" -> {
TODO("byte % at ${expr.position}")
}
"&", "and" -> {
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
"&" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
@@ -142,7 +114,7 @@ internal class AnyExprAsmGen(
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"|", "or" -> {
"|" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
@@ -1,5 +1,7 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.StMemVar
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
@@ -62,32 +64,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val value = assign.source.array!!
val elementDt = assign.source.datatype
val arrayVarName = asmgen.asmVariableName(value.variable)
if(value.usesPointerVariable) {
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")
if(value.type != DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
if(asmgen.isTargetCpu(CpuType.CPU65c02) && (value.index as? PtNumber)?.number==0.0) {
if (asmgen.isZpVar(value.variable)) {
asmgen.out(" lda ($arrayVarName)")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda (P8ZP_SCRATCH_W1)")
}
} else {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
if (asmgen.isZpVar(value.variable)) {
asmgen.out(" lda ($arrayVarName),y")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
}
}
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, false)
return
}
val constIndex = value.index.asConstInteger()
if(value.splitWords) {
@@ -96,7 +72,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda ${arrayVarName}_lsb+$constIndex | ldy ${arrayVarName}_msb+$constIndex")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
} else {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
asmgen.out(" lda ${arrayVarName}_lsb,y | ldx ${arrayVarName}_msb,y")
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
}
@@ -125,17 +101,17 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} else {
when (elementDt) {
in ByteDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y")
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, true)
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y")
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.A)
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A)
asmgen.out("""
ldy #>$arrayVarName
clc
@@ -168,7 +144,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
is PtBinaryExpression -> {
val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) {
assignRegisterByte(assign.target, CpuRegister.A, false, true)
} else {
assignViaExprEval(value.address)
@@ -425,7 +401,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
val translatedOk = when (expr.operator) {
in ComparisonOperators -> optimizedComparison(expr, assign)
in setOf("&", "|", "^", "and", "or", "xor") -> optimizedLogicalOrBitwiseExpr(expr, assign.target)
in setOf("&", "|", "^", "xor") -> optimizedBitwiseExpr(expr, assign.target)
in setOf("and", "or") -> optimizedLogicalAndOrExpr(expr, assign.target)
"+", "-" -> optimizedPlusMinExpr(expr, assign.target)
"<<", ">>" -> optimizedBitshiftExpr(expr, assign.target)
"*" -> optimizedMultiplyExpr(expr, assign.target)
@@ -483,13 +460,24 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return false
}
private fun directIntoY(expr: PtExpression): Boolean {
return when(expr) {
is PtIdentifier -> true
is PtMachineRegister -> true
is PtNumber -> true
is PtBuiltinFunctionCall -> expr.name in arrayOf("lsb", "msb")
else -> false
}
}
private fun optimizedRemainderExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
when(expr.type) {
DataType.UBYTE -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
if(!directIntoY(expr.right)) asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
asmgen.out(" pla | jsr math.divmod_ub_asm")
if(!directIntoY(expr.right)) asmgen.out(" pla")
asmgen.out(" jsr math.divmod_ub_asm")
if(target.register==RegisterOrPair.A)
asmgen.out(" cmp #0") // fix the status register
else
@@ -510,17 +498,19 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when(expr.type) {
DataType.UBYTE -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
if(!directIntoY(expr.right)) asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
asmgen.out(" pla | jsr math.divmod_ub_asm")
if(!directIntoY(expr.right)) asmgen.out(" pla")
asmgen.out(" jsr math.divmod_ub_asm")
assignRegisterByte(target, CpuRegister.Y, false, true)
return true
}
DataType.BYTE -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, true)
asmgen.out(" pha")
if(!directIntoY(expr.right)) asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, true)
asmgen.out(" pla | jsr math.divmod_b_asm")
if(!directIntoY(expr.right)) asmgen.out(" pla")
asmgen.out(" jsr math.divmod_b_asm")
assignRegisterByte(target, CpuRegister.Y, true, true)
return true
}
@@ -546,25 +536,26 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when(expr.type) {
in ByteDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type in SignedDatatypes)
asmgen.out(" pha")
if(!directIntoY(expr.right)) asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, expr.type in SignedDatatypes)
asmgen.out(" pla | jsr math.multiply_bytes")
if(!directIntoY(expr.right)) asmgen.out(" pla")
asmgen.out(" jsr math.multiply_bytes")
assignRegisterByte(target, CpuRegister.A, false, true)
return true
}
in WordDatatypes -> {
if(expr.definingBlock()!!.veraFxMuls) {
if(expr.definingBlock()!!.options.veraFxMuls) {
// cx16 verafx hardware mul
if(expr.right.isSimple()) {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R0, expr.left.type in SignedDatatypes)
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes)
} else {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.out(" pha")
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" pla")
asmgen.out(" sta cx16.r0 | sty cx16.r0+1")
}
asmgen.out(" jsr verafx.muls")
@@ -596,7 +587,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" jsr math.mul_word_${value}")
}
else {
if(expr.definingBlock()!!.veraFxMuls){
if(expr.definingBlock()!!.options.veraFxMuls){
// cx16 verafx hardware mul
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1")
asmgen.out("""
@@ -731,7 +722,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
asmgen.out(" tay | lda #0")
} else {
asmgen.out(" ldx #$shifts | jsr math.lsr_word_AY")
asmgen.out(" ldx #$shifts")
if(signed)
asmgen.out(" jsr math.lsr_word_AY")
else
asmgen.out(" jsr math.lsr_uword_AY")
}
assignRegisterpairWord(target, RegisterOrPair.AY)
return true
@@ -777,37 +772,47 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true
}
else -> {
val leftMemByte = expr.left as? PtMemoryByte
val rightMemByte = expr.right as? PtMemoryByte
val leftArrayIndexer = expr.left as? PtArrayIndexer
val rightArrayIndexer = expr.right as? PtArrayIndexer
if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
// special optimization for bytevalue +/- bytearr[y] : no need to use a tempvar, just use adc array,y or sbc array,y or adc (ptr),y / sbc (ptr),y
if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type in ByteDatatypes && right.type in ByteDatatypes) {
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
assignExpressionToRegister(right, RegisterOrPair.A, right.type==DataType.BYTE)
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y, false)
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
asmgen.out(" clc | adc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
} else if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
// special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,y
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
if(rightArrayIndexer.usesPointerVariable && !asmgen.isZpVar(rightArrayIndexer.variable)) {
asmgen.out("""
ldx ${rightArrayIndexer.variable.name}
stx P8ZP_SCRATCH_W1
ldx ${rightArrayIndexer.variable.name}+1
stx P8ZP_SCRATCH_W1+1""")
if (expr.operator == "+")
asmgen.out(" clc | adc (P8ZP_SCRATCH_W1),y")
else
asmgen.out(" sec | sbc (P8ZP_SCRATCH_W1),y")
} else {
val arrayvarname = if (rightArrayIndexer.usesPointerVariable)
"(${rightArrayIndexer.variable.name})"
else
asmgen.asmSymbolName(rightArrayIndexer.variable)
if (expr.operator == "+")
asmgen.out(" clc | adc $arrayvarname,y")
else
asmgen.out(" sec | sbc $arrayvarname,y")
}
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
if (expr.operator == "+")
asmgen.out(" clc | adc $arrayvarname,y")
else
asmgen.out(" sec | sbc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
} else if(expr.operator=="+" && leftMemByte!=null && right.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) {
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
return true
} else if(rightMemByte!=null && left.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(left, expr.operator, rightMemByte)) {
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
return true
} else {
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
if(directIntoY(right)) {
assignExpressionToRegister(right, RegisterOrPair.Y, left.type==DataType.BYTE)
asmgen.out(" sty P8ZP_SCRATCH_B1")
} else {
asmgen.out(" pha")
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", right.type)
asmgen.out(" pla")
}
if (expr.operator == "+")
asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
else
@@ -982,28 +987,74 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return false
}
private fun optimizedLogicalOrBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
private fun optimizedPointerIndexPlusMinusByteIntoA(value: PtExpression, operator: String, mem: PtMemoryByte): Boolean {
// special optimization for bytevalue +/- pointervar[y] (actually: bytevalue +/- @(address) )
val address = mem.address as? PtBinaryExpression
if(address is PtBinaryExpression) {
val ptrVar = address.left as? PtIdentifier
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
assignExpressionToRegister(value, RegisterOrPair.A, false)
val pointername = asmgen.asmVariableName(ptrVar)
val constOffset = address.right.asConstInteger()
if (constOffset != null && constOffset < 256) {
// we have value + @(zpptr + 255), or value - @(zpptr+255)
asmgen.out(" ldy #$constOffset")
if (operator == "+")
asmgen.out(" clc | adc ($pointername),y")
else
asmgen.out(" sec | sbc ($pointername),y")
} else if (address.right.type in ByteDatatypes) {
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
asmgen.out(" pha")
assignExpressionToRegister(address.right, RegisterOrPair.Y, false)
asmgen.out(" pla")
if (operator == "+")
asmgen.out(" clc | adc ($pointername),y")
else
asmgen.out(" sec | sbc ($pointername),y")
} else if ((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
// we have @(ptr + bytevar as uword) ++ , or @(ptr+bytevar as uword)--
asmgen.out(" pha")
assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.Y, false)
asmgen.out(" pla")
if (operator == "+")
asmgen.out(" clc | adc ($pointername),y")
else
asmgen.out(" sec | sbc ($pointername),y")
}
return true
}
}
return false
}
private fun optimizedBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
if (expr.right.isSimple()) {
if (expr.right is PtNumber || expr.right is PtIdentifier) {
assignLogicalWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
assignBitwiseWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
return true
}
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
assignLogicalWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
assignBitwiseWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
return true
}
}
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.saveRegisterStack(CpuRegister.A, false)
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(directIntoY(expr.right)) {
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
asmgen.out(" sty P8ZP_SCRATCH_B1")
} else {
asmgen.out(" pha")
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla")
}
when (expr.operator) {
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid operator")
else -> throw AssemblyError("invalid bitwise operator")
}
assignRegisterByte(target, CpuRegister.A, false, true)
return true
@@ -1011,20 +1062,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if (expr.right.isSimple()) {
if (expr.right is PtNumber || expr.right is PtIdentifier) {
assignLogicalWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
assignBitwiseWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
return true
}
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
assignLogicalWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
assignBitwiseWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
return true
}
}
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
when (expr.operator) {
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
"&" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
"|" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
else -> throw AssemblyError("invalid operator")
else -> throw AssemblyError("invalid bitwise operator")
}
assignRegisterpairWord(target, RegisterOrPair.AY)
return true
@@ -1032,6 +1083,63 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return false
}
private fun optimizedLogicalAndOrExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
if (expr.right is PtNumber || expr.right is PtIdentifier) {
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
return true
}
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
return true
}
if(!expr.right.isSimple()) {
// shortcircuit evaluation into A
val shortcutLabel = asmgen.makeLabel("shortcut")
when (expr.operator) {
"and" -> {
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" beq $shortcutLabel")
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
asmgen.out(shortcutLabel)
}
"or" -> {
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" bne $shortcutLabel")
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
asmgen.out(shortcutLabel)
}
else -> throw AssemblyError("invalid logical operator")
}
} else {
// normal evaluation into A, it is *likely* shorter and faster because of the simple operands.
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
if(directIntoY(expr.right)) {
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
asmgen.out(" sty P8ZP_SCRATCH_B1")
} else {
asmgen.out(" pha")
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla")
}
when (expr.operator) {
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid logical operator")
}
}
assignRegisterByte(target, CpuRegister.A, false, true)
return true
}
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
throw AssemblyError("logical and/or/xor on words should be done on typecast to bytes instead")
}
return false
}
private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
val signed = expr.left.type == DataType.BYTE || expr.right.type == DataType.BYTE
when(expr.operator) {
@@ -1703,7 +1811,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true
}
private fun assignLogicalWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
private fun assignBitwiseWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
assignExpressionToRegister(left, RegisterOrPair.A, false)
val operand = when(right) {
is PtNumber -> "#${right.number.toHex()}"
@@ -1711,33 +1819,52 @@ internal class AssignmentAsmGen(private val program: PtProgram,
else -> throw AssemblyError("wrong right operand type")
}
when (operator) {
"&", "and" -> asmgen.out(" and $operand")
"|", "or" -> asmgen.out(" ora $operand")
"&" -> asmgen.out(" and $operand")
"|" -> asmgen.out(" ora $operand")
"^", "xor" -> asmgen.out(" eor $operand")
else -> throw AssemblyError("invalid operator")
}
assignRegisterByte(target, CpuRegister.A, false, true)
}
private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
private fun assignLogicalAndOrWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
// normal evaluation, not worth to shortcircuit the simple right operand
assignExpressionToRegister(left, RegisterOrPair.A, false)
if(directIntoY(right)) {
assignExpressionToRegister(right, RegisterOrPair.Y, false)
asmgen.out(" sty P8ZP_SCRATCH_B1")
} else {
asmgen.out(" pha")
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla")
}
when (operator) {
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid logical operator")
}
assignRegisterByte(target, CpuRegister.A, false, true)
}
private fun assignBitwiseWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
assignExpressionToRegister(left, RegisterOrPair.AY, false)
when(right) {
is PtNumber -> {
val number = right.number.toHex()
when (operator) {
"&", "and" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa")
"|", "or" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa")
"&" -> asmgen.out(" and #<$number | tax | tya | and #>$number | tay | txa")
"|" -> asmgen.out(" ora #<$number | tax | tya | ora #>$number | tay | txa")
"^", "xor" -> asmgen.out(" eor #<$number | tax | tya | eor #>$number | tay | txa")
else -> throw AssemblyError("invalid operator")
else -> throw AssemblyError("invalid bitwise operator")
}
}
is PtIdentifier -> {
val name = asmgen.asmSymbolName(right)
when (operator) {
"&", "and" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa")
"|", "or" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa")
"&" -> asmgen.out(" and $name | tax | tya | and $name+1 | tay | txa")
"|" -> asmgen.out(" ora $name | tax | tya | ora $name+1 | tay | txa")
"^", "xor" -> asmgen.out(" eor $name | tax | tya | eor $name+1 | tay | txa")
else -> throw AssemblyError("invalid operator")
else -> throw AssemblyError("invalid bitwise operator")
}
}
else -> throw AssemblyError("wrong right operand type")
@@ -1814,44 +1941,41 @@ internal class AssignmentAsmGen(private val program: PtProgram,
private fun containmentCheckIntoA(containment: PtContainmentCheck) {
val elementDt = containment.element.type
val symbol = asmgen.symbolTable.lookup(containment.iterable.name)
val variable = symbol!!.astNode as IPtVariable
val varname = asmgen.asmVariableName(containment.iterable)
val numElements = when(variable) {
is PtConstant -> null
is PtMemMapped -> variable.arraySize
is PtVariable -> variable.arraySize
val symbol = asmgen.symbolTable.lookup(containment.iterable.name)!!
val symbolName = asmgen.asmVariableName(symbol, containment.definingSub())
val (dt, numElements) = when(symbol) {
is StStaticVariable -> symbol.dt to symbol.length!!
is StMemVar -> symbol.dt to symbol.length!!
else -> DataType.UNDEFINED to 0
}
when(variable.type) {
when(dt) {
DataType.STR -> {
// use subroutine
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterStack(CpuRegister.A, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname, null, null)
asmgen.restoreRegisterStack(CpuRegister.A, false)
val stringVal = (variable as PtVariable).value as PtString
asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, null, null)
asmgen.out(" pla")
asmgen.out(" ldy #${numElements-1}")
asmgen.out(" jsr prog8_lib.containment_bytearray")
return
}
DataType.ARRAY_F -> {
throw AssemblyError("containment check of floats not supported")
assignExpressionToRegister(containment.element, RegisterOrPair.FAC1, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr floats.containment_floatarray")
}
DataType.ARRAY_B, DataType.ARRAY_UB -> {
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterStack(CpuRegister.A, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname, null, null)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
asmgen.out(" pla")
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray")
return
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname, null, null)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, null, null)
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray")
return
}
else -> throw AssemblyError("invalid dt")
}
@@ -1870,7 +1994,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
+ lda #1
+""")
}
else -> throw AssemblyError("can't use Z or N flags as return 'values'")
Statusflag.Pz -> {
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+""")
}
Statusflag.Pn -> {
asmgen.out("""
bmi +
lda #0
beq ++
+ lda #1
+""")
}
}
assignRegisterByte(target, CpuRegister.A, false, true)
}
@@ -1913,7 +2052,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
is PtBinaryExpression -> {
val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
@@ -1974,20 +2113,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
if(valueDt==DataType.UBYTE || valueDt==DataType.BOOL) {
if(valueDt in ByteDatatypes) {
when(target.register) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> {
// 'cast' an ubyte value to a byte register; no cast needed at all
return assignExpressionToRegister(value, target.register, false)
return assignExpressionToRegister(value, target.register, valueDt in SignedDatatypes)
}
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY,
in Cx16VirtualRegisters -> {
assignExpressionToRegister(value, RegisterOrPair.A, false)
assignRegisterByte(target, CpuRegister.A, false, true)
assignRegisterByte(target, CpuRegister.A, valueDt in SignedDatatypes, true)
return
}
else -> {}
@@ -2006,7 +2145,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.XY,
in Cx16VirtualRegisters -> {
// 'cast' uword into a 16 bits register, just assign it
return assignExpressionToRegister(value, target.register!!, false)
return assignExpressionToRegister(value, target.register!!, targetDt in SignedDatatypes)
}
else -> {}
}
@@ -2391,7 +2530,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
else
program.memsizer.memorySize(arrayDt!!, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
} else {
val eltSize = program.memsizer.memorySize(ArrayToElementTypes.getValue(arrayDt!!))
val eltSize = if(arrayDt==DataType.UWORD) 1 else program.memsizer.memorySize(ArrayToElementTypes.getValue(arrayDt!!))
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_W1", DataType.UWORD)
when(eltSize) {
1 -> {}
@@ -2538,11 +2677,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
{
when(target.datatype) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
}
DataType.UWORD, DataType.WORD -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
if(target.array.splitWords)
asmgen.out("""
lda $sourceName
@@ -2641,11 +2780,11 @@ internal class AssignmentAsmGen(private val program: PtProgram,
jsr floats.copy_float""")
}
TargetStorageKind.ARRAY -> {
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.out(" pha")
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" pla")
asmgen.out("""
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
@@ -2715,21 +2854,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(assignsIndexedPointerVar(target)) {
if (target.constArrayIndexValue==0u) {
asmgen.out(" lda $sourceName")
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
} else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y")
} else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
asmgen.out(" lda $sourceName | sta (P8ZP_SCRATCH_W2),y")
}
}
return
}
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) {
@@ -2737,7 +2861,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
}
}
@@ -2788,7 +2912,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.X)
asmgen.out(" lda $sourceName")
asmgen.signExtendAYlsb(DataType.BYTE)
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
@@ -2857,7 +2981,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}_msb+$scaledIdx")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.Y)
asmgen.out("""
lda $sourceName
sta ${wordtarget.asmVarname}_lsb,y
@@ -2874,7 +2998,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, CpuRegister.Y)
asmgen.out("""
lda $sourceName
sta ${wordtarget.asmVarname},y
@@ -3025,7 +3149,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
CpuRegister.X -> when(target.register!!) {
RegisterOrPair.A -> { asmgen.out(" txa") }
RegisterOrPair.X -> { }
RegisterOrPair.Y -> { asmgen.out(" txy") }
RegisterOrPair.Y -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG") }
RegisterOrPair.AY -> {
require(extendWord)
if(signed)
@@ -3075,7 +3199,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
CpuRegister.Y -> when(target.register!!) {
RegisterOrPair.A -> { asmgen.out(" tya") }
RegisterOrPair.X -> { asmgen.out(" tyx") }
RegisterOrPair.X -> { asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
RegisterOrPair.Y -> { }
RegisterOrPair.AY -> {
require(extendWord)
@@ -3135,88 +3259,29 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(target.array!!.splitWords)
throw AssemblyError("cannot assign byte to split word array here ${target.position}")
if(assignsIndexedPointerVar(target)) {
// assign byte to a byte array via an uword pointervariable instead of a regular array variable
if (target.constArrayIndexValue!=null) {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
asmgen.out(" ldy #${target.constArrayIndexValue} | sta (${target.asmVarname}),y")
} else {
asmgen.out("""
ldy ${target.asmVarname}
sty P8ZP_SCRATCH_W1
ldy ${target.asmVarname}+1
sty P8ZP_SCRATCH_W1+1
ldy #${target.constArrayIndexValue}
sta (P8ZP_SCRATCH_W1),y""")
}
// assign regular array indexing
if (target.constArrayIndexValue!=null) {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
else {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
val indexVar = target.array.index as? PtIdentifier
if(indexVar!=null) {
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta (${target.asmVarname}),y")
} else {
asmgen.out("""
ldy ${target.asmVarname}
sty P8ZP_SCRATCH_W1
ldy ${target.asmVarname}+1
sty P8ZP_SCRATCH_W1+1
ldy ${asmgen.asmVariableName(indexVar)}
sta (P8ZP_SCRATCH_W1),y""")
}
} else {
asmgen.saveRegisterStack(register, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
asmgen.out(" sta (${target.asmVarname}),y")
} else {
asmgen.out("""
ldx ${target.asmVarname}
stx P8ZP_SCRATCH_W1
ldx ${target.asmVarname}+1
stx P8ZP_SCRATCH_W1+1
sta (P8ZP_SCRATCH_W1),y""")
}
}
asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
}
else {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
return
} else {
// assign regular array indexing
if (target.constArrayIndexValue!=null) {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
}
else {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
val indexVar = target.array.index as? PtIdentifier
if(indexVar!=null) {
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
} else {
require(target.array.index.type in ByteDatatypes)
asmgen.saveRegisterStack(register, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" sta ${target.asmVarname},y")
}
val indexVar = target.array.index as? PtIdentifier
if(indexVar!=null) {
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
} else {
require(target.array.index.type in ByteDatatypes)
asmgen.saveRegisterStack(register, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
asmgen.out(" pla | sta ${target.asmVarname},y")
}
}
}
@@ -3273,7 +3338,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.out("""
pla
sta ${target.asmVarname}_msb,y
@@ -3281,7 +3346,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sta ${target.asmVarname}_lsb,y""")
} else {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.out("""
lda $srcReg
sta ${target.asmVarname}_lsb,y
@@ -3316,7 +3381,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.out("""
iny
pla
@@ -3326,7 +3391,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sta ${target.asmVarname},y""")
} else {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.out("""
iny
lda $srcReg+1
@@ -3412,7 +3477,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
throw AssemblyError("memory is bytes not words")
}
TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
if(target.array.splitWords)
asmgen.out("""
lda #0
@@ -3466,7 +3531,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
}
TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
if(target.array.splitWords)
asmgen.out("""
lda #<${word.toHex()}
@@ -3512,21 +3577,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(assignsIndexedPointerVar(target)) {
if (target.constArrayIndexValue==0u) {
asmgen.out(" lda #0")
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
} else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
asmgen.out(" lda #0 | sta (${target.asmVarname}),y")
} else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
asmgen.out(" lda #0 | sta (P8ZP_SCRATCH_W2),y")
}
}
return
}
if(target.array!!.splitWords)
throw AssemblyError("cannot assign byte to split word array here ${target.position}")
if (target.constArrayIndexValue!=null) {
@@ -3566,21 +3616,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(assignsIndexedPointerVar(target)) {
if (target.constArrayIndexValue==0u) {
asmgen.out(" lda #${byte.toHex()}")
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
} else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y")
} else {
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
asmgen.out(" lda #${byte.toHex()} | sta (P8ZP_SCRATCH_W2),y")
}
}
return
}
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) {
@@ -3588,7 +3623,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y")
}
}
@@ -3612,9 +3647,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
private fun assignsIndexedPointerVar(target: AsmAssignTarget): Boolean =
target.origAstTarget?.array?.variable?.type==DataType.UWORD
internal fun assignConstantFloat(target: AsmAssignTarget, float: Double) {
if (float == 0.0) {
// optimized case for float zero
@@ -3887,7 +3919,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
}
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
storeViaExprEval()
}
else -> storeViaExprEval()
@@ -3941,9 +3973,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sta $addr""")
}
is PtIdentifier -> {
val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
asmgen.out(" eor #255")
asmgen.out(" sta ($sourceName),y")
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
}
else -> {
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
@@ -37,8 +37,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"*=" -> inplaceModification(assign.target, "*", assign.source)
"/=" -> inplaceModification(assign.target, "/", assign.source)
"|=" -> inplaceModification(assign.target, "|", assign.source)
"or=" -> inplaceModification(assign.target, "or", assign.source)
"&=" -> inplaceModification(assign.target, "&", assign.source)
"^=" -> inplaceModification(assign.target, "^", assign.source)
"and=" -> inplaceModification(assign.target, "and", assign.source)
"^=", "xor=" -> inplaceModification(assign.target, "^", assign.source)
"<<=" -> inplaceModification(assign.target, "<<", assign.source)
">>=" -> inplaceModification(assign.target, ">>", assign.source)
"%=" -> inplaceModification(assign.target, "%", assign.source)
@@ -161,7 +163,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
else -> {
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, memory.definingISub(), target.position, register = RegisterOrPair.AY))
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
return
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false)
asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.saveRegisterStack(CpuRegister.Y, true)
asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A")
@@ -189,12 +193,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" ldx P8ZP_SCRATCH_B1")
}
SourceStorageKind.EXPRESSION -> {
asmgen.out(" sta P8ZP_SCRATCH_B1")
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
asmgen.out(" sta $tempVar")
if(value.expression is PtTypeCast)
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression)
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression)
else
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!)
asmgen.out(" ldx P8ZP_SCRATCH_B1")
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
asmgen.out(" ldx $tempVar")
}
}
asmgen.restoreRegisterStack(CpuRegister.Y, false)
@@ -206,10 +211,23 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
TargetStorageKind.ARRAY -> {
val indexNum = target.array!!.index as? PtNumber
if (indexNum!=null) {
val targetVarName = if(target.array.splitWords)
"${target.asmVarname} + ${indexNum.number.toInt()}"
else
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
val index = indexNum.number.toInt()
if(target.array.splitWords) {
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplacemodificationSplitWordWithLiteralval(target.asmVarname, index, operator, value.number!!.number.toInt())
else -> {
// TODO: more optimized code for VARIABLE, REGISTER, MEMORY, ARRAY, EXPRESSION in the case of split-word arrays
val scope = target.origAstTarget?.definingSub()
val regTarget = AsmAssignTarget.fromRegisters(RegisterOrPair.R0, false, target.position, scope, asmgen)
val assignToReg = AsmAssignment(value, regTarget, program.memsizer, target.position)
assignmentAsmGen.translateNormalAssignment(assignToReg, scope)
inplacemodificationSplitWordWithR0(target.asmVarname, index, operator)
}
}
return
}
// normal array
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}"
when (target.datatype) {
in ByteDatatypes -> {
when(value.kind) {
@@ -276,7 +294,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.out(" lda ${target.array.variable.name},y")
when(value.kind) {
@@ -310,13 +328,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
SourceStorageKind.EXPRESSION -> {
asmgen.out(" sta P8ZP_SCRATCH_B1")
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
asmgen.out(" sta $tempVar")
if(value.expression is PtTypeCast)
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression)
inplacemodificationByteVariableWithValue(tempVar, target.datatype, operator, value.expression)
else
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!)
inplacemodificationByteVariableWithValue(tempVar, target.datatype, operator, value.expression!!)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" lda P8ZP_SCRATCH_B1")
asmgen.out(" lda $tempVar")
}
}
asmgen.out(" sta ${target.array.variable.name},y")
@@ -327,7 +346,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.saveRegisterStack(CpuRegister.Y, false)
if(target.array.splitWords) {
asmgen.out(" lda ${target.array.variable.name}_lsb,y")
@@ -384,12 +403,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
SourceStorageKind.EXPRESSION -> {
asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1")
val tempVar = asmgen.getTempVarName(DataType.UWORD)
asmgen.out(" sta $tempVar | stx $tempVar+1")
if(value.expression is PtTypeCast)
inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression, block)
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
else
inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!, block)
asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1")
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression!!, block)
asmgen.out(" lda $tempVar | ldx $tempVar+1")
}
}
asmgen.restoreRegisterStack(CpuRegister.Y, true)
@@ -402,7 +422,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
DataType.FLOAT -> {
// copy array value into tempvar
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
asmgen.out("""
ldy #>${target.asmVarname}
clc
@@ -456,6 +476,133 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
private fun tryOptimizedMemoryInplace(address: PtBinaryExpression, operator: String, value: AsmAssignSource): Boolean {
if(value.datatype !in ByteDatatypes || operator !in "|&^+-")
return false
val rightTc = address.right as? PtTypeCast
val constOffset = (address.right as? PtNumber)?.number?.toInt()
if(address.operator=="+" && (address.right.type in ByteDatatypes || (rightTc!=null && rightTc.value.type in ByteDatatypes) || (constOffset!=null && constOffset<256)) ) {
if(rightTc!=null)
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
else if(constOffset!=null)
asmgen.out(" lda #${constOffset}")
else
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
asmgen.out(" pha") // offset on stack
val zpPointerVarName: String
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
zpPointerVarName = (address.left as PtIdentifier).name
} else {
zpPointerVarName = "P8ZP_SCRATCH_W2"
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.out(" sta $zpPointerVarName | sty $zpPointerVarName+1")
}
// calculate value into A
val assignValue = AsmAssignment(value,
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A),
program.memsizer, Position.DUMMY)
assignmentAsmGen.translateNormalAssignment(assignValue, address.definingISub())
asmgen.restoreRegisterStack(CpuRegister.Y, false) // offset into Y
when(operator) {
"|" -> asmgen.out(" ora ($zpPointerVarName),y")
"&" -> asmgen.out(" and ($zpPointerVarName),y")
"^" -> asmgen.out(" eor ($zpPointerVarName),y")
"+" -> asmgen.out(" clc | adc ($zpPointerVarName),y")
"-" -> asmgen.out(" sta P8ZP_SCRATCH_REG | lda ($zpPointerVarName),y | sec | sbc P8ZP_SCRATCH_REG")
else -> throw AssemblyError("invalid op")
}
asmgen.out(" sta ($zpPointerVarName),y")
return true
}
return false
}
private fun inplacemodificationSplitWordWithR0(arrayVar: String, index: Int, operator: String) {
when (operator) {
"+" -> {
asmgen.out("""
lda ${arrayVar}_lsb+$index
clc
adc cx16.r0L
sta ${arrayVar}_lsb+$index
lda ${arrayVar}_msb+$index
adc cx16.r0H
sta ${arrayVar}_msb+$index""")
}
"-" -> {
asmgen.out("""
lda ${arrayVar}_lsb+$index
sec
sbc cx16.r0L
sta ${arrayVar}_lsb+$index
lda ${arrayVar}_msb+$index
sbc cx16.r0H
sta ${arrayVar}_msb+$index""")
}
else -> TODO("in-place modify split-words array value for operator $operator")
}
}
private fun inplacemodificationSplitWordWithLiteralval(arrayVar: String, index: Int, operator: String, value: Int) {
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
when (operator) {
"+" -> {
when {
value==0 -> {}
value in 1..0xff -> asmgen.out("""
lda ${arrayVar}_lsb+$index
clc
adc #$value
sta ${arrayVar}_lsb+$index
bcc +
inc ${arrayVar}_msb+$index
+""")
value==0x0100 -> asmgen.out(" inc ${arrayVar}_msb+$index")
value==0x0200 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
value==0x0300 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
value==0x0400 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
value and 255==0 -> asmgen.out(" lda ${arrayVar}_msb+$index | clc | adc #>$value | sta ${arrayVar}_msb+$index")
else -> asmgen.out("""
lda ${arrayVar}_lsb+$index
clc
adc #<$value
sta ${arrayVar}_lsb+$index
lda ${arrayVar}_msb+$index
adc #>$value
sta ${arrayVar}_msb+$index""")
}
}
"-" -> {
when {
value==0 -> {}
value in 1..0xff -> asmgen.out("""
lda ${arrayVar}_lsb+$index
sec
sbc #$value
sta ${arrayVar}_lsb+$index
bcs +
dec ${arrayVar}_msb+$index
+""")
value==0x0100 -> asmgen.out(" dec ${arrayVar}_msb+$index")
value==0x0200 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
value==0x0300 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
value==0x0400 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
value and 255==0 -> asmgen.out(" lda ${arrayVar}_msb+$index | sec | sbc #>$value | sta ${arrayVar}_msb+$index")
else -> asmgen.out("""
lda ${arrayVar}_lsb+$index
sec
sbc #<$value
sta ${arrayVar}_lsb+$index
lda ${arrayVar}_msb+$index
sbc #>$value
sta ${arrayVar}_msb+$index""")
}
}
else -> TODO("in-place modify split-words array value for operator $operator")
}
}
private fun inplacemodificationRegisterAXwithVariable(operator: String, variable: String, varDt: DataType): Boolean {
when(operator) {
"+" -> {
@@ -791,12 +938,38 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
val shortcutLabel = asmgen.makeLabel("shortcut")
when (operator) {
"and" -> {
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
asmgen.out(" lda $name | beq $shortcutLabel")
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
asmgen.out("""
and $name
sta $name
$shortcutLabel:""")
return
}
"or" -> {
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
asmgen.out(" lda $name | bne $shortcutLabel")
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
asmgen.out("""
ora $name
sta $name
$shortcutLabel:""")
return
}
}
// normal evaluation
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
asmgen.out(" sta $name")
}
private fun inplacemodificationByteVariableWithVariable(name: String, dt: DataType, operator: String, otherName: String) {
// note: no logical and/or shortcut here, not worth it due to simple right operand
asmgen.out(" lda $name")
inplacemodificationRegisterAwithVariable(operator, otherName, dt in SignedDatatypes)
asmgen.out(" sta $name")
@@ -850,9 +1023,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
+""")
}
}
"&" -> asmgen.out(" and $variable")
"|" -> asmgen.out(" ora $variable")
"^" -> asmgen.out(" eor $variable")
"&", "and" -> asmgen.out(" and $variable")
"|", "or" -> asmgen.out(" ora $variable")
"^","xor" -> asmgen.out(" eor $variable")
"==" -> {
asmgen.out("""
cmp $variable
@@ -1130,6 +1303,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private fun inplacemodificationByteVariableWithLiteralval(name: String, dt: DataType, operator: String, value: Int) {
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
// note: no logical and/or shortcut here, not worth it due to simple right operand
when (operator) {
"+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name")
"-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name")
@@ -1192,9 +1366,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
}
"&" -> immediateAndInplace(name, value)
"|" -> immediateOrInplace(name, value)
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
"&", "and" -> immediateAndInplace(name, value)
"|", "or" -> immediateOrInplace(name, value)
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
"==" -> {
asmgen.out("""
lda $name
@@ -1440,7 +1614,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(value in asmgen.optimizedWordMultiplications) {
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
} else {
if(block?.veraFxMuls==true)
if(block?.options?.veraFxMuls==true)
// cx16 verafx hardware mul
asmgen.out("""
lda $name
@@ -1907,7 +2081,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $name+1""")
}
"*" -> {
if(block?.veraFxMuls==true) {
if(block?.options?.veraFxMuls==true) {
// cx16 verafx hardware muls
if(valueDt==DataType.UBYTE) {
asmgen.out(" lda $otherName | sta cx16.r1")
@@ -2075,7 +2249,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
"*" -> {
if(block?.veraFxMuls==true)
if(block?.options?.veraFxMuls==true)
// cx16 verafx hardware muls
asmgen.out("""
lda $name
@@ -2279,7 +2453,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression, block: PtBlock?) {
fun multiplyVarByWordInAY() {
if(block?.veraFxMuls==true)
if(block?.options?.veraFxMuls==true)
// cx16 verafx hardware muls
asmgen.out("""
sta cx16.r1
+11 -3
View File
@@ -29,11 +29,11 @@ internal object DummyStringEncoder : IStringEncoding {
}
}
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
IErrorReporter {
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter {
val errors = mutableListOf<String>()
val warnings = mutableListOf<String>()
val infos = mutableListOf<String>()
override fun err(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg"
@@ -47,6 +47,12 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text)
}
override fun info(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg"
if(text !in infos)
infos.add(text)
}
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
@@ -54,10 +60,11 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
override fun noErrors(): Boolean = errors.isEmpty()
override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
if(throwExceptionAtReportIfErrors)
finalizeNumErrors(errors.size, warnings.size)
finalizeNumErrors(errors.size, warnings.size, infos.size)
if(!keepMessagesAfterReporting) {
clear()
}
@@ -66,5 +73,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
fun clear() {
errors.clear()
warnings.clear()
infos.clear()
}
}
+2 -2
View File
@@ -45,7 +45,7 @@ class TestCodegen: FunSpec({
//}
val codegen = AsmGen6502(prefixSymbols = false)
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@@ -95,7 +95,7 @@ class TestCodegen: FunSpec({
program.add(block)
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block)
@@ -14,5 +14,6 @@
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component>
</module>
@@ -1,12 +1,13 @@
package prog8.codegen.intermediate
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getOrElse
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.PrefixOperators
import prog8.code.core.SignedDatatypes
import prog8.code.core.*
import prog8.intermediate.*
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
internal fun translate(assignment: PtAssignment): IRCodeChunks {
@@ -22,91 +23,57 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(augAssign.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
val ident = augAssign.target.identifier
val memory = augAssign.target.memory
val array = augAssign.target.array
val target = augAssign.target
val targetDt = irType(target.type)
val memTarget = target.memory
val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt()
val symbol = target.identifier?.name
val array = target.array
val value = augAssign.value
val signed = target.type in SignedDatatypes
val result: Result<IRCodeChunks, NotImplementedError> = when(augAssign.operator) {
"+=" -> expressionEval.operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"-=" -> expressionEval.operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"*=" -> expressionEval.operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
"/=" -> expressionEval.operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"|=" -> expressionEval.operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"or=" -> expressionEval.operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"&=" -> expressionEval.operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"and=" -> expressionEval.operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"^=", "xor=" -> expressionEval.operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
"<<=" -> expressionEval.operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
">>=" -> expressionEval.operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"%=" -> expressionEval.operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
"==" -> expressionEval.operatorEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
"!=" -> expressionEval.operatorNotEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
"<" -> expressionEval.operatorLessInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
">" -> expressionEval.operatorGreaterInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"<=" -> expressionEval.operatorLessEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
">=" -> expressionEval.operatorGreaterEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
val chunks = if(ident!=null) {
assignVarAugmented(ident.name, augAssign)
} else if(memory != null) {
if(memory.address is PtNumber)
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
else
fallbackAssign(augAssign)
} else if(array!=null) {
// 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(augAssign)
} else {
fallbackAssign(augAssign)
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
}
val chunks = result.getOrElse { fallbackAssign(augAssign) }
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
return chunks
}
private fun assignMemoryAugmented(
address: Int,
assignment: PtAugmentedAssign
): IRCodeChunks {
val value = assignment.value
val vmDt = irType(value.type)
return when(assignment.operator) {
"+=" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
"-=" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
"*=" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
"/=" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
"|=" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
"&=" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
"^=" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
"<<=" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
">>=" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
"%=" -> expressionEval.operatorModuloInplace(address, null, vmDt, value)
"==" -> expressionEval.operatorEqualsInplace(address, null, vmDt, value)
"!=" -> expressionEval.operatorNotEqualsInplace(address, null, vmDt, value)
"<" -> expressionEval.operatorLessInplace(address, null, vmDt, value.type in SignedDatatypes, value)
">" -> expressionEval.operatorGreaterInplace(address, null, vmDt, value.type in SignedDatatypes, value)
"<=" -> expressionEval.operatorLessEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
">=" -> expressionEval.operatorGreaterEqualInplace(address, null, vmDt, value.type in SignedDatatypes, value)
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
}
}
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
val value = assignment.value
val targetDt = irType(assignment.target.type)
return when (assignment.operator) {
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, targetDt, value)
"/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
"|=" -> expressionEval.operatorOrInplace(null, symbol, targetDt, value)
"&=" -> expressionEval.operatorAndInplace(null, symbol, targetDt, value)
"^=" -> expressionEval.operatorXorInplace(null, symbol, targetDt, value)
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value)
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
"%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value)
"==" -> expressionEval.operatorEqualsInplace(null, symbol, targetDt, value)
"!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, value)
"<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
"<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, value.type in SignedDatatypes, value)
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, null, symbol)
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
}
}
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
val value: PtExpression
if(origAssign.operator in PrefixOperators) {
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
value.add(origAssign.value)
} else {
require(origAssign.operator.endsWith('='))
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
val operator = when(origAssign.operator) {
in ComparisonOperators -> origAssign.operator
else -> {
require(origAssign.operator.endsWith('='))
origAssign.operator.dropLast(1)
}
}
value = PtBinaryExpression(operator, origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
value.add(left)
value.add(origAssign.value)
@@ -117,28 +84,161 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return translateRegularAssign(normalAssign)
}
private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
val code= IRCodeChunk(null, null)
private fun inplacePrefix(operator: String, symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType): Result<IRCodeChunks, NotImplementedError> {
if(operator=="+")
return Ok(emptyList())
if(array!=null)
return Ok(inplacePrefixArray(operator, array))
val result = mutableListOf<IRCodeChunkBase>()
if(constAddress==null && memory!=null) {
val register = codeGen.registers.nextFree()
val tr = expressionEval.translateExpression(memory.address)
addToResult(result, tr, tr.resultReg, -1)
when(operator) {
"-" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, vmDt, reg1=register, reg2=tr.resultReg)
it += IRInstruction(Opcode.NEG, vmDt, reg1=register)
it += IRInstruction(Opcode.STOREI, vmDt, reg1=register, reg2=tr.resultReg)
}
}
"~" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, vmDt, reg1=register, reg2=tr.resultReg)
it += IRInstruction(Opcode.INV, vmDt, reg1=register)
it += IRInstruction(Opcode.STOREI, vmDt, reg1=register, reg2=tr.resultReg)
}
}
}
} else {
when (operator) {
"-" -> addInstr(result, IRInstruction(Opcode.NEGM, vmDt, address = constAddress, labelSymbol = symbol), null)
"~" -> addInstr(result, IRInstruction(Opcode.INVM, vmDt, address = constAddress, labelSymbol = symbol), null)
// TODO: in boolean branch, how is 'not' handled here?
else -> throw AssemblyError("weird prefix operator")
}
}
return Ok(result)
}
private fun inplacePrefixArray(operator: String, array: PtArrayIndexer): IRCodeChunks {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val result = mutableListOf<IRCodeChunkBase>()
val vmDt = irType(array.type)
val constIndex = array.index.asConstInteger()
fun loadIndex(): Int {
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
if(!array.splitWords && eltSize>1)
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
return tr.resultReg
}
if(array.splitWords) {
// handle split LSB/MSB arrays
when(operator) {
"+" -> { }
"-" -> {
val skipCarryLabel = codeGen.createLabelName()
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null)
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel)
} else {
val indexReg = loadIndex()
val registerLsb = codeGen.registers.nextFree()
val registerMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb)
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0)
it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel)
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb)
}
result += IRCodeChunk(skipCarryLabel, null).also {
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
}
}
}
"~" -> {
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
} else {
val indexReg = loadIndex()
val register = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
}
}
}
else -> throw AssemblyError("weird prefix operator")
}
return result
}
// normal array.
when(operator) {
"+" -> { }
"-" -> {
code += if(address!=null)
IRInstruction(Opcode.NEGM, vmDt, address = address)
else
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
} else {
val indexReg = loadIndex()
val register = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
}
}
}
"~" -> {
val regMask = codeGen.registers.nextFree()
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = mask)
code += if(address!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = address)
else
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
} else {
val indexReg = loadIndex()
val register = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
}
}
}
"not" -> {
// TODO: in boolean branch, is 'not' handled ok like this?
val register = codeGen.registers.nextFree()
if(constIndex!=null) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
}
} else {
val indexReg = loadIndex()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
}
}
}
else -> throw AssemblyError("weird prefix operator")
}
return listOf(code)
return result
}
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
@@ -202,36 +302,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val variable = targetArray.variable.name
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
if(targetArray.usesPointerVariable) {
if(itemsize!=1)
throw AssemblyError("non-array var indexing requires bytes dt")
if(targetArray.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
val tr = expressionEval.translateExpression(targetArray.index)
val idxReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
val code = IRCodeChunk(null, null)
if(zero) {
// there's no STOREZIX instruction
valueRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
}
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
result += code
return result
}
val fixedIndex = constIntValue(targetArray.index)
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
if(zero) {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
}
result += chunk
} else {
@@ -251,7 +332,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
}
result += chunk
} else {
@@ -266,12 +347,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
}
result += chunk
} else {
@@ -302,18 +383,47 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg)
}
}
} else {
if(targetMemory.address is PtNumber) {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
val constAddress = targetMemory.address as? PtNumber
if(constAddress!=null) {
addInstr(result, IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=constAddress.number.toInt()), null)
return result
}
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
// STOREIX only works with byte index.
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val offsetReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
}
return result
}
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
expressionEval.translateExpression(offsetTypecast!!.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return result
}
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg), null)
return result
}
return result
@@ -20,12 +20,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"pop" -> funcPop(call)
"popw" -> funcPopw(call)
"push" -> funcPush(call)
"pushw" -> funcPushw(call)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> funcCallfar(call)
"call" -> funcCall(call)
"msb" -> funcMsb(call)
"lsb" -> funcLsb(call)
"memory" -> funcMemory(call)
@@ -44,10 +41,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"reverse" -> funcReverse(call)
"setlsb" -> funcSetLsbMsb(call, false)
"setmsb" -> funcSetLsbMsb(call, true)
"rol" -> funcRolRor(Opcode.ROXL, call)
"ror" -> funcRolRor(Opcode.ROXR, call)
"rol2" -> funcRolRor(Opcode.ROL, call)
"ror2" -> funcRolRor(Opcode.ROR, call)
"rol" -> funcRolRor(call)
"ror" -> funcRolRor(call)
"rol2" -> funcRolRor(call)
"ror2" -> funcRolRor(call)
"prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
@@ -71,6 +68,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
private fun funcCall(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val addressTr = exprGen.translateExpression(call.args[0])
addToResult(result, addressTr, addressTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.CALLI, reg1 = addressTr.resultReg), null)
if(call.void)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else
return ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
}
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
@@ -140,6 +148,31 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val result = mutableListOf<IRCodeChunkBase>()
val lengthReg = codeGen.registers.nextFree()
if(arrayName.type in SplitWordArrayTypes) {
// any(lsb-array) or any(msb-array)
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
addToResult(result, trLsb, trLsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trLsb.resultReg)
val shortcircuitLabel = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = trLsb.resultReg, immediate = 0)
it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcircuitLabel)
it += IRInstruction(Opcode.PREPARECALL, immediate = 2)
}
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
addToResult(result, trMsb, trMsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trMsb.resultReg)
addInstr(result, IRInstruction(Opcode.ORR, IRDataType.BYTE, reg1=trLsb.resultReg, reg2=trMsb.resultReg), null)
result += IRCodeChunk(shortcircuitLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, trLsb.resultReg, -1)
}
val syscall =
when (arrayName.type) {
DataType.ARRAY_UB,
@@ -149,11 +182,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type")
}
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0])
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
@@ -162,6 +193,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
if(arrayName.type in SplitWordArrayTypes) {
// this is a bit complicated to calculate.... have to check all recombined (lsb,msb) words for $0000
TODO("all(split words $arrayName)")
}
val syscall =
when(arrayName.type) {
DataType.ARRAY_UB,
@@ -173,7 +210,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0])
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
@@ -223,14 +260,21 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val vmDt = irType(call.type)
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
if(tr.dt==IRDataType.FLOAT) {
addToResult(result, tr, -1, tr.resultFpReg)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, fpReg1 = tr.resultFpReg)
}
} else {
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, reg2 = tr.resultReg)
}
}
return ExpressionCodeResult(result, vmDt, resultReg, -1)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@@ -262,64 +306,40 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
else -> throw AssemblyError("invalid dt for sqrt")
else -> throw AssemblyError("invalid dt")
}
}
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val code = IRCodeChunk(null, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return ExpressionCodeResult(result, IRDataType.BYTE, reg, -1)
}
private fun funcPopw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val code = IRCodeChunk(null, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return ExpressionCodeResult(result, IRDataType.WORD, reg, -1)
}
private fun funcPush(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcPushw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val lengthReg = codeGen.registers.nextFree()
val result = mutableListOf<IRCodeChunkBase>()
if(arrayName.type in SplitWordArrayTypes) {
// reverse the lsb and msb arrays both, independently
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
addToResult(result, trLsb, trLsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), null)
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
addToResult(result, trMsb, trMsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
val syscall =
when(arrayName.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw IllegalArgumentException("weird type to reverse")
}
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0])
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
@@ -341,7 +361,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(call.args[0])
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
@@ -595,7 +615,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
return exprGen.translateExpression(call.args.single())
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
// TODO to be more strict, maybe we *should* introduce a new result register that is of type .b?
// ....To be more strict, maybe we should introduce a new result register that is of type .b?
}
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@@ -603,22 +623,58 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg)
}
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
val vmDt = irType(call.args[0].type)
private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(opcode, vmDt, reg1 = tr.resultReg)
val arg = call.args[0]
val vmDt = irType(arg.type)
val opcodeMemAndReg = when(call.name) {
"rol" -> Opcode.ROXLM to Opcode.ROXL
"ror" -> Opcode.ROXRM to Opcode.ROXR
"rol2" -> Opcode.ROLM to Opcode.ROL
"ror2" -> Opcode.RORM to Opcode.ROR
else -> throw AssemblyError("wrong func")
}
result += assignRegisterTo(call.args[0], tr.resultReg)
val ident = arg as? PtIdentifier
if(ident!=null) {
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = ident.name), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
val memAddr = (arg as? PtMemoryByte)?.address?.asConstInteger()
if(memAddr!=null) {
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, address = memAddr), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
val arr = (arg as? PtArrayIndexer)
val index = arr?.index?.asConstInteger()
if(arr!=null && index!=null) {
val variable = arr.variable.name
val itemsize = codeGen.program.memsizer.memorySize(arr.type)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
val opcode = opcodeMemAndReg.second
val saveCarry = opcode in OpcodesThatDependOnCarry && !arg.isSimple()
if(saveCarry)
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
val tr = exprGen.translateExpression(arg)
addToResult(result, tr, tr.resultReg, -1)
if(saveCarry)
addInstr(result, IRInstruction(Opcode.POPST), null)
addInstr(result, IRInstruction(opcode, vmDt, reg1 = tr.resultReg), null)
if(saveCarry)
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
result += assignRegisterTo(arg, tr.resultReg)
if(saveCarry)
addInstr(result, IRInstruction(Opcode.POPST), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
@@ -649,7 +705,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
is PtArrayIndexer -> {
require(!target.usesPointerVariable)
if(target.splitWords) {
// lsb/msb in split arrays, element index 'size' is always 1
val constIndex = target.index.asConstInteger()
File diff suppressed because it is too large. Load diff
@@ -18,7 +18,6 @@ class IRCodeGen(
private val expressionEval = ExpressionGen(this)
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
private val assignmentGen = AssignmentGen(this, expressionEval)
private var irSymbolTable: IRSymbolTable = IRSymbolTable(null)
internal val registers = RegisterPool()
fun generate(): IRProgram {
@@ -26,7 +25,7 @@ class IRCodeGen(
moveAllNestedSubroutinesToBlockScope()
verifyNameScoping(program, symbolTable)
irSymbolTable = IRSymbolTable(symbolTable)
val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable)
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
// collect global variables initializers
@@ -144,19 +143,10 @@ class IRCodeGen(
(idx, instr) ->
val symbolExpr = instr.labelSymbol
if(symbolExpr!=null) {
val symbol: String
val index: UInt
if('+' in symbolExpr) {
val operands = symbolExpr.split('+', )
symbol = operands[0]
index = operands[1].toUInt()
} else {
symbol = symbolExpr
index = 0u
}
val target = symbolTable.flat[symbol]
val index = instr.labelSymbolOffset ?: 0
val target = symbolTable.flat[symbolExpr]
if (target is StMemVar) {
replacements.add(Triple(chunk, idx, target.address+index))
replacements.add(Triple(chunk, idx, target.address+index.toUInt()))
}
}
}
@@ -316,7 +306,7 @@ class IRCodeGen(
}
addInstr(result, branchIns, null)
} else {
val label = if(goto.generatedLabel!=null) goto.generatedLabel else goto.identifier!!.name
val label = goto.identifier!!.name
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
@@ -485,8 +475,8 @@ class IRCodeGen(
val tmpRegLsb = registers.nextFree()
val tmpRegMsb = registers.nextFree()
val concatReg = registers.nextFree()
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_msb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
}
@@ -994,8 +984,6 @@ class IRCodeGen(
}
it += if (goto.address != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
else if (goto.generatedLabel != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.generatedLabel)
else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
}
@@ -1016,8 +1004,6 @@ class IRCodeGen(
private fun branchInstr(goto: PtJump, branchOpcode: Opcode) = if (goto.address != null)
IRInstruction(branchOpcode, address = goto.address?.toInt())
else if (goto.generatedLabel != null)
IRInstruction(branchOpcode, labelSymbol = goto.generatedLabel)
else
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
@@ -1031,7 +1017,8 @@ class IRCodeGen(
val condition = ifElse.condition as PtBinaryExpression
val leftTr = expressionEval.translateExpression(condition.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val requireCompareZero = leftTr.lastInstruction().opcode !in OpcodesThatSetStatusbits
val lastInstruction = leftTr.chunks.last().instructions.lastOrNull()
val requireCompareZero = lastInstruction?.opcode !in OpcodesThatSetStatusbits
when(condition.operator) {
"==" -> {
if(requireCompareZero)
@@ -1053,8 +1040,6 @@ class IRCodeGen(
}
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
else if (goto.generatedLabel != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.generatedLabel), null)
else
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
}
@@ -1102,8 +1087,6 @@ class IRCodeGen(
}
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
else if (goto.generatedLabel != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.generatedLabel), null)
else
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
}
@@ -1161,8 +1144,6 @@ class IRCodeGen(
} else {
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
else if (goto.generatedLabel != null)
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
else
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
}
@@ -1461,27 +1442,26 @@ class IRCodeGen(
when(postIncrDecr.operator) {
"++" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
result += IRCodeChunk(skipLabel, null)
}
"--" -> {
val valueReg=registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
result += IRCodeChunk(skipLabel, null).also {
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
}
}
else -> throw AssemblyError("weird operator")
}
} else {
val arrayLength = symbolTable.getLength(array.variable.name)
val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
val incReg = registers.nextFree()
@@ -1489,28 +1469,28 @@ class IRCodeGen(
when(postIncrDecr.operator) {
"++" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
}
result += IRCodeChunk(skipLabel, null)
}
"--" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
}
result += IRCodeChunk(skipLabel, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
}
}
else -> throw AssemblyError("weird operator")
@@ -1536,38 +1516,60 @@ class IRCodeGen(
if(ident!=null) {
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
} else if(memory!=null) {
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
} else {
val tr = expressionEval.translateExpression(memory.address)
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
val constAddress = memory.address as? PtNumber
if(constAddress!=null) {
addInstr(result, IRInstruction(operationMem, irDt, address = constAddress.number.toInt()), null)
return result
}
val ptrWithOffset = memory.address as? PtBinaryExpression
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
// LOADIX only works with byte index.
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val offsetReg = registers.nextFree()
val incReg = registers.nextFree()
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=incReg, reg2=offsetReg, labelSymbol = ptrName)
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=incReg, reg2=offsetReg, labelSymbol = ptrName)
}
return result
}
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
expressionEval.translateExpression(offsetTypecast!!.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
result += IRCodeChunk(null, null).also {
val incReg = registers.nextFree()
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=incReg, reg2=tr.resultReg, labelSymbol = ptrName)
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=incReg, reg2=tr.resultReg, labelSymbol = ptrName)
}
return result
}
val tr = expressionEval.translateExpression(memory.address)
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
val incReg = registers.nextFree()
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
}
return result
} else if (array!=null) {
val variable = array.variable.name
val itemsize = program.memsizer.memorySize(array.type)
val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val indexReg = registers.nextFree()
val dataReg = registers.nextFree()
if(array.usesPointerVariable) {
// we don't have an indirect dec/inc so do it via an intermediate register
result += IRCodeChunk(null,null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = offset)
it += IRInstruction(Opcode.LOADIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
it += IRInstruction(operationRegister, irDt, reg1=dataReg)
it += IRInstruction(Opcode.STOREIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
}
} else {
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = "$variable+$offset"), null)
}
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize), null)
} else {
val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
@@ -1575,15 +1577,9 @@ class IRCodeGen(
result += multiplyByConst(IRDataType.BYTE, indexTr.resultReg, itemsize)
result += IRCodeChunk(null, null).also {
val incReg = registers.nextFree()
if(array.usesPointerVariable) {
it += IRInstruction(Opcode.LOADIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
it += IRInstruction(operationRegister, irDt, reg1=incReg)
it += IRInstruction(Opcode.STOREIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
} else {
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
it += IRInstruction(operationRegister, irDt, reg1=incReg)
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
}
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
it += IRInstruction(operationRegister, irDt, reg1=incReg)
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
}
}
} else
@@ -1628,9 +1624,7 @@ class IRCodeGen(
if(jump.address!=null) {
chunk += IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
} else {
if (jump.generatedLabel != null)
chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
else if (jump.identifier != null) {
if (jump.identifier != null) {
val symbol = symbolTable.lookup(jump.identifier!!.name)
if(symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR) {
val jumpReg = registers.nextFree()
@@ -1674,7 +1668,15 @@ class IRCodeGen(
}
private fun translate(block: PtBlock): IRBlock {
val irBlock = IRBlock(block.name, block.address, block.library, block.forceOutput, translate(block.alignment), block.position) // no use for other attributes yet?
val irBlock = IRBlock(block.name, block.library,
IRBlock.Options(
block.options.address,
block.options.forceOutput,
block.options.noSymbolPrefixing,
block.options.veraFxMuls,
block.options.ignoreUnused,
translate(block.options.alignment)
), block.position)
for (child in block.children) {
when(child) {
is PtNop -> { /* nothing */ }
@@ -1722,7 +1724,7 @@ class IRCodeGen(
is PtLabel -> {
irBlock += IRCodeChunk(child.name, null)
}
else -> TODO("weird child node $child")
else -> TODO("weird block child node $child")
}
}
return irBlock
@@ -1746,7 +1748,7 @@ class IRCodeGen(
private var labelSequenceNumber = 0
internal fun createLabelName(): String {
labelSequenceNumber++
return "prog8_label_gen_$labelSequenceNumber"
return "label_gen_$labelSequenceNumber"
}
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
@@ -46,7 +46,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
.map { IndexedValue(it.index, it.value) }
val changed = removeNops(chunk1, indexedInstructions)
|| replaceConcatZeroMsbWithExt(chunk1, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions)
|| removeUselessArithmetic(chunk1, indexedInstructions)
|| removeNeedlessCompares(chunk1, indexedInstructions)
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
@@ -324,14 +324,16 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
indexedInstructions.reversed().forEach { (idx, ins) ->
if(idx>0 && idx<(indexedInstructions.size-1) && ins.opcode==Opcode.CMPI && ins.immediate==0) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
chunk.instructions.removeAt(idx)
changed = true
} else if(previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
val next = indexedInstructions[idx+1].value
if(next.opcode !in arrayOf(Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTPOS, Opcode.BSTNEG)) {
if(previous.reg1==ins.reg1) {
if (previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
chunk.instructions.removeAt(idx)
changed = true
} else if (previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
val next = indexedInstructions[idx + 1].value
if (next.opcode !in OpcodesThatDependOnCarry) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
@@ -417,7 +419,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
// 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
@@ -1,7 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import prog8.intermediate.*
@@ -61,6 +60,9 @@ class IRUnusedCodeRemover(
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!block.options.ignoreUnused) {
errors.info("unused subroutine '${sub.label}'", sub.position)
}
block.children.remove(sub)
irprog.st.removeTree(sub.label)
numRemoved++
@@ -79,8 +81,8 @@ class IRUnusedCodeRemover(
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
if(!block.options.ignoreUnused) {
errors.info("unused subroutine '${sub.label}'", sub.position)
}
block.children.remove(sub)
irprog.st.removeTree(sub.label)
@@ -100,7 +102,7 @@ class IRUnusedCodeRemover(
// check if asmsub is called from another asmsub
irprog.blocks.asSequence().forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
if (block.forceOutput || block.library)
if (block.options.forceOutput || block.library)
linkedAsmSubs += sub
if (sub.asmChunk.isNotEmpty()) {
allSubs.forEach { (label, asmsub) ->
@@ -165,7 +167,13 @@ class IRUnusedCodeRemover(
it.next?.let { next -> new += next }
it.instructions.forEach { instr ->
if (instr.branchTarget == null)
instr.labelSymbol?.let { label -> allLabeledChunks[label]?.let { chunk -> new += chunk } }
instr.labelSymbol?.let { label ->
val chunk = allLabeledChunks[label.substringBeforeLast('.')]
if(chunk!=null)
new+=chunk
else
allLabeledChunks[label]?.let { new += it }
}
else
new += instr.branchTarget!!
}
@@ -212,6 +220,16 @@ class IRUnusedCodeRemover(
linkedChunks += chunk
}
// make sure that chunks that are only used as a prefix of a label, are also marked as linked
linkedChunks.toList().forEach { chunk ->
chunk.instructions.forEach {
if(it.labelSymbol!=null) {
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
}
}
}
return removeUnlinkedChunks(linkedChunks)
}
+11 -3
View File
@@ -27,11 +27,11 @@ internal object DummyStringEncoder : IStringEncoding {
}
}
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
IErrorReporter {
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false): IErrorReporter {
val errors = mutableListOf<String>()
val warnings = mutableListOf<String>()
val infos = mutableListOf<String>()
override fun err(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg"
@@ -45,6 +45,12 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text)
}
override fun info(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg"
if(text !in infos)
infos.add(text)
}
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
@@ -52,10 +58,11 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
override fun noErrors(): Boolean = errors.isEmpty()
override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
if(throwExceptionAtReportIfErrors)
finalizeNumErrors(errors.size, warnings.size)
finalizeNumErrors(errors.size, warnings.size, infos.size)
if(!keepMessagesAfterReporting) {
clear()
}
@@ -64,5 +71,6 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
fun clear() {
errors.clear()
warnings.clear()
infos.clear()
}
}
@@ -8,7 +8,7 @@ import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start")
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
chunks.forEach { sub += it }
block += sub
@@ -24,7 +24,7 @@ class TestIRPeepholeOpt: FunSpec({
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
val prog = IRProgram("test", IRSymbolTable(null), options, target)
val prog = IRProgram("test", IRSymbolTable(), options, target)
prog.addBlock(block)
prog.linkChunks()
prog.validate()
+13 -13
View File
@@ -42,7 +42,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@@ -92,7 +92,7 @@ class TestVmCodeGen: FunSpec({
program.add(block)
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block)
@@ -121,7 +121,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -184,7 +184,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -243,7 +243,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -251,7 +251,7 @@ class TestVmCodeGen: FunSpec({
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
@@ -259,7 +259,7 @@ class TestVmCodeGen: FunSpec({
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@@ -290,7 +290,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -353,7 +353,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -412,7 +412,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -420,7 +420,7 @@ class TestVmCodeGen: FunSpec({
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
@@ -428,7 +428,7 @@ class TestVmCodeGen: FunSpec({
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@@ -452,7 +452,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(romsub)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
+1
View File
@@ -27,6 +27,7 @@ dependencies {
implementation project(':codeCore')
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
}
+1
View File
@@ -11,5 +11,6 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="compilerAst" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component>
</module>
@@ -1,17 +1,19 @@
package prog8.optimizer
import prog8.ast.Program
import prog8.ast.base.ExpressionError
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.Expression
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.NumericLiteral
import prog8.code.core.DataType
import prog8.code.core.IntegerDatatypes
import prog8.code.core.Position
import kotlin.math.*
class ConstExprEvaluator {
fun evaluate(left: NumericLiteral, operator: String, right: NumericLiteral): Expression {
fun evaluate(left: NumericLiteral, operator: String, right: NumericLiteral): NumericLiteral {
try {
return when(operator) {
"+" -> plus(left, right)
@@ -19,9 +21,9 @@ class ConstExprEvaluator {
"*" -> multiply(left, right)
"/" -> divide(left, right)
"%" -> remainder(left, right)
"&" -> bitwiseand(left, right)
"|" -> bitwiseor(left, right)
"^" -> bitwisexor(left, right)
"&" -> bitwiseAnd(left, right)
"|" -> bitwiseOr(left, right)
"^" -> bitwiseXor(left, right)
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
">" -> NumericLiteral.fromBoolean(left > right, left.position)
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
@@ -30,6 +32,9 @@ class ConstExprEvaluator {
"!=" -> NumericLiteral.fromBoolean(left != right, left.position)
"<<" -> shiftedleft(left, right)
">>" -> shiftedright(left, right)
"and" -> logicalAnd(left, right)
"or" -> logicalOr(left, right)
"xor" -> logicalXor(left, right)
else -> throw FatalAstException("const evaluation for invalid operator $operator")
}
} catch (ax: FatalAstException) {
@@ -37,7 +42,7 @@ class ConstExprEvaluator {
}
}
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): Expression {
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
throw ExpressionError("cannot compute $left >> $amount", left.position)
val result =
@@ -48,52 +53,81 @@ class ConstExprEvaluator {
return NumericLiteral(left.type, result.toDouble(), left.position)
}
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): Expression {
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
throw ExpressionError("cannot compute $left << $amount", left.position)
val result = left.number.toInt().shl(amount.number.toInt())
return NumericLiteral(left.type, result.toDouble(), left.position)
// when(left.type) {
// DataType.BOOL -> result = result and 1
// DataType.UBYTE -> result = result and 255
// DataType.BYTE -> result = result.toByte().toInt()
// DataType.UWORD -> result = result and 65535
// DataType.WORD -> result = result.toShort().toInt()
// else -> { /* keep as it is */ }
// }
return NumericLiteral.optimalNumeric(result.toDouble(), left.position)
}
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type== DataType.LONG) {
if(right.type in IntegerDatatypes) {
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left ^ $right", left.position)
}
private fun bitwiseor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt()).toDouble(), left.position)
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type== DataType.LONG) {
if(right.type in IntegerDatatypes) {
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left | $right", left.position)
}
private fun bitwiseand(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt()).toDouble(), left.position)
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type== DataType.LONG) {
if(right.type in IntegerDatatypes) {
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left & $right", left.position)
}
private fun logicalAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
NumericLiteral.fromBoolean(left.asBooleanValue and right.asBooleanValue, left.position)
private fun logicalOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
NumericLiteral.fromBoolean(left.asBooleanValue or right.asBooleanValue, left.position)
private fun logicalXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
NumericLiteral.fromBoolean(left.asBooleanValue xor right.asBooleanValue, left.position)
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot add $left and $right"
return when (left.type) {
@@ -206,4 +240,154 @@ class ConstExprEvaluator {
else -> throw ExpressionError(error, left.position)
}
}
fun evaluate(call: FunctionCallExpression, program: Program): NumericLiteral? {
if(call.target.nameInSource.size!=2)
return null // likely a builtin function, or user function, these get evaluated elsewhere
val constArgs = call.args.mapNotNull { it.constValue(program) }
if(constArgs.size!=call.args.size)
return null
return when(call.target.nameInSource[0]) {
"math" -> evalMath(call, constArgs)
"floats" -> evalFloats(call, constArgs)
"string" -> evalString(call, constArgs)
else -> null
}
}
private fun evalFloats(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
val result= when(func.target.nameInSource[1]) {
"pow" -> args[0].number.pow(args[1].number)
"sin" -> sin(args[0].number)
"cos" -> cos(args[0].number)
"tan" -> tan(args[0].number)
"atan" -> atan(args[0].number)
"ln" -> ln(args[0].number)
"log2" -> log2(args[0].number)
"rad" -> args[0].number/360.0 * 2 * PI
"deg" -> args[0].number/ 2 / PI * 360.0
"round" -> round(args[0].number)
"floor" -> floor(args[0].number)
"ceil" -> ceil(args[0].number)
"minf", "min" -> min(args[0].number, args[1].number)
"maxf", "max" -> max(args[0].number, args[1].number)
"clampf", "clamp" -> {
var value = args[0].number
val minimum = args[1].number
val maximum = args[2].number
if(value<minimum)
value=minimum
if(value<maximum)
value
else
maximum
}
else -> null
}
return if(result==null)
null
else
NumericLiteral(DataType.FLOAT, result, func.position)
}
private fun evalMath(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
return when(func.target.nameInSource[1]) {
"sin8u" -> {
val value = truncate(128.0 + 127.5 * sin(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
}
"cos8u" -> {
val value = truncate(128.0 + 127.5 * cos(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
}
"sin8" -> {
val value = truncate(127.0 * sin(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
}
"cos8" -> {
val value = truncate(127.0 * cos(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
}
"sinr8u" -> {
val value = truncate(128.0 + 127.5 * sin(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
}
"cosr8u" -> {
val value = truncate(128.0 + 127.5 * cos(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
}
"sinr8" -> {
val value = truncate(127.0 * sin(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
}
"cosr8" -> {
val value = truncate(127.0 * cos(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
}
"log2" -> {
val value = truncate(log2(args.single().number))
NumericLiteral(DataType.UBYTE, value, func.position)
}
"log2w" -> {
val value = truncate(log2(args.single().number))
NumericLiteral(DataType.UWORD, value, func.position)
}
"atan2" -> {
val x1f = args[0].number
val y1f = args[1].number
val x2f = args[2].number
val y2f = args[3].number
var radians = atan2(y2f-y1f, x2f-x1f)
if(radians<0)
radians+=2*PI
NumericLiteral(DataType.UWORD, floor(radians/2.0/PI*256.0), func.position)
}
"diff" -> {
val n1 = args[0].number
val n2 = args[1].number
val value = if(n1>n2) n1-n2 else n2-n1
NumericLiteral(DataType.UBYTE, value, func.position)
}
"diffw" -> {
val n1 = args[0].number
val n2 = args[1].number
val value = if(n1>n2) n1-n2 else n2-n1
NumericLiteral(DataType.UWORD, value, func.position)
}
else -> null
}
}
private fun evalString(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
return when(func.target.nameInSource[1]) {
"isdigit" -> {
val char = args[0].number.toInt()
NumericLiteral.fromBoolean(char in 48..57, func.position)
}
"isupper" -> {
// shifted petscii has 2 ranges that contain the upper case letters... 97-122 and 193-218
val char = args[0].number.toInt()
NumericLiteral.fromBoolean(char in 97..122 || char in 193..218, func.position)
}
"islower" -> {
val char = args[0].number.toInt()
NumericLiteral.fromBoolean(char in 65..90, func.position)
}
"isletter" -> {
val char = args[0].number.toInt()
NumericLiteral.fromBoolean(char in 65..90 || char in 97..122 || char in 193..218, func.position)
}
"isspace" -> {
val char = args[0].number.toInt()
NumericLiteral.fromBoolean(char in arrayOf(32, 13, 9, 10, 141, 160), func.position)
}
"isprint" -> {
val char = args[0].number.toInt()
NumericLiteral.fromBoolean(char in 32..127 || char>=160, func.position)
}
else -> null
}
}
}
@@ -4,18 +4,23 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.ForLoop
import prog8.ast.statements.RepeatLoop
import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import kotlin.math.floor
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
class ConstantFoldingOptimizer(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
private val evaluator = ConstExprEvaluator()
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
val constAddr = addressOf.constValue(program) ?: return noModifications
return listOf(IAstModification.ReplaceNode(addressOf, constAddr, parent))
}
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// @( &thing ) --> thing (but only if thing is a byte type!)
@@ -27,6 +32,19 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
return noModifications
}
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
if(parent is Assignment) {
val iDt = parent.target.inferType(program)
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED))
if(casted.isValid) {
return listOf(IAstModification.ReplaceNode(numLiteral, casted.value!!, parent))
}
}
}
return noModifications
}
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
val result = containment.constValue(program)
if(result!=null)
@@ -79,6 +97,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
else if(expr.operator=="*" && rightconst!=null && expr.left is StringLiteral) {
// mutiply a string.
val part = expr.left as StringLiteral
if(part.value.isEmpty())
errors.warn("resulting string has length zero", part.position)
val newStr = StringLiteral(part.value.repeat(rightconst.number.toInt()), part.encoding, expr.position)
return listOf(IAstModification.ReplaceNode(expr, newStr, parent))
}
@@ -134,9 +154,8 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
}
val evaluator = ConstExprEvaluator()
// const fold when both operands are a const
// const fold when both operands are a const.
// if in a chained comparison, that one has to be desugared first though.
if(leftconst != null && rightconst != null) {
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
modifications += IAstModification.ReplaceNode(expr, result, parent)
@@ -229,51 +248,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
else if(leftBinExpr.operator=="*" && rightBinExpr.operator=="*"){
if (c1 != null && c2 != null && c1==c2) {
//(X * C) <plusmin> (Y * C) => (X <plusmin> Y) * C
val xwithy = BinaryExpression(leftBinExpr.left, expr.operator, rightBinExpr.left, expr.position)
val newExpr = BinaryExpression(xwithy, "*", c1, expr.position)
modifications += IAstModification.ReplaceNode(expr, newExpr, parent)
}
}
}
}
if(rightconst!=null && (expr.operator=="<<" || expr.operator==">>")) {
val dt = expr.left.inferType(program)
if(dt.isBytes && rightconst.number>=8) {
if(dt.istype(DataType.UBYTE)) {
val zeroUB = NumericLiteral(DataType.UBYTE, 0.0, expr.position)
modifications.add(IAstModification.ReplaceNode(expr, zeroUB, parent))
} else {
if(leftconst!=null) {
val zeroB = NumericLiteral(DataType.BYTE, 0.0, expr.position)
val minusoneB = NumericLiteral(DataType.BYTE, -1.0, expr.position)
if(leftconst.number<0.0) {
if(expr.operator=="<<")
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
else
modifications.add(IAstModification.ReplaceNode(expr, minusoneB, parent))
} else {
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
}
}
}
}
else if(dt.isWords && rightconst.number>=16) {
if(dt.istype(DataType.UWORD)) {
val zeroUW = NumericLiteral(DataType.UWORD, 0.0, expr.position)
modifications.add(IAstModification.ReplaceNode(expr, zeroUW, parent))
} else {
if(leftconst!=null) {
val zeroW = NumericLiteral(DataType.WORD, 0.0, expr.position)
val minusoneW = NumericLiteral(DataType.WORD, -1.0, expr.position)
if(leftconst.number<0.0) {
if(expr.operator=="<<")
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
else
modifications.add(IAstModification.ReplaceNode(expr, minusoneW, parent))
} else {
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
//(X * C) <plusmin> (Y * C) => (X <plusmin> Y) * C (only if types of X and Y are the same!)
val xDt = leftBinExpr.left.inferType(program)
val yDt = rightBinExpr.left.inferType(program)
if(xDt==yDt) {
val xwithy = BinaryExpression(leftBinExpr.left, expr.operator, rightBinExpr.left, expr.position)
val newExpr = BinaryExpression(xwithy, "*", c1, expr.position)
modifications += IAstModification.ReplaceNode(expr, newExpr, parent)
}
}
}
@@ -313,8 +294,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
val constvalue = functionCallExpr.constValue(program)
return if(constvalue!=null)
listOf(IAstModification.ReplaceNode(functionCallExpr, constvalue, parent))
else
noModifications
else {
val const2 = evaluator.evaluate(functionCallExpr, program)
return if(const2!=null)
listOf(IAstModification.ReplaceNode(functionCallExpr, const2, parent))
else
noModifications
}
}
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
@@ -336,14 +322,14 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
if(stepLiteral!=null) {
val stepCast = stepLiteral.cast(targetDt)
if(stepCast.isValid)
stepCast.valueOrZero()
stepCast.value!!
else
range.step
} else {
range.step
}
return RangeExpression(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
return RangeExpression(fromCast.value!!, toCast.value!!, newStep, range.position)
}
// adjust the datatype of a range expression in for loops to the loop variable.
@@ -402,7 +388,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
val cast = numval.cast(decl.datatype)
if (cast.isValid)
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
return listOf(IAstModification.ReplaceNode(numval, cast.value!!, decl))
}
}
}
@@ -419,6 +405,11 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
return noModifications
}
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
val constValue = typecast.constValue(program) ?: return noModifications
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
}
private class ShuffleOperands(val expr: BinaryExpression,
val exprOperator: String?,
val subExpr: BinaryExpression,
@@ -1,5 +1,7 @@
package prog8.optimizer
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
@@ -9,10 +11,17 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.compiler.CallGraph
// Fix up the literal value's type to match that of the vardecl
// (also check range literal operands types before they get expanded into arrays for instance)
class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
class VarConstantValueTypeAdjuster(
private val program: Program,
private val options: CompilationOptions,
private val errors: IErrorReporter
) : AstWalker() {
private val callGraph by lazy { CallGraph(program) }
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
@@ -25,19 +34,86 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
&& declConstValue.type != decl.datatype) {
// avoid silent float roundings
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
errors.err("refused rounding of float to avoid loss of precision", decl.value!!.position)
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
} else {
// cast the numeric literal to the appropriate datatype of the variable
declConstValue.linkParents(decl)
val cast = declConstValue.cast(decl.datatype)
if (cast.isValid)
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.value!!, decl))
}
}
} catch (x: UndefinedSymbolError) {
errors.err(x.message, x.position)
}
// replace variables by constants, if possible
if(options.optimize) {
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || decl.datatype !in NumericDatatypes)
return noModifications
if (decl.value != null && decl.value!!.constValue(program) == null)
return noModifications
val usages = callGraph.usages(decl)
val (writes, reads) = usages
.partition {
it is InlineAssembly // can't really tell if it's written to or only read, assume the worst
|| it.parent is AssignTarget
|| it.parent is ForLoop
|| it.parent is AddressOf
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
}
val singleAssignment =
writes.singleOrNull()?.parent?.parent as? Assignment ?: writes.singleOrNull()?.parent as? Assignment
if (singleAssignment == null) {
if (writes.isEmpty()) {
if(reads.isEmpty()) {
// variable is never used AT ALL so we just remove it altogether
if("ignore_unused" !in decl.definingBlock.options())
errors.info("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
}
val declValue = decl.value?.constValue(program)
if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
)
}
}
} else {
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
if(reads.isEmpty()) {
// variable is never used AT ALL so we just remove it altogether, including the single assignment
if("ignore_unused" !in decl.definingBlock.options())
errors.info("removing unused variable '${decl.name}'", decl.position)
return listOf(
IAstModification.Remove(decl, parent as IStatementContainer),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
}
/*
TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value)
if(writes.size==2) {
val firstAssignment = writes[0].parent as? Assignment
val secondAssignment = writes[1].parent as? Assignment
if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) {
errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position)
}
}
*/
}
return noModifications
}
@@ -196,6 +272,14 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
// This is needed because further constant optimizations depend on those.
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
override fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
val constValue = addressOf.constValue(program)
if(constValue!=null) {
return listOf(IAstModification.ReplaceNode(addressOf, constValue, parent))
}
return noModifications
}
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
// replace identifiers that refer to const value, with the value itself
// if it's a simple type and if it's not a left hand side variable
@@ -228,16 +312,20 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
}
}
return when (cval.type) {
in NumericDatatypes -> listOf(
IAstModification.ReplaceNode(
identifier,
NumericLiteral(cval.type, cval.number, identifier.position),
identifier.parent
when (cval.type) {
in NumericDatatypes -> {
if(parent is AddressOf)
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
return listOf(
IAstModification.ReplaceNode(
identifier,
NumericLiteral(cval.type, cval.number, identifier.position),
identifier.parent
)
)
)
}
in PassByReferenceDatatypes -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
else -> noModifications
else -> return noModifications
}
} catch (x: UndefinedSymbolError) {
errors.err(x.message, x.position)
@@ -245,14 +333,14 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
}
}
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// the initializer value can't refer to the variable itself (recursive definition)
if(decl.value?.referencesIdentifier(listOf(decl.name)) == true || decl.arraysize?.indexExpr?.referencesIdentifier(listOf(decl.name)) == true) {
errors.err("recursive var declaration", decl.position)
return noModifications
}
if(decl.isArray && decl.type==VarDeclType.MEMORY) {
if(decl.isArray && decl.type==VarDeclType.MEMORY && decl.value !is IdentifierReference) {
val memaddr = decl.value?.constValue(program)
if(memaddr!=null && memaddr !== decl.value) {
return listOf(IAstModification.SetExpression(
@@ -13,7 +13,6 @@ import prog8.ast.statements.Jump
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
import prog8.code.target.VMTarget
import kotlin.math.abs
import kotlin.math.log2
import kotlin.math.pow
@@ -33,8 +32,8 @@ class ExpressionSimplifier(private val program: Program,
val literal = typecast.expression as? NumericLiteral
if (literal != null) {
val newLiteral = literal.cast(typecast.type)
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
if (newLiteral.isValid && newLiteral.value!! !== literal) {
mods += IAstModification.ReplaceNode(typecast, newLiteral.value!!, parent)
}
}
@@ -66,30 +65,36 @@ class ExpressionSimplifier(private val program: Program,
)
}
if(elsepart.statements.singleOrNull() is Jump) {
val invertedCondition = invertCondition(ifElse.condition)
if(invertedCondition!=null) {
return listOf(
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
)
}
val invertedCondition = invertCondition(ifElse.condition, program)
return listOf(
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse),
IAstModification.ReplaceNode(truepart, elsepart, ifElse)
)
}
}
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 booleanCondition = ifElse.condition as? BinaryExpression
if(booleanCondition!=null && booleanCondition.operator=="&") {
val rightNum = booleanCondition.right as? NumericLiteral
if (rightNum!=null && rightNum.type==DataType.UWORD) {
if ((rightNum.number.toInt() and 0x00ff) == 0) {
// if WORD & $xx00 -> if msb(WORD) & $xx
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))
}
else if ((rightNum.number.toInt() and 0xff00) == 0) {
// if WORD & $00ff -> if lsb(WORD) & $ff
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, rightNum.number, booleanCondition.right.position)
return listOf(
IAstModification.ReplaceNode(booleanCondition.left, lsb, booleanCondition),
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
}
}
}
@@ -97,6 +102,10 @@ class ExpressionSimplifier(private val program: Program,
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val newExpr = applyAbsorptionLaws(expr)
if(newExpr!=null)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
val leftVal = expr.left.constValue(program)
val rightVal = expr.right.constValue(program)
@@ -158,8 +167,8 @@ class ExpressionSimplifier(private val program: Program,
val y = determineY(x, leftBinExpr)
if (y != null) {
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt, 1.0, y.position), y.position)
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
}
} else {
// Y*X - X -> X*(Y - 1)
@@ -168,8 +177,8 @@ class ExpressionSimplifier(private val program: Program,
val y = determineY(x, leftBinExpr)
if (y != null) {
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt, 1.0, y.position), y.position)
val newExpr = BinaryExpression(x, "*", yMinus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
val replacement = BinaryExpression(x, "*", yMinus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
}
}
} else if (rightBinExpr?.operator == "*") {
@@ -180,8 +189,8 @@ class ExpressionSimplifier(private val program: Program,
val y = determineY(x, rightBinExpr)
if (y != null) {
val yPlus1 = BinaryExpression(y, "+", NumericLiteral.optimalInteger(1, y.position), y.position)
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
}
}
}
@@ -270,7 +279,7 @@ class ExpressionSimplifier(private val program: Program,
// simplify when a term is constant and directly determines the outcome
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
val newExpr: Expression? = when (expr.operator) {
val newExpr2 = when (expr.operator) {
"|" -> {
when {
leftVal?.number==0.0 -> expr.right
@@ -315,33 +324,102 @@ class ExpressionSimplifier(private val program: Program,
}
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)
)
// boolean compare against a number -> just keep the boolean, no compare
if(expr.operator=="==" || expr.operator=="!=") {
val test = if (expr.operator == "==") rightVal.asBooleanValue else !rightVal.asBooleanValue
return if (test) {
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
} else {
listOf(IAstModification.ReplaceNode(expr, invertCondition(expr.left, program), parent))
}
}
}
if(newExpr != null)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
if(newExpr2 != null)
return listOf(IAstModification.ReplaceNode(expr, newExpr2, parent))
if (rightVal!=null && (expr.operator == "==" || expr.operator == "!=")) {
val bitwise = expr.left as? BinaryExpression
if(bitwise!=null && bitwise.operator=="&" && bitwise.inferType(program).isWords) {
val andNum = (bitwise.right as? NumericLiteral)?.number?.toInt()
if (andNum!=null) {
if ((andNum and 0x00ff) == 0) {
// (WORD & $xx00)==y -> (msb(WORD) & $xx)==y
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, (andNum shr 8).toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() shr 8).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, msb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
}
else if((andNum and 0xff00) == 0) {
// (WORD & $00xx)==y -> (lsb(WORD) & $xx)==y
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, andNum.toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() and 255).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, lsb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
}
}
}
}
return noModifications
}
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator=="not") {
// not X <compare> Y -> X <invertedcompare> Y
val binExpr = expr.expression as? BinaryExpression
if(binExpr!=null) {
val invertedOperator = invertedComparisonOperator(binExpr.operator)
if(invertedOperator!=null) {
val inverted = BinaryExpression(binExpr.left, invertedOperator, binExpr.right, binExpr.position)
return listOf(IAstModification.ReplaceNode(expr, inverted, parent))
}
}
}
return noModifications
}
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
val rightB = expr.right as? BinaryExpression
if(rightB!=null) {
// absorption laws: a or (a and b) --> a, a and (a or b) --> a
if(expr.operator=="or" && rightB.operator=="and") {
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
return expr.left
}
}
else if(expr.operator=="and" && rightB.operator=="or") {
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
return expr.left
}
}
}
val leftB = expr.left as? BinaryExpression
if(leftB!=null) {
// absorption laws: (a and b) or a --> a, (a or b) and a --> a
if(expr.operator=="or" && leftB.operator=="and") {
if(expr.right isSameAs leftB.left || expr.left isSameAs leftB.right) {
return expr.right
}
}
else if(expr.operator=="and" && leftB.operator=="or") {
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
return expr.right
}
}
}
return null
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource == listOf("lsb")) {
if(functionCallExpr.args.isEmpty())
@@ -349,10 +427,15 @@ class ExpressionSimplifier(private val program: Program,
val arg = functionCallExpr.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
// useless lsb() of byte value that was typecasted to word
if (valueDt istype DataType.UBYTE) {
// useless lsb() of ubyte value
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
}
else if (valueDt istype DataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
} else {
if(arg is IdentifierReference && arg.nameInSource.size==2
&& arg.nameInSource[0]=="cx16" && arg.nameInSource[1].uppercase() in RegisterOrPair.names) {
@@ -361,10 +444,15 @@ class ExpressionSimplifier(private val program: Program,
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program)
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
if (argDt istype DataType.UBYTE) {
// useless lsb() of byte value
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg, parent))
}
else if (argDt istype DataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
}
else if(functionCallExpr.target.nameInSource == listOf("msb")) {
@@ -405,6 +493,14 @@ class ExpressionSimplifier(private val program: Program,
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
else if(functionCallExpr.target.nameInSource == listOf("string", "contains")) {
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
if(target?.value is StringLiteral) {
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr as Node, contains, parent))
}
}
return noModifications
}
@@ -540,10 +636,25 @@ class ExpressionSimplifier(private val program: Program,
return expr.left
}
}
256.0 -> {
when(leftDt) {
DataType.UBYTE -> return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
DataType.BYTE -> return null // is either 0 or -1 we cannot tell here
DataType.UWORD, DataType.WORD -> {
// just use: msb(value) as type
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return if(leftDt==DataType.WORD)
TypecastExpression(msb, DataType.BYTE, true, expr.position)
else
TypecastExpression(msb, DataType.UWORD, true, expr.position)
}
else -> return null
}
}
in powersOfTwo -> {
if (leftDt==DataType.UBYTE || leftDt==DataType.UWORD) {
// 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),
// Signed number can't simply be bitshifted in this case (due to rounding issues for negative values), TODO is this correct???
// so we leave that as is and let the code generator deal with it.
val numshifts = log2(cv).toInt()
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
@@ -705,8 +816,8 @@ class ExpressionSimplifier(private val program: Program,
}
DataType.UWORD -> {
if (amount >= 16) {
errors.warn("shift always results in 0", expr.position)
return NumericLiteral.optimalInteger(0, expr.position)
errors.err("useless to shift by more than 15 bits", expr.position)
return null
}
else if(amount==8) {
// shift right by 8 bits is just a byte operation: msb(X) as uword
@@ -720,11 +831,21 @@ class ExpressionSimplifier(private val program: Program,
}
}
DataType.WORD -> {
// bit-shifting a signed value shouldn't be allowed by the compiler but here we go...
if (amount > 16) {
expr.right = NumericLiteral.optimalInteger(16, expr.right.position)
if (amount >= 16) {
errors.err("useless to shift by more than 15 bits", expr.position)
return null
}
else if(amount == 8) {
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
}
else if(amount > 8) {
// same as above but with residual shifts. Take care to do signed shift.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
}
}
else -> {
}
@@ -7,13 +7,13 @@ import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
val valuetypefixer = VarConstantValueTypeAdjuster(this, options, errors)
valuetypefixer.visit(this)
if(errors.noErrors()) {
valuetypefixer.applyModifications()
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
val replacer = ConstantIdentifierReplacer(this, errors, options.compTarget)
replacer.visit(this)
if (errors.noErrors()) {
replacer.applyModifications()
@@ -22,7 +22,7 @@ fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget)
if(errors.noErrors()) {
valuetypefixer.applyModifications()
val optimizer = ConstantFoldingOptimizer(this)
val optimizer = ConstantFoldingOptimizer(this, errors)
optimizer.visit(this)
while (errors.noErrors() && optimizer.applyModifications() > 0) {
optimizer.visit(this)
@@ -110,12 +110,27 @@ class StatementOptimizer(private val program: Program,
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
)
}
// switch if/else around if the else is just a jump or branch
if(ifElse.elsepart.isNotEmpty() && ifElse.elsepart.statements.size==1) {
val jump = ifElse.elsepart.statements[0]
if(jump is Jump) {
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position)
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse)
)
}
}
return noModifications
}
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
if(forLoop.body.isEmpty()) {
errors.warn("removing empty for loop", forLoop.position)
errors.info("removing empty for loop", forLoop.position)
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
} else if(forLoop.body.statements.size==1) {
val loopvar = forLoop.body.statements[0] as? VarDecl
@@ -151,7 +166,7 @@ class StatementOptimizer(private val program: Program,
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
}
else if(iterable.datatype in ArrayDatatypes) {
else if(iterable.isArray) {
val size = iterable.arraysize!!.constIndex()
if(size==1) {
// loop over array of length 1 -> just assign the single value
@@ -271,7 +286,7 @@ class StatementOptimizer(private val program: Program,
val iter = repeatLoop.iterations
if(iter!=null) {
if(repeatLoop.body.isEmpty()) {
errors.warn("empty loop removed", repeatLoop.position)
errors.info("empty loop removed", repeatLoop.position)
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
}
val iterations = iter.constValue(program)?.number?.toInt()
@@ -453,14 +468,14 @@ class StatementOptimizer(private val program: Program,
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position)
errors.warn("for boolean condition a normal if statement is preferred", whenStmt.position)
errors.info("for boolean condition a normal if statement is preferred", whenStmt.position)
return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent))
}
if(whenStmt.condition.inferType(program).isBool) {
if(whenStmt.choices.all { it.values?.size==1 }) {
if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) {
// it's a when statement on booleans that can just be replaced by an if or if..else.
// it's a when statement on booleans that can just be replaced by an if or if-else.
if (whenStmt.choices.size == 1) {
return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null)
@@ -66,14 +66,14 @@ class UnusedCodeRemover(private val program: Program,
if (block.containsNoCodeNorVars) {
if(block.name != internedStringsModuleName) {
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.warn("removing unused block '${block.name}'", block.position)
errors.info("removing unused block '${block.name}'", block.position)
}
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
}
if(callgraph.unused(block)) {
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR}) {
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.warn("removing unused block '${block.name}'", block.position)
errors.info("removing unused block '${block.name}'", block.position)
}
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
program.removeInternedStringsFromRemovedBlock(block)
@@ -90,8 +90,8 @@ class UnusedCodeRemover(private val program: Program,
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.isAsmSubroutine) {
if(callgraph.unused(subroutine)) {
if(subroutine.containsNoCodeNorVars) {
if(!subroutine.definingModule.isLibrary)
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
if("ignore_unused" !in subroutine.definingBlock.options())
errors.info("removing empty subroutine '${subroutine.name}'", subroutine.position)
val removals = mutableListOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
callgraph.calledBy[subroutine]?.let {
for(node in it)
@@ -99,8 +99,8 @@ class UnusedCodeRemover(private val program: Program,
}
return removals
}
if(!subroutine.hasBeenInlined && subroutine.definingModule===program.toplevelModule) {
errors.warn("unused subroutine '${subroutine.name}'", subroutine.position)
if(!subroutine.hasBeenInlined && "ignore_unused" !in subroutine.definingBlock.options()) {
errors.info("unused subroutine '${subroutine.name}'", subroutine.position)
}
if(!subroutine.inline) {
program.removeInternedStringsFromRemovedSubroutine(subroutine)
@@ -119,8 +119,8 @@ class UnusedCodeRemover(private val program: Program,
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
val usages = callgraph.usages(decl)
if (usages.isEmpty()) {
if(!decl.definingModule.isLibrary)
errors.warn("removing unused variable '${decl.name}'", decl.position)
if("ignore_unused" !in decl.definingBlock.options())
errors.info("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
}
else {
@@ -131,8 +131,8 @@ class UnusedCodeRemover(private val program: Program,
if(assignment!=null && assignment.origin==AssignmentOrigin.VARINIT) {
if(assignment.value.isSimple) {
// remove the vardecl
if(!decl.definingModule.isLibrary)
errors.warn("removing unused variable '${decl.name}'", decl.position)
if("ignore_unused" !in decl.definingBlock.options())
errors.info("removing unused variable '${decl.name}'", decl.position)
return listOf(
IAstModification.Remove(decl, parent as IStatementContainer),
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
@@ -144,7 +144,7 @@ class UnusedCodeRemover(private val program: Program,
val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
if(declIndex==singleUseIndex-1) {
errors.warn("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
val fcall = assignment.value as IFunctionCall
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
return listOf(
@@ -154,7 +154,7 @@ class UnusedCodeRemover(private val program: Program,
}
}
} else {
errors.warn("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
errors.info("variable '${decl.name}' is unused but has non-trivial initialization assignment. Leaving this in but maybe it can be removed altogether", decl.position)
}
}
}
+37 -7
View File
@@ -1,8 +1,9 @@
; Prog8 definitions for the Atari800XL
; Including memory registers, I/O registers, Basic and Kernal subroutines.
%option no_symbol_prefixing, ignore_unused
atari {
%option no_symbol_prefixing
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
@@ -20,8 +21,6 @@ atari {
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
asmsub init_system() {
@@ -253,9 +252,9 @@ save_SCRATCH_ZPWORD2 .word 0
}}
}
inline asmsub exit(ubyte returnvalue @A) {
asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
; TODO
; TODO where to store A as exit code?
%asm {{
ldx prog8_lib.orig_stackpointer
txs
@@ -270,11 +269,37 @@ save_SCRATCH_ZPWORD2 .word 0
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the Atari as well but their location in memory is different
; TODO
@@ -406,4 +431,9 @@ cx16 {
}}
}
sub cpu_is_65816() -> bool {
; Returns true when you have a 65816 cpu, false when it's a 6502.
return false
}
}
+1 -1
View File
@@ -7,7 +7,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 24
+55 -14
View File
@@ -1,9 +1,10 @@
; Prog8 definitions for the Commodore-128
; Including memory registers, I/O registers, Basic and Kernal subroutines.
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte
@@ -78,7 +79,7 @@ romsub $FFAB = UNTLK() clobbers(A) ; command serial
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word (use CLEARST to reset it to 0)
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
@@ -128,12 +129,19 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
}}
}
sub CLEARST() {
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
SETNAM(0, $0000)
SETLFS(15, 3, 15)
void OPEN()
CLOSE(15)
}
}
c64 {
; C64 I/O registers (VIC, SID, CIA)
%option no_symbol_prefixing
; the default locations of the 8 sprite pointers (store address of sprite / 64)
&ubyte SPRPTR0 = 2040
@@ -288,7 +296,6 @@ c64 {
c128 {
; ---- C128 specific registers ----
%option no_symbol_prefixing
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
@@ -325,7 +332,6 @@ asmsub disable_basic() clobbers(A) {
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
@@ -367,7 +373,13 @@ asmsub init_system_phase2() {
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp sys.enable_runstop_and_charsetswitch
lda #0
sta $ff00 ; default bank 15
jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch
_exitcode = *+1
lda #0 ; exit code possibly modified in exit()
rts
}}
}
@@ -748,16 +760,13 @@ _longcopy
}}
}
inline asmsub exit(ubyte returnvalue @A) {
asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
lda #0
sta $ff00 ; default bank 15
jsr cbm.CLRCHN ; reset i/o channels
jsr sys.enable_runstop_and_charsetswitch
sta cleanup_at_exit._exitcode
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller
jmp cleanup_at_exit
}}
}
@@ -768,11 +777,37 @@ _longcopy
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the C128 as well but their location in memory is different
; (because there's no room for them in the zeropage)
@@ -904,4 +939,10 @@ cx16 {
rts
}}
}
sub cpu_is_65816() -> bool {
; Returns true when you have a 65816 cpu, false when it's a 6502.
return false
}
}
+1 -1
View File
@@ -7,7 +7,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25
+4 -6
View File
@@ -1,12 +1,10 @@
; Prog8 definitions for floating point handling on the Commodore-64
%option enable_floats
%option enable_floats, no_symbol_prefixing, ignore_unused
%import floats_functions
floats {
; ---- this block contains C-64 floating point related functions ----
%option no_symbol_prefixing
const float π = 3.141592653589793
const float PI = π
const float TWOPI = 2*π
@@ -47,7 +45,7 @@ romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
romsub $bc3c = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length. Also see parse_f()
romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/23 must point to string, A=string length. Also see parse()
romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100)
@@ -159,7 +157,7 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
&uword AYINT_facmo = $64 ; $64/$65 contain result of AYINT
sub rndf() -> float {
sub rnd() -> float {
%asm {{
lda #1
jsr FREADSA
@@ -167,7 +165,7 @@ sub rndf() -> float {
}}
}
asmsub parse_f(str value @AY) -> float @FAC1 {
asmsub parse(str value @AY) -> float @FAC1 {
%asm {{
sta $22
sty $23
@@ -147,3 +147,54 @@ func_sqrt_into_FAC1 .proc
jsr MOVFM
jmp SQR
.pend
containment_floatarray .proc
; -- check if a value exists in a float array.
; parameters: FAC1: value to check, P8ZP_SCRATCH_W1: address of the word array, Y = length of array (>=1).
; returns boolean 0/1 in A.
sty P8ZP_SCRATCH_REG
ldx #<floats.floats_temp_var
ldy #>floats.floats_temp_var
jsr floats.MOVMF
ldx P8ZP_SCRATCH_REG
ldy #0
- lda floats.floats_temp_var
cmp (P8ZP_SCRATCH_W1),y
bne _firstmiss
iny
lda floats.floats_temp_var+1
cmp (P8ZP_SCRATCH_W1),y
bne _secondmiss
iny
lda floats.floats_temp_var+2
cmp (P8ZP_SCRATCH_W1),y
bne _thirdmiss
iny
lda floats.floats_temp_var+3
cmp (P8ZP_SCRATCH_W1),y
bne _fourthmiss
iny
lda floats.floats_temp_var+4
cmp (P8ZP_SCRATCH_W1),y
bne _fifthmiss
lda #1
rts
_firstmiss
iny
_secondmiss
iny
_thirdmiss
iny
_fourthmiss
iny
_fifthmiss
iny
dex
bne -
lda #0
rts
.pend
+26 -26
View File
@@ -8,7 +8,7 @@
; so that the program itself can be larger without starting to overwrite the graphics memory.
graphics {
%option no_symbol_prefixing
%option ignore_unused
const uword WIDTH = 320
const ubyte HEIGHT = 200
@@ -175,11 +175,11 @@ graphics {
if separate_pixels {
%asm {{
lda pixaddr
lda p8v_pixaddr
sta P8ZP_SCRATCH_W1
lda pixaddr+1
lda p8v_pixaddr+1
sta P8ZP_SCRATCH_W1+1
ldy separate_pixels
ldy p8v_separate_pixels
lda hline_filled_right,y
eor #255
ldy #0
@@ -193,23 +193,23 @@ graphics {
if length {
%asm {{
lda length
lda p8v_length
and #7
sta separate_pixels
lsr length+1
ror length
lsr length+1
ror length
lsr length+1
ror length
lda pixaddr
sta p8v_separate_pixels
lsr p8v_length+1
ror p8v_length
lsr p8v_length+1
ror p8v_length
lsr p8v_length+1
ror p8v_length
lda p8v_pixaddr
sta _modified+1
lda pixaddr+1
lda p8v_pixaddr+1
sta _modified+2
lda length
ora length+1
lda p8v_length
ora p8v_length+1
beq _zero
ldy length
ldy p8v_length
ldx #$ff
_modified stx $ffff ; modified
lda _modified+1
@@ -221,7 +221,7 @@ _modified stx $ffff ; modified
+ dey
bne _modified
_zero
ldy separate_pixels
ldy p8v_separate_pixels
beq hline_zero2
lda _modified+1
sta P8ZP_SCRATCH_W1
@@ -319,9 +319,9 @@ hline_zero2
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
%asm {{
stx graphics.internal_plotx
sty graphics.internal_plotx+1
jsr graphics.internal_plot
stx p8b_graphics.p8v_internal_plotx
sty p8b_graphics.p8v_internal_plotx+1
jsr p8b_graphics.p8s_internal_plot
}}
}
@@ -333,11 +333,11 @@ hline_zero2
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 {{
tay
lda internal_plotx+1
lda p8v_internal_plotx+1
sta P8ZP_SCRATCH_W2+1
lsr a ; 0
sta P8ZP_SCRATCH_W2
lda internal_plotx
lda p8v_internal_plotx
pha
and #7
tax
@@ -365,7 +365,7 @@ _ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
_plot_y_values := BITMAP_ADDRESS + 320*(range(200)>>3) + (range(200) & 7)
_plot_y_values := p8c_BITMAP_ADDRESS + 320*(range(200)>>3) + (range(200) & 7)
_y_lookup_lo .byte <_plot_y_values
_y_lookup_hi .byte >_plot_y_values
@@ -375,9 +375,9 @@ _y_lookup_hi .byte >_plot_y_values
asmsub get_y_lookup(ubyte yy @Y) -> uword @AY {
%asm {{
lda internal_plot._y_lookup_lo,y
lda p8s_internal_plot._y_lookup_lo,y
pha
lda internal_plot._y_lookup_hi,y
lda p8s_internal_plot._y_lookup_hi,y
tay
pla
rts
+53 -18
View File
@@ -1,13 +1,11 @@
; Prog8 definitions for the Commodore-64
; Including memory registers, I/O registers, Basic and Kernal subroutines.
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
@@ -82,7 +80,7 @@ romsub $FFAB = UNTLK() clobbers(A) ; command serial
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word (use CLEARST to reset it to 0)
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
@@ -128,13 +126,20 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
}}
}
sub CLEARST() {
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
SETNAM(0, $0000)
SETLFS(15, 3, 15)
void OPEN()
CLOSE(15)
}
}
c64 {
; C64 I/O registers (VIC, SID, CIA)
%option no_symbol_prefixing
; the default locations of the 8 sprite pointers (store address of sprite / 64)
; (depending on the VIC bank and screen ram address selection these can be shifted around though,
; see the two routines after this for a dynamic way of determining the correct memory location)
@@ -324,8 +329,6 @@ c64 {
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
@@ -369,7 +372,13 @@ asmsub init_system_phase2() {
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp sys.enable_runstop_and_charsetswitch
lda #31
sta $01 ; bank the kernal in
jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch
_exitcode = *+1
lda #0 ; exit code possibly modified in exit()
rts
}}
}
@@ -749,16 +758,13 @@ _longcopy
}}
}
inline asmsub exit(ubyte returnvalue @A) {
asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
lda #31
sta $01 ; bank the kernal in
jsr cbm.CLRCHN ; reset i/o channels
jsr sys.enable_runstop_and_charsetswitch
sta cleanup_at_exit._exitcode
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller
jmp cleanup_at_exit
}}
}
@@ -769,12 +775,36 @@ _longcopy
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the C64 as well but their location in memory is different
; (because there's no room for them in the zeropage in the default configuration)
@@ -908,4 +938,9 @@ cx16 {
}}
}
sub cpu_is_65816() -> bool {
; Returns true when you have a 65816 cpu, false when it's a 6502.
return false
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25
+1 -1
View File
@@ -2,7 +2,7 @@
conv {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
; ----- number conversions to decimal strings ----
+2 -1
View File
@@ -3,6 +3,7 @@
; BMX Specification: https://cx16forum.com/forum/viewtopic.php?t=6945
%import diskio
%option ignore_unused
bmx {
@@ -17,7 +18,7 @@ bmx {
uword palette_entries ; 1-256
ubyte palette_start
ubyte compression
uword palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
uword @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
uword error_message ; pointer to error message, or 0 if all ok
ubyte old_drivenumber
+23 -22
View File
@@ -12,16 +12,17 @@
%import textio
%import conv
%import string
%import syslib
diskio {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte READ_IO_CHANNEL=12
const ubyte WRITE_IO_CHANNEL=13
ubyte drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
sub reset_read_channel() {
void cbm.CHKIN(READ_IO_CHANNEL)
@@ -41,8 +42,6 @@ diskio {
if_cs
goto io_error
reset_read_channel()
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
@@ -99,8 +98,6 @@ io_error:
if_cs
goto io_error
reset_read_channel()
if_cs
goto io_error
while cbm.CHRIN()!='"' {
; skip up to entry name
@@ -184,8 +181,6 @@ io_error:
if_cs
goto io_error
reset_read_channel()
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
@@ -195,6 +190,7 @@ io_error:
return true
io_error:
cbm.CLOSE(READ_IO_CHANNEL)
lf_end_list()
return false
}
@@ -290,21 +286,20 @@ close_end:
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL) ; note: has to be Channel,x,Channel because otherwise f_seek doesn't work
void cbm.OPEN() ; open 12,8,12,"filename"
if_cc {
reset_read_channel()
if cbm.READST()==0 {
iteration_in_progress = true
reset_read_channel()
if_cc {
void cbm.CHRIN() ; read first byte to test for file not found
if not cbm.READST() {
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file
cbm.CLRCHN() ; reset default i/o channels
return true
}
void cbm.CHRIN() ; read first byte to test for file not found
if cbm.READST()==0 {
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file
cbm.CLRCHN() ; reset default i/o channels
return true
}
}
}
f_close()
cbm.CLOSE(READ_IO_CHANNEL)
return false
}
@@ -451,6 +446,7 @@ _end rts
if_cc {
return not cbm.READST()
}
cbm.CLOSE(WRITE_IO_CHANNEL)
f_close_w()
return false
}
@@ -494,6 +490,14 @@ no_mciout:
sub status() -> uword {
; -- retrieve the disk drive's current status message
str device_not_present_error = "device not present #xx"
if cbm.READST()==128 {
device_not_present_error[len(device_not_present_error)-2] = 0
conv.str_ub(drivenumber)
void string.copy(conv.string_out, &device_not_present_error+len(device_not_present_error)-2)
return device_not_present_error
}
uword messageptr = &list_filename
cbm.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15)
@@ -501,8 +505,6 @@ no_mciout:
if_cs
goto io_error
void cbm.CHKIN(15) ; use #15 as input channel
if_cs
goto io_error
while not cbm.READST() {
cx16.r5L = cbm.CHRIN()
@@ -519,7 +521,8 @@ done:
return list_filename
io_error:
list_filename = "?disk error"
cbm.CLOSE(15)
list_filename = "io error"
goto done
}
@@ -748,8 +751,6 @@ internal_vload:
if_cs
goto io_error
reset_read_channel()
if_cs
goto io_error
repeat 6 {
void cbm.CHRIN()
+12 -7
View File
@@ -2,6 +2,7 @@
; Docs: https://github.com/X16Community/x16-emulator#emulator-io-registers
emudbg {
%option ignore_unused
const uword EMU_BASE = $9fb0
@@ -13,10 +14,14 @@ emudbg {
&ubyte EMU_RECORD_GIF = EMU_BASE + 5
&ubyte EMU_RECORD_WAV = EMU_BASE + 6
&ubyte EMU_CMDKEYS_DISABLED = EMU_BASE + 7
&ubyte EMU_CPUCLK_L = EMU_BASE + 8 ; write: reset cpu clock to 0
&ubyte EMU_CPUCLK_M = EMU_BASE + 9 ; write: outputs "User debug 1: $xx"
&ubyte EMU_CPUCLK_H = EMU_BASE + 10 ; write: outputs "User debug 2: $xx"
&ubyte EMU_CPUCLK_U = EMU_BASE + 11 ; write: outputs as character to console
&ubyte EMU_CPUCLK_L = EMU_BASE + 8
&ubyte EMU_CPUCLK_M = EMU_BASE + 9
&ubyte EMU_CPUCLK_H = EMU_BASE + 10
&ubyte EMU_CPUCLK_U = EMU_BASE + 11
&ubyte EMU_CPUCLK_RESET = EMU_BASE + 8 ; write: reset cpu clock to 0
&ubyte EMU_DBGOUT1 = EMU_BASE + 9 ; write: outputs "User debug 1: $xx"
&ubyte EMU_DBGOUT2 = EMU_BASE + 10 ; write: outputs "User debug 2: $xx"
&ubyte EMU_CHROUT = EMU_BASE + 11 ; write: outputs as character to console
; 12 is unused for now
&ubyte EMU_KEYMAP = EMU_BASE + 13
&ubyte EMU_EMU_DETECT1 = EMU_BASE + 14
@@ -45,16 +50,16 @@ emudbg {
sub console_chrout(ubyte char) {
; note: make sure the character is in Iso encoding.
if is_emulator()
EMU_CPUCLK_U = char
EMU_CHROUT = char
}
sub console_value1(ubyte value) {
if is_emulator()
EMU_CPUCLK_M = value
EMU_DBGOUT1 = value
}
sub console_value2(ubyte value) {
if is_emulator()
EMU_CPUCLK_H = value
EMU_DBGOUT2 = value
}
}
+5 -7
View File
@@ -1,13 +1,11 @@
; Prog8 definitions for floating point handling on the CommanderX16
%option enable_floats
%option enable_floats, no_symbol_prefixing, ignore_unused
%import floats_functions
floats {
; ---- this block contains C-64 compatible floating point related functions ----
%option no_symbol_prefixing
const float π = 3.141592653589793
const float PI = π
const float TWOPI = 2*π
@@ -29,7 +27,7 @@ romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
romsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
romsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: not implemented in the ROM yet!
romsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead.
; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
@@ -137,11 +135,11 @@ asmsub FREADUY (ubyte value @Y) {
}}
}
asmsub parse_f(str value @AY) -> float @FAC1 {
asmsub parse(str value @AY) -> float @FAC1 {
; -- parse a string value of a number to float in FAC1
; warning: on older <R47 kernals it uses an internal BASIC routine that is ROM version dependent,
; ($deb6 is inside the routine for VAL at $deb3) See basic.sym from x16-rom
; TODO once VAL_1 is merged into the kernal properly, remove all the workarounds here
; TODO once ROM v47 is released, all the workarounds here can be removed. But probably keep the kernal VAL_1 existance check
%asm {{
ldx VAL_1
cpx #$4c ; is there an implementation in VAL_1? (test for JMP)
@@ -177,7 +175,7 @@ _msg .text 13,"?val kaputt",13,0
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT
sub rndf() -> float {
sub rnd() -> float {
%asm {{
lda #1
jmp RND_0
+47 -47
View File
@@ -24,7 +24,7 @@
gfx2 {
%option no_symbol_prefixing
%option ignore_unused
; read-only control variables:
ubyte active_mode = 0
@@ -141,8 +141,8 @@ gfx2 {
; lores 256c
position(xx, yy)
%asm {{
lda color
ldx length+1
lda p8v_color
ldx p8v_length+1
beq +
ldy #0
- sta cx16.VERA_DATA0
@@ -150,7 +150,7 @@ gfx2 {
bne -
dex
bne -
+ ldy length ; remaining
+ ldy p8v_length ; remaining
beq +
- sta cx16.VERA_DATA0
dey
@@ -180,7 +180,7 @@ gfx2 {
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
ldx xx
ldx p8v_xx
}}
repeat length {
@@ -189,8 +189,8 @@ gfx2 {
and #3
tay
lda cx16.VERA_DATA0
and gfx2.plot.mask4c,y
ora colorbits,y
and p8b_gfx2.p8s_plot.p8v_mask4c,y
ora p8v_colorbits,y
sta cx16.VERA_DATA0
cpy #%00000011 ; next vera byte?
bne ++
@@ -232,9 +232,9 @@ gfx2 {
position(xx,yy)
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
%asm {{
ldy lheight
ldy p8v_lheight
beq +
lda color
lda p8v_color
- sta cx16.VERA_DATA0
dey
bne -
@@ -260,8 +260,8 @@ gfx2 {
repeat lheight {
%asm {{
lda cx16.VERA_DATA0
and mask
ora color
and p8v_mask
ora p8v_color
sta cx16.VERA_DATA1
}}
}
@@ -534,7 +534,7 @@ gfx2 {
sta cx16.VERA_ADDR_M
lda cx16.r0
sta cx16.VERA_ADDR_L
lda color
lda p8v_color
sta cx16.VERA_DATA0
}}
}
@@ -558,9 +558,9 @@ gfx2 {
lda cx16.r0L
sta cx16.VERA_ADDR_L
ldy cx16.r2L ; xbits
lda mask4c,y
lda p8v_mask4c,y
and cx16.VERA_DATA0
ora color
ora p8v_color
sta cx16.VERA_DATA0
}}
}
@@ -605,10 +605,10 @@ gfx2 {
sta cx16.VERA_ADDR_L
lda cx16.VERA_DATA0
pha
lda xx
lda p8v_xx
and #3
tay
lda gfx2.plot.shift4c,y
lda p8b_gfx2.p8s_plot.p8v_shift4c,y
tay
pla
cpy #0
@@ -651,21 +651,21 @@ gfx2 {
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda sxl
sta stack_xl_lsb,y
lda sxl+1
sta stack_xl_msb,y
lda sxr
sta stack_xr_lsb,y
lda sxr+1
sta stack_xr_msb,y
lda sy
sta stack_y_lsb,y
lda sy+1
sta stack_y_msb,y
lda p8v_sxl
sta p8v_stack_xl_lsb,y
lda p8v_sxl+1
sta p8v_stack_xl_msb,y
lda p8v_sxr
sta p8v_stack_xr_lsb,y
lda p8v_sxr+1
sta p8v_stack_xr_msb,y
lda p8v_sy
sta p8v_stack_y_lsb,y
lda p8v_sy+1
sta p8v_stack_y_msb,y
ldy cx16.r12L
lda sdy
sta stack_dy,y
lda p8v_sdy
sta p8v_stack_dy,y
inc cx16.r12L
}}
}
@@ -679,21 +679,21 @@ gfx2 {
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda stack_xl_lsb,y
sta x1
lda stack_xl_msb,y
sta x1+1
lda stack_xr_lsb,y
sta x2
lda stack_xr_msb,y
sta x2+1
lda stack_y_lsb,y
sta yy
lda stack_y_msb,y
sta yy+1
lda p8v_stack_xl_lsb,y
sta p8v_x1
lda p8v_stack_xl_msb,y
sta p8v_x1+1
lda p8v_stack_xr_lsb,y
sta p8v_x2
lda p8v_stack_xr_msb,y
sta p8v_x2+1
lda p8v_stack_y_lsb,y
sta p8v_yy
lda p8v_stack_y_msb,y
sta p8v_yy+1
ldy cx16.r12L
lda stack_dy,y
sta dy
lda p8v_stack_dy,y
sta p8v_dy
}}
yy+=dy
}
@@ -708,7 +708,7 @@ gfx2 {
while cx16.r12L {
pop_stack()
xx = x1
; TODO: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
; possible speed optimization: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
while xx >= 0 {
if pget(xx as uword, yy as uword) != cx16.r11L
break
@@ -726,7 +726,7 @@ gfx2 {
do {
cx16.r9 = xx
; TODO: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it?
; possible speed optimization: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it?
while xx <= width-1 {
if pget(xx as uword, yy as uword) != cx16.r11L
break
@@ -852,7 +852,7 @@ skip:
position(xx,yy)
yy++
%asm {{
ldx color
ldx p8v_color
lda cx16.VERA_DATA1
sta P8ZP_SCRATCH_B1
ldy #8
+2 -2
View File
@@ -14,7 +14,7 @@
graphics {
%option no_symbol_prefixing
%option ignore_unused
romsub $feff = FB_cursor_position2() clobbers(A,X,Y) ; alias for the normal FB_cursor_position() call but reuses existing r0 and r1
@@ -158,7 +158,7 @@ graphics {
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
%asm {{
jsr cx16.FB_cursor_position
lda graphics.stroke_color
lda p8b_graphics.p8v_stroke_color
jsr cx16.FB_set_pixel
}}
}
+67 -67
View File
@@ -11,7 +11,7 @@
monogfx {
%option no_symbol_prefixing
%option ignore_unused
; read-only control variables:
uword width = 0
@@ -107,11 +107,11 @@ monogfx {
; just use 2 byte writes with shifted mask
position2(xx,yy,true)
%asm {{
ldy length
lda masked_ends,y
ldy p8v_length
lda p8v_masked_ends,y
sta cx16.r0L ; save left byte
stz P8ZP_SCRATCH_B1
lda xx
lda p8v_xx
and #7
beq +
tay
@@ -122,10 +122,10 @@ monogfx {
bne -
sta cx16.r0L ; new left byte
+
lda dont_stipple_flag
lda p8v_dont_stipple_flag
bne _dontstipple
; determine stipple pattern
lda yy
lda p8v_yy
and #1
beq +
lda #%10101010
@@ -139,7 +139,7 @@ monogfx {
and P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1
_dontstipple
lda draw
lda p8v_draw
beq _clear
lda cx16.r0L ; left byte
ora cx16.VERA_DATA1
@@ -184,34 +184,34 @@ _clear
separate_pixels = lsb(length) & 7
xx += length & $fff8
%asm {{
lsr length+1
ror length
lsr length+1
ror length
lsr length+1
ror length
lda draw
lsr p8v_length+1
ror p8v_length
lsr p8v_length+1
ror p8v_length
lsr p8v_length+1
ror p8v_length
lda p8v_draw
bne +
ldy #0 ; black
bra _loop
+ lda dont_stipple_flag
+ lda p8v_dont_stipple_flag
beq _stipple
ldy #255 ; don't stipple
bra _loop
_stipple lda yy
_stipple lda p8v_yy
and #1 ; determine stipple pattern to use
bne +
ldy #%01010101
bra _loop
+ ldy #%10101010
_loop lda length
ora length+1
_loop lda p8v_length
ora p8v_length+1
beq _done
sty cx16.VERA_DATA0
lda length
lda p8v_length
bne +
dec length+1
+ dec length
dec p8v_length+1
+ dec p8v_length
bra _loop
_done
}}
@@ -554,9 +554,9 @@ _done
if draw {
; solid color or perhaps stipple
%asm {{
lda xx
eor yy
ora dont_stipple_flag
lda p8v_xx
eor p8v_yy
ora p8v_dont_stipple_flag
and #1
}}
if_nz {
@@ -575,7 +575,7 @@ _done
sub prepare() {
%asm {{
lda xx
lda p8v_xx
and #7
pha ; xbits
}}
@@ -587,12 +587,12 @@ _done
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda xx+1
lda p8v_xx+1
sta cx16.VERA_ADDR_M
lda xx
lda p8v_xx
sta cx16.VERA_ADDR_L
ply ; xbits
lda maskbits,y
lda p8v_maskbits,y
}}
}
}
@@ -608,7 +608,7 @@ _done
sub pget(uword @zp xx, uword yy) -> ubyte {
%asm {{
lda xx
lda p8v_xx
and #7
pha ; xbits
}}
@@ -621,12 +621,12 @@ _done
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda xx+1
lda p8v_xx+1
sta cx16.VERA_ADDR_M
lda xx
lda p8v_xx
sta cx16.VERA_ADDR_L
ply ; xbits
lda plot.maskbits,y
lda p8s_plot.p8v_maskbits,y
and cx16.VERA_DATA0
beq +
lda #1
@@ -662,21 +662,21 @@ _done
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda sxl
sta stack_xl_lsb,y
lda sxl+1
sta stack_xl_msb,y
lda sxr
sta stack_xr_lsb,y
lda sxr+1
sta stack_xr_msb,y
lda sy
sta stack_y_lsb,y
lda sy+1
sta stack_y_msb,y
lda p8v_sxl
sta p8v_stack_xl_lsb,y
lda p8v_sxl+1
sta p8v_stack_xl_msb,y
lda p8v_sxr
sta p8v_stack_xr_lsb,y
lda p8v_sxr+1
sta p8v_stack_xr_msb,y
lda p8v_sy
sta p8v_stack_y_lsb,y
lda p8v_sy+1
sta p8v_stack_y_msb,y
ldy cx16.r12L
lda sdy
sta stack_dy,y
lda p8v_sdy
sta p8v_stack_dy,y
inc cx16.r12L
}}
}
@@ -690,21 +690,21 @@ _done
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda stack_xl_lsb,y
sta x1
lda stack_xl_msb,y
sta x1+1
lda stack_xr_lsb,y
sta x2
lda stack_xr_msb,y
sta x2+1
lda stack_y_lsb,y
sta yy
lda stack_y_msb,y
sta yy+1
lda p8v_stack_xl_lsb,y
sta p8v_x1
lda p8v_stack_xl_msb,y
sta p8v_x1+1
lda p8v_stack_xr_lsb,y
sta p8v_x2
lda p8v_stack_xr_msb,y
sta p8v_x2+1
lda p8v_stack_y_lsb,y
sta p8v_yy
lda p8v_stack_y_msb,y
sta p8v_yy+1
ldy cx16.r12L
lda stack_dy,y
sta dy
lda p8v_stack_dy,y
sta p8v_dy
}}
yy+=dy
}
@@ -797,7 +797,7 @@ skip:
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
%asm {{
; pre-shift the bits
lda text.xx
lda p8s_text.p8v_xx
and #7
sta P8ZP_SCRATCH_B1
ldy #0
@@ -810,9 +810,9 @@ skip:
ror P8ZP_SCRATCH_REG
dex
bne -
+ sta char_bitmap_bytes_left,y
+ sta p8v_char_bitmap_bytes_left,y
lda P8ZP_SCRATCH_REG
sta char_bitmap_bytes_right,y
sta p8v_char_bitmap_bytes_right,y
iny
cpy #8
bne --
@@ -823,7 +823,7 @@ skip:
if draw {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
- lda p8v_char_bitmap_bytes_left,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
@@ -833,7 +833,7 @@ skip:
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
- lda p8v_char_bitmap_bytes_left,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
@@ -849,7 +849,7 @@ skip:
if draw {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
- lda p8v_char_bitmap_bytes_right,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
@@ -859,7 +859,7 @@ skip:
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
- lda p8v_char_bitmap_bytes_right,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
+6 -1
View File
@@ -3,7 +3,7 @@
; The first 16 colors can be restored to their default with set_default16()
palette {
%option no_symbol_prefixing
%option ignore_unused
uword vera_palette_ptr
@@ -14,6 +14,11 @@ palette {
cx16.vpoke(1, vera_palette_ptr, msb(color))
}
sub get_color(ubyte index) -> uword {
vera_palette_ptr = $fa00+(index as uword * 2)
return mkword(cx16.vpeek(1, vera_palette_ptr+1), cx16.vpeek(1, vera_palette_ptr))
}
sub set_rgb_be(uword palette_ptr, uword num_colors) {
; 1 word per color entry, $0rgb in big endian format
vera_palette_ptr = $fa00
+9 -9
View File
@@ -1,7 +1,7 @@
%import syslib
psg {
%option no_symbol_prefixing
%option ignore_unused
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
; 00 frequency word LSB
@@ -114,10 +114,10 @@ psg {
; 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)
sys.pushw(cx16.r0)
sys.push(cx16.r1L)
sys.push(cx16.r2L)
sys.pushw(cx16.r9)
; calculate new volumes
for cx16.r1L in 0 to 15 {
when envelope_states[cx16.r1L] {
@@ -166,10 +166,10 @@ psg {
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
}
cx16.restore_vera_context()
popw(cx16.r9)
pop(cx16.r2L)
pop(cx16.r1L)
popw(cx16.r0)
cx16.r9 = sys.popw()
cx16.r2L = sys.pop()
cx16.r1L = sys.pop()
cx16.r0 = sys.popw()
return true ; run the system IRQ handler afterwards
}
+55
View File
@@ -7,6 +7,8 @@
; note: collision mask is not supported here yet.
sprites {
%option ignore_unused
const ubyte SIZE_8 = 0
const ubyte SIZE_16 = 1
const ubyte SIZE_32 = 2
@@ -39,6 +41,16 @@ sprites {
cx16.vpoke_mask(1, sprite_reg+1, %11110000, msb(addr)) ; address 16:13
}
sub get_data_ptr(ubyte spritenum) {
; -- returns the VRAM address where the sprite's bitmap data is stored
; R1 (byte) = the vera bank (0 or 1), R0 (word) = the address.
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
cx16.r0L = cx16.vpeek(1, sprite_reg)
cx16.r0H = cx16.vpeek(1, sprite_reg+1)
cx16.r1L = cx16.r0H & %00001000 !=0 ; bank
cx16.r0 <<= 5 ; address
}
sub pos(ubyte spritenum, word xpos, word ypos) {
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
cx16.vpoke(1, sprite_reg, lsb(xpos))
@@ -133,4 +145,47 @@ sprites {
sub set_palette_offset(ubyte spritenum, ubyte offset) {
cx16.vpoke_mask(1, VERA_SPRITEREGS + 7 + spritenum*$0008, %11110000, offset>>4)
}
sub set_mousepointer_hand() {
; the array below is the compressed form of this sprite image:
; 00, 16, 16, 00, 00, 00, 00, 00, 16, 16, 16, 00, 00, 00, 00, 00,
; 16, 01, 01, 16, 00, 00, 16, 16, 15, 01, 01, 16, 00, 00, 00, 00,
; 16, 01, 01, 01, 16, 16, 15, 01, 16, 15, 01, 01, 16, 00, 00, 00,
; 16, 15, 01, 01, 01, 01, 16, 15, 01, 01, 01, 01, 16, 00, 00, 00,
; 00, 16, 15, 01, 01, 01, 01, 01, 01, 01, 11, 01, 01, 16, 00, 00,
; 00, 00, 16, 15, 01, 01, 01, 01, 11, 01, 01, 11, 01, 16, 00, 00,
; 00, 00, 16, 16, 15, 01, 01, 01, 01, 11, 01, 01, 01, 16, 00, 00,
; 00, 00, 16, 12, 12, 15, 01, 01, 01, 01, 01, 01, 01, 16, 16, 00,
; 00, 00, 16, 12, 15, 15, 01, 01, 01, 01, 01, 01, 16, 01, 01, 16,
; 00, 00, 00, 16, 15, 15, 01, 01, 01, 01, 01, 01, 01, 01, 01, 16,
; 00, 00, 00, 00, 16, 15, 15, 15, 15, 15, 16, 01, 01, 15, 16, 00,
; 00, 00, 00, 00, 00, 16, 16, 16, 16, 16, 15, 01, 15, 16, 00, 00,
; 00, 00, 00, 00, 00, 00, 00, 00, 16, 12, 15, 15, 16, 00, 00, 00,
; 00, 00, 00, 00, 00, 00, 00, 00, 16, 12, 12, 16, 00, 00, 00, 00,
; 00, 00, 00, 00, 00, 00, 00, 00, 00, 16, 16, 00, 00, 00, 00, 00,
; 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
ubyte[] hand_image_lzsa = [
26, 31, 0, 16, 16, 0, 46, 16, 192, 58, 1, 1, 16, 156, 41, 15, 37,
137, 42, 1, 6, 115, 2, 43, 1, 152, 34, 4, 127, 4, 15, 11, 116, 233,
11, 37, 135, 6, 38, 7, 19, 12, 12, 67, 167, 35, 136, 237, 15, 77,
16, 157, 37, 71, 157, 47, 2, 33, 24, 9, 15, 69, 83, 66, 94, 6, 136,
77, 0, 186, 6, 69, 154, 6, 143, 70, 204, 15, 0, 191, 231, 232]
set_mousepointer_image(hand_image_lzsa, true)
}
sub set_mousepointer_image(uword data, bool compressed) {
get_data_ptr(0) ; the mouse cursor is sprite 0
if cx16.r1L==0 and cx16.r0==0
return ; mouse cursor not enabled
ubyte vbank = cx16.r1L
cx16.vaddr(vbank, cx16.r0, 0, 1)
if compressed {
void cx16.memory_decompress(data, &cx16.VERA_DATA0) ; decompress directly into vram
} else {
for cx16.r0L in 0 to 255 {
cx16.VERA_DATA0 = data[cx16.r0L]
}
}
}
}
+208 -25
View File
@@ -1,17 +1,17 @@
; Prog8 definitions for the CommanderX16
; Including memory registers, I/O registers, Basic and Kernal subroutines.
cbm {
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
; STROUT --> use txt.print
; CLEARSCR -> use txt.clear_screen
; HOMECRSR -> use txt.home or txt.plot
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip, including resetting to the default color palette
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip, including resetting to the default color palette. Note: also sets the video mode back to VGA
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, IRQ, ...)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
@@ -29,7 +29,7 @@ romsub $FFAB = UNTLK() clobbers(A) ; command serial
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word (use CLEARST to reset it to 0)
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
@@ -81,12 +81,19 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
}}
}
sub CLEARST() {
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
SETNAM(0, $0000)
SETLFS(15, 3, 15)
void OPEN()
CLOSE(15)
}
}
cx16 {
%option no_symbol_prefixing
; irq, system and hardware vectors:
&uword IERROR = $0300
&uword IMAIN = $0302
@@ -232,6 +239,7 @@ cx16 {
const uword VERA_BASE = $9F20
&ubyte VERA_ADDR_L = VERA_BASE + $0000
&ubyte VERA_ADDR_M = VERA_BASE + $0001
&uword VERA_ADDR = VERA_BASE + $0000 ; still need to do the _H separately
&ubyte VERA_ADDR_H = VERA_BASE + $0002
&ubyte VERA_DATA0 = VERA_BASE + $0003
&ubyte VERA_DATA1 = VERA_BASE + $0004
@@ -257,15 +265,19 @@ cx16 {
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
&uword VERA_L0_HSCROLL = VERA_BASE + $0010
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
&uword VERA_L0_VSCROLL = VERA_BASE + $0012
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
&uword VERA_L1_HSCROLL = VERA_BASE + $0017
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
&uword VERA_L1_VSCROLL = VERA_BASE + $0019
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
@@ -279,16 +291,21 @@ cx16 {
&ubyte VERA_FX_MULT = VERA_BASE + $000c
&ubyte VERA_FX_X_INCR_L = VERA_BASE + $0009
&ubyte VERA_FX_X_INCR_H = VERA_BASE + $000a
&uword VERA_FX_X_INCR = VERA_BASE + $0009
&ubyte VERA_FX_Y_INCR_L = VERA_BASE + $000b
&ubyte VERA_FX_Y_INCR_H = VERA_BASE + $000c
&uword VERA_FX_Y_INCR = VERA_BASE + $000b
&ubyte VERA_FX_X_POS_L = VERA_BASE + $0009
&ubyte VERA_FX_X_POS_H = VERA_BASE + $000a
&uword VERA_FX_X_POS = VERA_BASE + $0009
&ubyte VERA_FX_Y_POS_L = VERA_BASE + $000b
&ubyte VERA_FX_Y_POS_H = VERA_BASE + $000c
&uword VERA_FX_Y_POS = VERA_BASE + $000b
&ubyte VERA_FX_X_POS_S = VERA_BASE + $0009
&ubyte VERA_FX_Y_POS_S = VERA_BASE + $000a
&ubyte VERA_FX_POLY_FILL_L = VERA_BASE + $000b
&ubyte VERA_FX_POLY_FILL_H = VERA_BASE + $000c
&uword VERA_FX_POLY_FILL = VERA_BASE + $000b
&ubyte VERA_FX_CACHE_L = VERA_BASE + $0009
&ubyte VERA_FX_CACHE_M = VERA_BASE + $000a
&ubyte VERA_FX_CACHE_H = VERA_BASE + $000b
@@ -432,12 +449,70 @@ romsub $ff53 = joystick_scan() clobbers(A, X, 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
; Audio (rom bank 10)
romsub $C04B = psg_init() clobbers(A,X,Y) ; (re)init Vera PSG
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize both vera PSG and YM audio chips
; TODO: add more of the audio routines?
; X16Edit (rom bank 13/14 but you ideally should use the routine search_x16edit() to search for the correct bank)
romsub $C000 = x16edit_default() clobbers(A,X,Y)
romsub $C003 = x16edit_loadfile(ubyte firstbank @X, ubyte lastbank @Y, str filename @R0, ubyte filenameLength @R1) clobbers(A,X,Y)
romsub $C006 = x16edit_loadfile_options(ubyte firstbank @X, ubyte lastbank @Y, str filename @R0,
uword filenameLengthAndOptions @R1, uword tabstopAndWordwrap @R2,
uword disknumberAndColors @R3, uword headerAndStatusColors @R4) clobbers(A,X,Y)
; Audio (rom bank 10 - you have to activate this bank manually first! Or use callfar/JSRFAR.)
romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize both vera PSG and YM audio chips
romsub $C000 = bas_fmfreq(ubyte channel @A, uword freq @XY, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
romsub $C003 = bas_fmnote(ubyte channel @A, ubyte note @X, ubyte fracsemitone @Y, bool noretrigger @Pc) clobbers(A,X,Y) -> bool @Pc
romsub $C006 = bas_fmplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
romsub $C009 = bas_fmvib(ubyte speed @A, ubyte depth @X) clobbers(A,X,Y) -> bool @Pc
romsub $C00C = bas_playstringvoice(ubyte channel @A) clobbers(Y)
romsub $C00F = bas_psgfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y) -> bool @Pc
romsub $C012 = bas_psgnote(ubyte voice @A, ubyte note @X, ubyte fracsemitone @Y) clobbers(A,X,Y) -> bool @Pc
romsub $C015 = bas_psgwav(ubyte voice @A, ubyte waveform @X) clobbers(A,X,Y) -> bool @Pc
romsub $C018 = bas_psgplaystring(ubyte length @A, str string @XY) clobbers(A,X,Y)
romsub $C08D = bas_fmchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
romsub $C090 = bas_psgchordstring(ubyte length @A, str string @XY) clobbers(A,X,Y)
romsub $C01B = notecon_bas2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
romsub $C01E = notecon_bas2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
romsub $C021 = notecon_bas2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
romsub $C024 = notecon_fm2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
romsub $C027 = notecon_fm2midi(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
romsub $C02A = notecon_fm2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
romsub $C02D = notecon_freq2bas(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
romsub $C030 = notecon_freq2fm(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
romsub $C033 = notecon_freq2midi(uword freqHz @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
romsub $C036 = notecon_freq2psg(uword freqHz @XY) clobbers(A) -> uword @XY, bool @Pc
romsub $C039 = notecon_midi2bas(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
romsub $C03C = notecon_midi2fm(ubyte note @X) clobbers(A) -> ubyte @X, bool @Pc
romsub $C03F = notecon_midi2psg(ubyte note @X, ubyte fracsemitone @Y) clobbers(A) -> uword @XY, bool @Pc
romsub $C042 = notecon_psg2bas(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
romsub $C045 = notecon_psg2fm(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
romsub $C048 = notecon_psg2midi(uword freq @XY) clobbers(A) -> ubyte @X, ubyte @Y, bool @Pc
romsub $C04B = psg_init() clobbers(A,X,Y) ; (re)init Vera PSG
romsub $C04E = psg_playfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
romsub $C051 = psg_read(ubyte offset @X, bool cookedVol @Pc) clobbers(Y) -> ubyte @A
romsub $C054 = psg_setatten(ubyte voice @A, ubyte attenuation @X) clobbers(A,X,Y)
romsub $C057 = psg_setfreq(ubyte voice @A, uword freq @XY) clobbers(A,X,Y)
romsub $C05A = psg_setpan(ubyte voice @A, ubyte panning @X) clobbers(A,X,Y)
romsub $C05D = psg_setvol(ubyte voice @A, ubyte volume @X) clobbers(A,X,Y)
romsub $C060 = psg_write(ubyte value @A, ubyte offset @X) clobbers(Y)
romsub $C0A2 = psg_write_fast(ubyte value @A, ubyte offset @X) clobbers(Y)
romsub $C093 = psg_getatten(ubyte voice @A) clobbers(Y) -> ubyte @X
romsub $C096 = psg_getpan(ubyte voice @A) clobbers(Y) -> ubyte @X
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
romsub $C069 = ym_loadpatch(ubyte channel @A, uword patchOrAddress @XY, bool what @Pc) clobbers(A,X,Y)
romsub $C06C = ym_loadpatchlfn(ubyte channel @A, ubyte lfn @X) clobbers(X,Y) -> ubyte @A, bool @Pc
romsub $C06F = ym_playdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
romsub $C072 = ym_playnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y, bool notrigger @Pc) clobbers(A,X,Y) -> bool @Pc
romsub $C075 = ym_setatten(ubyte channel @A, ubyte attenuation @X) clobbers(Y) -> bool @Pc
romsub $C078 = ym_setdrum(ubyte channel @A, ubyte note @X) clobbers(A,X,Y) -> bool @Pc
romsub $C07B = ym_setnote(ubyte channel @A, ubyte kc @X, ubyte kf @Y) clobbers(A,X,Y) -> bool @Pc
romsub $C07E = ym_setpan(ubyte channel @A, ubyte panning @X) clobbers(A,X,Y) -> bool @Pc
romsub $C081 = ym_read(ubyte register @X, bool cooked @Pc) clobbers(Y) -> ubyte @A, bool @Pc
romsub $C084 = ym_release(ubyte channel @A) clobbers(A,X,Y) -> bool @Pc
romsub $C087 = ym_trigger(ubyte channel @A, bool noRelease @Pc) clobbers(A,X,Y) -> bool @Pc
romsub $C08A = ym_write(ubyte value @A, ubyte register @X) clobbers(Y) -> bool @Pc
romsub $C099 = ym_getatten(ubyte channel @A) clobbers(Y) -> ubyte @X
romsub $C09C = ym_getpan(ubyte channel @A) clobbers(Y) -> ubyte @X
romsub $C0A5 = ym_get_chip_type() clobbers(X) -> ubyte @A
asmsub set_screen_mode(ubyte mode @A) clobbers(A,X,Y) -> bool @Pc {
@@ -545,7 +620,6 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
; note: inefficient when reading multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
sty cx16.VERA_ADDR_M
stx cx16.VERA_ADDR_L
@@ -559,7 +633,6 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
; with optional auto increment or decrement of 1.
; Note that the vaddr_autoincr() and vaddr_autodecr() routines allow to set all possible strides, not just 1.
%asm {{
and #1
pha
lda cx16.r1
and #1
@@ -615,8 +688,7 @@ asmsub vaddr_autoincr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword
sta cx16.VERA_ADDR_H
rts
_setup and #1
sta P8ZP_SCRATCH_REG
_setup sta P8ZP_SCRATCH_REG
lda cx16.r1
and #1
sta cx16.VERA_CTRL
@@ -686,7 +758,6 @@ asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
@@ -702,7 +773,6 @@ asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
@@ -720,7 +790,6 @@ asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
@@ -738,7 +807,6 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
@@ -757,7 +825,6 @@ asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @
%asm {{
sty P8ZP_SCRATCH_B1
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
@@ -1057,14 +1124,100 @@ inline asmsub disable_irq_handlers() {
}}
}
sub search_x16edit() -> ubyte {
; -- Search the rom bank that contains x16edit. Returns bank number, or 255 if not found.
cx16.r0L = cx16.getrombank()
sys.set_irqd()
str @shared signature = petscii:"x16edit"
for cx16.r1L in 31 downto 0 {
cx16.rombank(cx16.r1L)
cx16.r2 = $fff0
%asm {{
ldy #0
- lda signature,y
cmp (cx16.r2),y
bne +
iny
cpy #7
bne -
sec
bcs ++
+ clc
+
}}
if_cs {
cx16.rombank(cx16.r0L)
sys.clear_irqd()
return cx16.r1L
}
}
sys.clear_irqd()
return 255
}
asmsub cpu_is_65816() -> bool @A {
; Returns true when you have a 65816 cpu, false when it's a 6502.
%asm {{
php
clv
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
bvc +
lda #1
plp
rts
+ lda #0
plp
rts
}}
}
sub set_program_args(uword args_ptr, ubyte args_size) {
; Set the inter-program arguments.
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
; see https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2007%20-%20Memory%20Map.md#bank-0
sys.push(getrambank())
rambank(0)
sys.memcopy(args_ptr, $bf00, args_size)
if args_size<255
@($bf00+args_size) = 0
rambank(sys.pop())
}
asmsub get_program_args(uword buffer @R0, ubyte buf_size @R1, bool binary @Pc) {
; Retrieve the inter-program arguments. If binary=false, it treats them as a string (stops copying at first zero).
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
; see https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2007%20-%20Memory%20Map.md#bank-0
%asm {{
lda #0
rol a
sta P8ZP_SCRATCH_REG
lda $00
pha
stz $00
stz P8ZP_SCRATCH_W1
lda #$bf
sta P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta (cx16.r0),y
beq +
_continue iny
cpy cx16.r1L ; max size?
bne -
beq ++
+ lda P8ZP_SCRATCH_REG ; binary?
bne _continue
+ pla
sta $00
}}
}
}
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub init_system() {
@@ -1135,6 +1288,9 @@ asmsub cleanup_at_exit() {
lda #4
sta $01 ; rom bank 4 (basic)
stz $2d ; hack to reset machine code monitor bank to 0
jsr cbm.CLRCHN ; reset i/o channels
_exitcode = *+1
lda #0 ; exit code possibly modified in exit()
rts
}}
}
@@ -1498,10 +1654,10 @@ save_SCRATCH_ZPWORD2 .word 0
asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
jsr cbm.CLRCHN ; reset i/o channels
sta cleanup_at_exit._exitcode
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller
jmp cleanup_at_exit
}}
}
@@ -1511,4 +1667,31 @@ save_SCRATCH_ZPWORD2 .word 0
ldy #>prog8_program_end
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
phy
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
ply
pla
}}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 80
const ubyte DEFAULT_HEIGHT = 60
+59 -12
View File
@@ -1,10 +1,10 @@
; Experimental Vera FX support.
; Somewhat experimental Vera FX support.
; Docs:
; https://github.com/X16Community/x16-docs/blob/master/VERA%20FX%20Reference.md
; https://docs.google.com/document/d/1q34uWOiM3Be2pnaHRVgSdHySI-qsiQWPTo_gfE54PTg/edit
verafx {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
sub available() -> bool {
; returns true if Vera FX is available (Vera V0.3.1 or later), false if not.
@@ -21,7 +21,7 @@ verafx {
return cx16.r1L
}
sub clear(ubyte vbank, uword vaddr, ubyte data, uword amountof32bits) {
sub clear(ubyte vbank, uword vaddr, ubyte data, uword num_longwords) {
; use cached 4-byte write to quickly clear a portion of the video memory to a given byte value
; this routine is around 3 times faster as gfx2.clear_screen()
cx16.VERA_CTRL = 0
@@ -37,24 +37,24 @@ verafx {
cx16.VERA_FX_MULT = 0
cx16.VERA_FX_CTRL = %01000000 ; cache write enable
if (amountof32bits & %1111110000000011) == 0 {
repeat lsb(amountof32bits >> 2)
if (num_longwords & %1111110000000011) == 0 {
repeat lsb(num_longwords >> 2)
unroll 4 cx16.VERA_DATA0=0 ; write 4*4 bytes at a time, unrolled
}
else if (amountof32bits & %1111111000000001) == 0 {
repeat lsb(amountof32bits >> 1)
else if (num_longwords & %1111111000000001) == 0 {
repeat lsb(num_longwords >> 1)
unroll 2 cx16.VERA_DATA0=0 ; write 2*4 bytes at a time, unrolled
}
else if (lsb(amountof32bits) & 3) == 0 {
repeat amountof32bits >> 2
else if (lsb(num_longwords) & 3) == 0 {
repeat num_longwords >> 2
unroll 4 cx16.VERA_DATA0=0 ; write 4*4 bytes at a time, unrolled
}
else if (lsb(amountof32bits) & 1) == 0 {
repeat amountof32bits >> 1
else if (lsb(num_longwords) & 1) == 0 {
repeat num_longwords >> 1
unroll 2 cx16.VERA_DATA0=0 ; write 2*4 bytes at a time, unrolled
}
else {
repeat amountof32bits
repeat num_longwords
cx16.VERA_DATA0=0 ; write 4 bytes at a time
}
@@ -62,6 +62,53 @@ verafx {
cx16.VERA_CTRL = 0
}
sub copy(ubyte srcbank, uword srcaddr, ubyte tgtbank, uword tgtaddr, uword num_longwords) {
; use cached 4-byte writes to quickly copy a portion of the video memory to somewhere else
; this routine is about 50% faster as a plain byte-by-byte copy
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = srcbank | %00010000 ; source: 1-byte increment
cx16.VERA_ADDR_M = msb(srcaddr)
cx16.VERA_ADDR_L = lsb(srcaddr)
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = tgtbank | %00110000 ; target: 4-byte increment
cx16.VERA_ADDR_M = msb(tgtaddr)
cx16.VERA_ADDR_L = lsb(tgtaddr)
cx16.VERA_CTRL = 2<<1 ; dcsel = 2
cx16.VERA_FX_MULT = 0
cx16.VERA_FX_CTRL = %01100000 ; cache write enable + cache fill enable
cx16.r0 = num_longwords
if (cx16.r0L & 1) == 0 {
repeat cx16.r0>>1 {
%asm {{
lda cx16.VERA_DATA1 ; fill cache with 4 source bytes...
lda cx16.VERA_DATA1
lda cx16.VERA_DATA1
lda cx16.VERA_DATA1
stz cx16.VERA_DATA0 ; write 4 bytes at once.
lda cx16.VERA_DATA1 ; fill cache with 4 source bytes...
lda cx16.VERA_DATA1
lda cx16.VERA_DATA1
lda cx16.VERA_DATA1
stz cx16.VERA_DATA0 ; write 4 bytes at once.
}}
}
} else {
repeat cx16.r0 {
%asm {{
lda cx16.VERA_DATA1 ; fill cache with 4 source bytes...
lda cx16.VERA_DATA1
lda cx16.VERA_DATA1
lda cx16.VERA_DATA1
stz cx16.VERA_DATA0 ; write 4 bytes at once.
}}
}
}
cx16.VERA_FX_CTRL = 0 ; cache write disable
cx16.VERA_CTRL = 0
}
; unsigned multiplication just passes the values as signed to muls
; if you do this yourself in your call to muls, it will save a few instructions.
sub mult(uword value1, uword value2) -> uword {
+1 -1
View File
@@ -3,7 +3,7 @@
%import textio
cx16logo {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
sub logo_at(ubyte column, ubyte row) {
uword strptr
+22 -20
View File
@@ -1,16 +1,17 @@
; C64/C128 disk drive I/O routines.
%import textio
%import conv
%import string
%import syslib
diskio {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte READ_IO_CHANNEL=12
const ubyte WRITE_IO_CHANNEL=13
ubyte drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
sub reset_read_channel() {
void cbm.CHKIN(READ_IO_CHANNEL)
@@ -30,8 +31,6 @@ diskio {
if_cs
goto io_error
reset_read_channel()
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
@@ -89,8 +88,6 @@ io_error:
if_cs
goto io_error
reset_read_channel()
if_cs
goto io_error
while cbm.CHRIN()!='"' {
; skip up to entry name
@@ -175,8 +172,6 @@ io_error:
if_cs
goto io_error
reset_read_channel()
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
@@ -186,6 +181,7 @@ io_error:
return true
io_error:
cbm.CLOSE(READ_IO_CHANNEL)
lf_end_list()
return false
}
@@ -281,21 +277,20 @@ close_end:
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL) ; note: has to be Channel,x,Channel because otherwise f_seek doesn't work
void cbm.OPEN() ; open 12,8,12,"filename"
if_cc {
reset_read_channel()
if cbm.READST()==0 {
iteration_in_progress = true
reset_read_channel()
if_cc {
void cbm.CHRIN() ; read first byte to test for file not found
if not cbm.READST() {
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file
cbm.CLRCHN() ; reset default i/o channels
return true
}
void cbm.CHRIN() ; read first byte to test for file not found
if cbm.READST()==0 {
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file
cbm.CLRCHN() ; reset default i/o channels
return true
}
}
}
f_close()
cbm.CLOSE(READ_IO_CHANNEL)
return false
}
@@ -402,6 +397,7 @@ _end rts
void cbm.OPEN() ; open 13,8,1,"filename"
if_cc
return not cbm.READST()
cbm.CLOSE(WRITE_IO_CHANNEL)
f_close_w()
return false
}
@@ -430,6 +426,13 @@ _end rts
sub status() -> uword {
; -- retrieve the disk drive's current status message
str device_not_present_error = "device not present #xx"
if cbm.READST()==128 {
device_not_present_error[len(device_not_present_error)-2] = 0
conv.str_ub(drivenumber)
void string.copy(conv.string_out, &device_not_present_error+len(device_not_present_error)-2)
return device_not_present_error
}
uword messageptr = &list_filename
cbm.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15)
@@ -437,8 +440,6 @@ _end rts
if_cs
goto io_error
void cbm.CHKIN(15) ; use #15 as input channel
if_cs
goto io_error
while not cbm.READST() {
cx16.r5L = cbm.CHRIN()
@@ -455,7 +456,8 @@ done:
return list_filename
io_error:
list_filename = "?disk error"
cbm.CLOSE(15)
list_filename = "io error"
goto done
}
+18 -5
View File
@@ -1,11 +1,11 @@
floats {
; the floating point functions shared across compiler targets
%option merge, no_symbol_prefixing
%option merge, no_symbol_prefixing, ignore_unused
asmsub print_f(float value @FAC1) clobbers(A,X,Y) {
asmsub print(float value @FAC1) clobbers(A,X,Y) {
; ---- prints the floating point value (without a newline). No leading space (unlike BASIC)!
%asm {{
jsr str_f
jsr tostr
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq +
@@ -16,7 +16,7 @@ asmsub print_f(float value @FAC1) clobbers(A,X,Y) {
}}
}
asmsub str_f(float value @FAC1) clobbers(X) -> str @AY {
asmsub tostr(float value @FAC1) clobbers(X) -> str @AY {
; ---- converts the floating point value to a string. No leading space!
%asm {{
jsr FOUT
@@ -179,7 +179,7 @@ sub ceil(float value) -> float {
}}
}
sub rndseedf(float seed) {
sub rndseed(float seed) {
if seed>0
seed = -seed ; make sure fp seed is always negative
@@ -215,4 +215,17 @@ sub clampf(float value, float minimum, float maximum) -> float {
return minimum
}
inline asmsub push(float value @FAC1) {
%asm {{
jsr floats.pushFAC1
}}
}
inline asmsub pop() -> float @FAC1 {
%asm {{
clc
jsr floats.popFAC
}}
}
}
+46 -1
View File
@@ -3,7 +3,7 @@
; such as abs, sqrt, clamp, min, max for example.
math {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
%asminclude "library:math.asm"
@@ -484,4 +484,49 @@ log2_tab
}}
}
sub crc16(uword data, uword length) -> uword {
; calculates the CRC16 (XMODEM) checksum of the buffer.
cx16.r1 = data ; make sure pointer is in zp (on cx16)
cx16.r0 = 0 ; the crc value
repeat length {
cx16.r0H ^= @(cx16.r1)
repeat 8 {
if cx16.r0H & $80 {
cx16.r0 <<= 1
cx16.r0 ^= $1021
}
else
cx16.r0<<=1
}
cx16.r1++
}
return cx16.r0
}
sub crc32(uword data, uword length) {
; Calculates the CRC-32 (POSIX) checksum of the buffer.
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
; result stored in cx16.r0 (low word) and cx16.r1 (high word)
cx16.r2 = data ; make sure pointer is in zp (on cx16)
cx16.r1 = 0
cx16.r0 = 0
repeat length {
cx16.r1H ^= @(cx16.r2)
repeat 8 {
if cx16.r1H & $80 {
cx16.r0 <<= 1
rol(cx16.r1)
cx16.r1 ^= $04c1
cx16.r0 ^= $1db7
}
else {
cx16.r0 <<= 1
rol(cx16.r1)
}
}
cx16.r2++
}
cx16.r1 ^= $ffff
cx16.r0 ^= $ffff
}
}
+41 -6
View File
@@ -2,9 +2,10 @@
; Including memory registers, I/O registers, Basic and Kernal subroutines.
; see: https://www.pagetable.com/?p=926 , http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
%option no_symbol_prefixing, ignore_unused
cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $8d ; software jiffy clock, hi byte
&ubyte TIME_MID = $8e ; .. mid byte
@@ -79,7 +80,6 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 32 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 32=PET
@@ -110,6 +110,8 @@ asmsub init_system_phase2() {
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
_exitcode = *+1
lda #0 ; exit code possibly modified in exit()
rts
}}
}
@@ -345,12 +347,13 @@ save_SCRATCH_ZPWORD2 .word 0
}}
}
inline asmsub exit(ubyte returnvalue @A) {
asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register
%asm {{
sta cleanup_at_exit._exitcode
ldx prog8_lib.orig_stackpointer
txs
rts ; return to original caller
jmp cleanup_at_exit
}}
}
@@ -361,11 +364,37 @@ save_SCRATCH_ZPWORD2 .word 0
}}
}
inline asmsub push(ubyte value @A) {
%asm {{
pha
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha
tya
pha
}}
}
inline asmsub pop() -> ubyte @A {
%asm {{
pla
}}
}
inline asmsub popw() -> uword @AY {
%asm {{
pla
tay
pla
}}
}
}
cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the PET as well but their location in memory is different
; (because there's no room for them in the zeropage)
@@ -497,4 +526,10 @@ cx16 {
rts
}}
}
sub cpu_is_65816() -> bool {
; Returns true when you have a 65816 cpu, false when it's a 6502.
return false
}
}
+1 -1
View File
@@ -7,7 +7,7 @@
txt {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25
-164
View File
@@ -218,170 +218,6 @@ rol2_mem_ub .proc
rts
.pend
rol_array_ub .proc
; -- rol a ubyte in an array
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy _arg_index
lda (P8ZP_SCRATCH_W1),y
rol a
sta (P8ZP_SCRATCH_W1),y
rts
_arg_target .word 0
_arg_index .byte 0
.pend
ror_array_ub .proc
; -- ror a ubyte in an array
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy _arg_index
lda (P8ZP_SCRATCH_W1),y
ror a
sta (P8ZP_SCRATCH_W1),y
rts
_arg_target .word 0
_arg_index .byte 0
.pend
ror2_array_ub .proc
; -- ror2 (8-bit ror) a ubyte in an array
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy _arg_index
lda (P8ZP_SCRATCH_W1),y
lsr a
bcc +
ora #$80
+ sta (P8ZP_SCRATCH_W1),y
rts
_arg_target .word 0
_arg_index .byte 0
.pend
rol2_array_ub .proc
; -- rol2 (8-bit rol) a ubyte in an array
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy _arg_index
lda (P8ZP_SCRATCH_W1),y
cmp #$80
rol a
sta (P8ZP_SCRATCH_W1),y
rts
_arg_target .word 0
_arg_index .byte 0
.pend
ror_array_uw .proc
; -- ror a uword in an array
php
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda _arg_index
asl a
tay
iny
lda (P8ZP_SCRATCH_W1),y
plp
ror a
sta (P8ZP_SCRATCH_W1),y
dey
lda (P8ZP_SCRATCH_W1),y
ror a
sta (P8ZP_SCRATCH_W1),y
rts
_arg_target .word 0
_arg_index .byte 0
.pend
rol_array_uw .proc
; -- rol a uword in an array
php
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda _arg_index
asl a
tay
lda (P8ZP_SCRATCH_W1),y
plp
rol a
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
rol a
sta (P8ZP_SCRATCH_W1),y
rts
_arg_target .word 0
_arg_index .byte 0
.pend
rol2_array_uw .proc
; -- rol2 (16-bit rol) a uword in an array
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda _arg_index
asl a
tay
lda (P8ZP_SCRATCH_W1),y
asl a
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
rol a
sta (P8ZP_SCRATCH_W1),y
bcc +
dey
lda (P8ZP_SCRATCH_W1),y
adc #0
sta (P8ZP_SCRATCH_W1),y
+ rts
_arg_target .word 0
_arg_index .byte 0
.pend
ror2_array_uw .proc
; -- ror2 (16-bit ror) a uword in an array
lda _arg_target
ldy _arg_target+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda _arg_index
asl a
tay
iny
lda (P8ZP_SCRATCH_W1),y
lsr a
sta (P8ZP_SCRATCH_W1),y
dey
lda (P8ZP_SCRATCH_W1),y
ror a
sta (P8ZP_SCRATCH_W1),y
bcc +
iny
lda (P8ZP_SCRATCH_W1),y
ora #$80
sta (P8ZP_SCRATCH_W1),y
+ rts
_arg_target .word 0
_arg_index .byte 0
.pend
strcpy .proc
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
+1 -1
View File
@@ -1,7 +1,7 @@
; Internal library routines - always included by the compiler
prog8_lib {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
%asminclude "library:prog8_lib.asm"
%asminclude "library:prog8_funcs.asm"
+66 -26
View File
@@ -1,7 +1,7 @@
; 0-terminated string manipulation routines.
string {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y {
; Returns the number of bytes in the string.
@@ -20,22 +20,19 @@ string {
}}
}
asmsub left(uword source @R0, ubyte length @A, uword target @R1) clobbers(A, Y) {
asmsub left(uword source @AX, ubyte length @Y, uword target @R1) clobbers(A, Y) {
; Copies the left side of the source string of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result.
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
%asm {{
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
ldy cx16.r0
sty P8ZP_SCRATCH_W1
ldy cx16.r0+1
sty P8ZP_SCRATCH_W1+1
ldy cx16.r1
sty P8ZP_SCRATCH_W2
ldy cx16.r1+1
sty P8ZP_SCRATCH_W2+1
tay
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
sta P8ZP_SCRATCH_W1
stx P8ZP_SCRATCH_W1+1
lda cx16.r1
sta P8ZP_SCRATCH_W2
lda cx16.r1+1
sta P8ZP_SCRATCH_W2+1
lda #0
sta (P8ZP_SCRATCH_W2),y
cpy #0
@@ -51,16 +48,16 @@ _loop dey
; asmgen.out(" jsr prog8_lib.func_leftstr")
}
asmsub right(uword source @R0, ubyte length @A, uword target @R1) clobbers(A,Y) {
asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) {
; Copies the right side of the source string of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result.
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
%asm {{
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
sta P8ZP_SCRATCH_B1
lda cx16.r0
ldy cx16.r0+1
stx P8ZP_SCRATCH_B1
sta cx16.r0
sty cx16.r0+1
jsr string.length
tya
sec
@@ -128,16 +125,14 @@ _startloop dey
}}
}
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, bool @Pc {
asmsub find(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
; Locates the first position of the given character in the string,
; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
%asm {{
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
sta P8ZP_SCRATCH_B1
lda cx16.r0
ldy cx16.r0+1
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
stx P8ZP_SCRATCH_B1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq _notfound
@@ -154,6 +149,13 @@ _found tya
}}
}
asmsub contains(uword string @AY, ubyte character @X) -> bool @Pc {
; Just return true/false if the character is in the given string or not.
%asm {{
jmp find
}}
}
asmsub copy(uword source @R0, uword target @AY) clobbers(A) -> ubyte @Y {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
@@ -384,7 +386,7 @@ fail clc ; yes, no match found, return with c=0
}}
}
asmsub isdigit(ubyte character @A) -> bool @Pc {
asmsub isdigit(ubyte petsciichar @A) -> bool @Pc {
%asm {{
cmp #'0'
bcs +
@@ -398,8 +400,8 @@ fail clc ; yes, no match found, return with c=0
}}
}
asmsub isupper(ubyte character @A) -> bool @Pc {
; shifted petscii has 2 ranges that contain the upper case letters...
asmsub isupper(ubyte petsciichar @A) -> bool @Pc {
; shifted petscii has 2 ranges that contain the upper case letters... 97-122 and 193-218
%asm {{
cmp #97
bcs +
@@ -418,7 +420,7 @@ _yes sec
}}
}
asmsub islower(ubyte character @A) -> bool @Pc {
asmsub islower(ubyte petsciichar @A) -> bool @Pc {
%asm {{
cmp #'a'
bcs +
@@ -432,7 +434,7 @@ _yes sec
}}
}
asmsub isletter(ubyte character @A) -> bool @Pc {
asmsub isletter(ubyte petsciichar @A) -> bool @Pc {
%asm {{
jsr islower
bcs +
@@ -440,4 +442,42 @@ _yes sec
+ rts
}}
}
asmsub isspace(ubyte petsciichar @A) -> bool @Pc {
%asm {{
cmp #32
beq +
cmp #13
beq +
cmp #9
beq +
cmp #10
beq +
cmp #141
beq +
cmp #160
beq +
clc
rts
+ sec
rts
}}
}
asmsub isprint(ubyte petsciichar @A) -> bool @Pc {
%asm {{
cmp #160
bcc +
rts
+ cmp #32
bcs +
rts
+ cmp #128
bcc +
clc
rts
+ sec
rts
}}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
%import textio
test_stack {
%option no_symbol_prefixing
%option no_symbol_prefixing, ignore_unused
asmsub test() {
%asm {{
+2
View File
@@ -4,6 +4,8 @@ conv {
; ----- number conversions to decimal strings ----
%option ignore_unused
str string_out = "????????????????" ; result buffer for the string conversion routines
sub str_ub0(ubyte value) {
+1
View File
@@ -4,6 +4,7 @@
%import textio
emudbg {
%option ignore_unused
sub is_emulator() -> bool {
; Test for emulator presence.
+29 -12
View File
@@ -1,6 +1,6 @@
; Prog8 definitions for floating point handling on the VirtualMachine
%option enable_floats
%option enable_floats, ignore_unused
floats {
@@ -9,31 +9,31 @@ floats {
const float TWOPI = 2*π
sub print_f(float value) {
sub print(float value) {
; ---- prints the floating point value (without a newline and no leading spaces).
%ir {{
loadm.f fr65535,floats.print_f.value
loadm.f fr65535,floats.print.value
syscall 25 (fr65535.f)
return
}}
}
sub str_f(float value) -> str {
sub tostr(float value) -> str {
; ---- converts the floating point value to a string (no leading spaces)
str @shared buffer=" "*20
%ir {{
load.w r65535,floats.str_f.buffer
loadm.f fr65535,floats.str_f.value
load.w r65535,floats.tostr.buffer
loadm.f fr65535,floats.tostr.value
syscall 47 (r65535.w, fr65535.f)
load.w r0,floats.str_f.buffer
load.w r0,floats.tostr.buffer
returnr.w r0
}}
}
sub parse_f(str value) -> float {
sub parse(str value) -> float {
; -- parse a string value of a number to float
%ir {{
loadm.w r65535,floats.parse_f.value
loadm.w r65535,floats.parse.value
syscall 45 (r65535.w): fr0.f
returnr.f fr0
}}
@@ -131,16 +131,16 @@ sub ceil(float value) -> float {
}}
}
sub rndf() -> float {
sub rnd() -> float {
%ir {{
syscall 35 () : fr0.f
returnr.f fr0
}}
}
sub rndseedf(float seed) {
sub rndseed(float seed) {
%ir {{
loadm.f fr65535,floats.rndseedf.seed
loadm.f fr65535,floats.rndseed.seed
syscall 32 (fr65535.f)
return
}}
@@ -173,4 +173,21 @@ sub normalize(float value) -> float {
return value
}
sub push(float value) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.f fr65535,floats.push.value
push.f fr65535
return
}}
}
sub pop() -> float {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.f fr65535
returnr.f fr65535
}}
}
}
+42
View File
@@ -3,6 +3,7 @@
; such as abs, sqrt, clamp, min, max for example.
math {
%option ignore_unused
sub sin8u(ubyte angle) -> ubyte {
ubyte[256] sintab = [$80, $83, $86, $89, $8c, $8f, $92, $95, $98, $9b, $9e, $a2, $a5, $a7, $aa, $ad, $b0, $b3, $b6, $b9,
@@ -292,4 +293,45 @@ math {
return w2-w1
}
sub crc16(uword data, uword length) -> uword {
; calculates the CRC16 (XMODEM) checksum of the buffer.
cx16.r0 = 0 ; the crc value
repeat length {
cx16.r0H ^= @(data)
repeat 8 {
if cx16.r0H & $80
cx16.r0 = (cx16.r0<<1)^$1021
else
cx16.r0<<=1
}
data++
}
return cx16.r0
}
sub crc32(uword data, uword length) {
; Calculates the CRC-32 (POSIX) checksum of the buffer.
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
; result stored in cx16.r0 (low word) and cx16.r1 (high word)
cx16.r1 = 0
cx16.r0 = 0
repeat length {
cx16.r1H ^= @(data)
repeat 8 {
if cx16.r1H & $80 {
cx16.r0 <<= 1
rol(cx16.r1)
cx16.r1 ^= $04c1
cx16.r0 ^= $1db7
}
else {
cx16.r0 <<= 1
rol(cx16.r1)
}
}
data++
}
cx16.r1 ^= $ffff
cx16.r0 ^= $ffff
}
}
+5 -11
View File
@@ -7,7 +7,7 @@
monogfx {
%option no_symbol_prefixing
%option ignore_unused
; read-only control variables:
uword width = 0
@@ -70,12 +70,9 @@ monogfx {
}
sub horizontal_line(uword xx, uword yy, uword length, bool draw) {
ubyte color = 0
if draw
color = 255
uword xpos
for xpos in xx to xx+length-1
plot(xpos, yy, color)
plot(xpos, yy, draw)
}
sub safe_horizontal_line(uword xx, uword yy, uword length, bool draw) {
@@ -97,12 +94,9 @@ monogfx {
}
sub vertical_line(uword xx, uword yy, uword lheight, bool draw) {
ubyte color = 0
if draw
color = 255
uword ypos
for ypos in yy to yy+lheight-1
plot(xx, ypos, color)
plot(xx, ypos, draw)
}
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, bool draw) {
@@ -460,12 +454,12 @@ skip:
; -- select the text charset to use with the text() routine
; the charset number is the same as for the cx16.screen_set_charset() ROM function.
; 1 = ISO charset, 2 = PETSCII uppercase+graphs, 3= PETSCII uppercase+lowercase.
; TODO vm has no bitmap charset
; TODO vm bitmap charset
}
sub text(uword @zp xx, uword yy, bool draw, uword sctextptr) {
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use.
; TODO vm has no bitmap charset
; TODO vm bitmap charset
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
; Internal library routines - always included by the compiler
prog8_lib {
%option force_output
%option force_output, ignore_unused
}
+18 -1
View File
@@ -1,6 +1,8 @@
; 0-terminated string manipulation routines. For the Virtual Machine target.
string {
%option ignore_unused
sub length(str st) -> ubyte {
; Returns the number of bytes in the string.
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
@@ -63,6 +65,13 @@ string {
return 0
}
sub contains(str st, ubyte character) -> bool {
void find(st, character)
if_cs
return true
return false
}
sub copy(str source, str target) -> ubyte {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
@@ -190,4 +199,12 @@ string {
sub isletter(ubyte character) -> bool {
return islower(character) or isupper(character)
}
}
sub isspace(ubyte character) -> bool {
return character in [32, 13, 9, 10, 141, 160]
}
sub isprint(ubyte character) -> bool {
return character>=32 and character<=127 or character>=160
}
}
+58
View File
@@ -1,5 +1,7 @@
; Prog8 definitions for the Virtual Machine
%option ignore_unused
sys {
; ------- lowlevel system routines --------
@@ -126,6 +128,62 @@ sys {
returnr.b r0
}}
}
sub push(ubyte b) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.b r65535,sys.push.b
push.b r65535
}}
}
sub pushw(uword w) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.w r65535,sys.pushw.w
push.w r65535
}}
}
sub pop() -> ubyte {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.b r65535
returnr.b r65535
}}
}
sub popw() -> uword {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.w r65535
returnr.w r65535
}}
}
sub read_flags() -> ubyte {
; "simulate" the 6502 status register a little bit
if_neg {
if_z
cx16.r0L = %10000010
else
cx16.r0L = %10000000
}
else {
if_z
cx16.r0L = %00000010
else
cx16.r0L = %00000000
}
if_cs
cx16.r0L |= 1
; TODO: overflow flag not yet supported
; if_vs
; cx16.r0L |= %01000000
return cx16.r0L
}
}
cx16 {
+1
View File
@@ -1,6 +1,7 @@
; Prog8 definitions for the Text I/O console routines for the Virtual Machine
%import conv
%option ignore_unused
txt {
+57 -7
View File
@@ -37,6 +37,7 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
private fun compileMain(args: Array<String>): Boolean {
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 checkSource by cli.option(ArgType.Boolean, fullName = "check", description = "quickly check program for errors, no output will be produced")
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
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")
@@ -49,11 +50,16 @@ private fun compileMain(args: Array<String>): Boolean {
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 includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
val breakpointCpuInstruction by cli.option(ArgType.Boolean, fullName = "breakinstr", description = "also use a CPU instruction for %breakpoint")
val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the compiler AST")
val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the intermediate AST that is used for code generation")
val breakpointCpuInstruction by cli.option(ArgType.Choice(listOf("brk", "stp"), { it }), fullName = "breakinstr", description = "the CPU instruction to use as well for %breakpoint")
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}', '${PETTarget.NAME}', '${VMTarget.NAME}') (required)")
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@@ -96,6 +102,32 @@ private fun compileMain(args: Array<String>): Boolean {
return false
}
if (varsGolden==true) {
if (compilationTarget != Cx16Target.NAME) {
System.err.println("Golden Ram is only available on the Commander X16 target.")
return false
}
if (varsHighBank!=null || slabsHighBank!=null) {
System.err.println("Either use varsgolden or varshigh (and slabsgolden or slabshigh), not both or mixed.")
return false
}
}
if (slabsGolden==true) {
if (compilationTarget != Cx16Target.NAME) {
System.err.println("Golden Ram is only available on the Commander X16 target.")
return false
}
if (varsHighBank!=null || slabsHighBank!=null) {
System.err.println("Either use golden or high ram, not both.")
return false
}
}
if(varsHighBank!=null && slabsHighBank!=null && varsHighBank!=slabsHighBank) {
System.err.println("Vars and slabs high memory bank must be the same.")
return false
}
if(startVm==true) {
return runVm(moduleFiles.first())
}
@@ -113,22 +145,31 @@ private fun compileMain(args: Array<String>): Boolean {
val filepath = pathFrom(filepathRaw).normalize()
val compilerArgs = CompilerArguments(
filepath,
dontOptimize != true,
dontWriteAssembly != true,
if(checkSource==true) false else dontOptimize != true,
if(checkSource==true) false else dontWriteAssembly != true,
warnSymbolShadowing == true,
quietAssembler == true,
asmListfile == true,
includeSourcelines == true,
experimentalCodegen == true,
varsHighBank,
varsGolden == true,
slabsHighBank,
slabsGolden == true,
compilationTarget!!,
splitWordArrays == true,
breakpointCpuInstruction = false,
breakpointCpuInstruction,
printAst1 == true,
printAst2 == true,
processedSymbols,
srcdirs,
outputPath
)
val compilationResult = compileProgram(compilerArgs)
if(checkSource==true)
println("No output produced.")
if(compilationResult!=null)
results.add(compilationResult)
}
@@ -182,22 +223,31 @@ private fun compileMain(args: Array<String>): Boolean {
try {
val compilerArgs = CompilerArguments(
filepath,
dontOptimize != true,
dontWriteAssembly != true,
if(checkSource==true) false else dontOptimize != true,
if(checkSource==true) false else dontWriteAssembly != true,
warnSymbolShadowing == true,
quietAssembler == true,
asmListfile == true,
includeSourcelines == true,
experimentalCodegen == true,
varsHighBank,
varsGolden == true,
slabsHighBank,
slabsGolden == true,
compilationTarget!!,
splitWordArrays == true,
breakpointCpuInstruction == true,
breakpointCpuInstruction,
printAst1 == true,
printAst2 == true,
processedSymbols,
srcdirs,
outputPath
)
val result = compileProgram(compilerArgs)
if(checkSource==true)
println("No output produced.")
if(result==null)
return false
else
Loaded 100 of 218 files, more files were not shown because too many files have changed in this diff. Show more