Compare commits

...

1292 Commits

Author SHA1 Message Date
47dbafacd4 correct version 2024-03-21 20:15:54 +01:00
5b6811d073 not separate 2024-03-21 20:07:44 +01:00
7516116bb7 last attempt for hash build step 2024-03-21 20:04:07 +01:00
e6014ea4dd version 10.3.1 2024-03-21 19:32:16 +01:00
362abfe284 ci 2024-03-20 22:46:18 +01:00
ad4880997a no operand swap on logical expressions with shortcircuit evaluation (and,or are no longer associative!) 2024-03-20 22:34:48 +01:00
592becc126 allow %breakpoint also in if/else blocks and other anonymous scopes 2024-03-19 23:31:21 +01:00
c38765301e gfx2 screenmode tweak 2024-03-19 01:21:56 +01:00
5f27426f59 only on release event 2024-03-17 23:35:53 +01:00
d924f8bff8 another attempt to get the hashes working 2024-03-17 23:31:04 +01:00
d14c61b160 added string.findstr(). string.find() returns 255 if not found.
also fix string assignment bug for sub args.
2024-03-17 23:18:33 +01:00
fe2b67998c vm: fix load_raw, fix rng bug in textelite (carry flag shifting...) 2024-03-17 16:46:26 +01:00
04df3c9f7f vm: implemented in-place array multiplication better 2024-03-17 13:39:05 +01:00
de3d0b40dc fixed vm problem with branching instructions in global init chunk 2024-03-17 13:22:17 +01:00
4db4a5f1b2 vm: txt.width() and height() now return the actual console terminal width and height if possible. 2024-03-16 22:40:08 +01:00
5a0524ff4d various fixes 2024-03-16 18:48:06 +01:00
5b7801eea1 added crc verifications to diskspeed 2024-03-16 16:26:39 +01:00
fbe231793b optimized and added "streaming" crc32 and crc16 routines to math module. Return value is put in different register now! r14+r15 instead of r0+r1! 2024-03-16 01:07:03 +01:00
6a9269111e some changes in SMC routines for the cx16:
sys.poweroff_system() moved to cx16
sys.set_leds_brightness() moved to cx16 and changed to set_led_brightness, you can only change the activity led brightness.
2024-03-15 23:00:14 +01:00
a94cfd34f5 don't apply absorption law on functioncall operands 2024-03-15 01:04:27 +01:00
28eae5a0fd updated diskspeed example to deal with increased I/O speeds 2024-03-15 00:37:13 +01:00
1818738fc8 fixed potential bug in cx16.kbdbuf_clear() is and it is now cbm.kbdbuf_clear() and is available on all cbm like targets 2024-03-14 22:12:29 +01:00
7e1e7a0780 fix conv.str_ub and conv.str_b for missing tens digits 2024-03-13 23:03:25 +01:00
1fc79ff6dd implement the missing in-place array operators for split word arrays and numeric operand 2024-03-13 21:16:49 +01:00
3535c1acda fix broken boolean != comparison optimization 2024-03-13 20:23:42 +01:00
33c8caac8f get rid of containment expression restriction 2024-03-12 23:39:54 +01:00
51d708bbdd fix monogfx issue 2024-03-12 23:27:15 +01:00
a5a918df84 update docs about boolean type 2024-03-12 18:54:56 +01:00
820541e427 fixed and optimized pointervar indexed in-place operations 2024-03-11 23:27:48 +01:00
e63a8f0c01 fix vm textio prefix type 2024-03-11 22:22:30 +01:00
c11a9b8709 fix callgraph issue when module gets removed by optimizations 2024-03-11 20:34:22 +01:00
80f39e8097 Merge branch 'booleans'
# Conflicts:
#	compiler/res/prog8lib/cx16/monogfx.p8
#	compiler/res/prog8lib/virtual/monogfx.p8
#	compiler/src/prog8/compiler/astprocessing/BoolRemover.kt
#	compiler/test/TestTypecasts.kt
#	docs/source/todo.rst
#	examples/cx16/highresbitmap.p8
#	examples/test.p8
#	httpCompilerService/src/prog8/http/TestHttp.kt
2024-03-11 01:00:48 +01:00
2a8b65e29c test str to uword change in function params 2024-03-10 23:48:58 +01:00
4bdf50145e recognise \t character (TAB) in string literals (note: only valid in iso encoding) 2024-03-10 13:20:09 +01:00
3a9919a377 implemented a couple more diskio routines for the VM target 2024-03-09 17:36:39 +01:00
eef8ae00b8 replace str return type by uword 2024-03-09 15:38:46 +01:00
ed15fac691 improve IR error message 2024-03-09 13:38:25 +01:00
f739e679e4 added sys.exit2 and sys.exit3 to set more result registers at program exit
todo
2024-03-09 03:30:05 +01:00
fc0fae8caf tweak the redundant beq asm optimizer a bit more 2024-03-09 00:05:06 +01:00
f46896fd74 attempt to no longer have BuildVersion.kt in git, blocking easy pulls
(cherry picked from commit bfcf07c1a2)
2024-03-08 23:36:35 +01:00
52649a8e4f conv routines now return the string buffer address. 2024-03-08 02:12:46 +01:00
bdfb01f6a0 VM: implemented a few core routines in diskio (load/save)
textelite can now load and save your progress like it already could in the real version
2024-03-06 23:21:01 +01:00
1137e57393 update VTUI lib 2024-03-06 21:39:30 +01:00
267ea13e8c clearer error msg 2024-03-06 20:10:10 +01:00
04f7b772a3 lib updates, removed unused and obsolete http and dbus modules 2024-03-05 23:42:35 +01:00
42c7569791 doc 2024-03-05 22:58:27 +01:00
6d29b00a80 fix monogfx horizontal line and text draw in invert mode 2024-03-05 22:38:34 +01:00
9f1bd2d7d6 asmoptimizer that removes redundant branches on boolean in A 2024-03-05 20:37:15 +01:00
9826d7c494 optimize certain boolean comparisons more 2024-03-05 03:09:53 +01:00
c6bf57b390 non strict bools should also replace not byte with byte==0 2024-03-04 23:25:34 +01:00
bfcf07c1a2 attempt to no longer have BuildVersion.kt in git, blocking easy pulls 2024-03-04 20:45:59 +01:00
4d7e96d423 add monogfx inverted (eor) draw mode 2024-03-03 23:28:37 +01:00
449461e412 tweak monogfx stipple plot 2024-03-03 21:47:42 +01:00
607275ec66 tweak 2024-03-03 19:52:46 +01:00
e55cde2a81 more nonstrictbool conversions 2024-03-03 19:43:48 +01:00
84afb374e6 nostrictbool array conversions 2024-03-03 17:48:52 +01:00
da1620807f fixed all todos in ifelse gen 2024-03-03 12:10:42 +01:00
f39ef8f565 optimize byte comparison assignment to use rol trick instead of branching 2024-03-03 00:01:14 +01:00
fe8b6e820c getting rid of problematic fallback (infinite recursion) 2024-03-02 23:19:55 +01:00
f29d24e96a fixup split words array comparisons 2024-03-02 23:19:55 +01:00
620ffe54ec asm optimizer: don't remove labels. remove redundant cmp/cpx/cpy instructions. 2024-03-02 23:19:53 +01:00
ceaa4cd07d array issue 2024-03-02 23:19:39 +01:00
af17f903ee fix that if not fcall() wasn't transformed to a conditional branch instruction 2024-03-02 23:19:39 +01:00
c532e28841 fix several remaining bool return values in library routines 2024-03-02 23:19:39 +01:00
dba0846866 optimize word >, word <= 2024-03-02 23:19:39 +01:00
bed629998a fix large code for some compares 2024-03-02 23:19:39 +01:00
bc2ede76bf tweak to byte compares 2024-03-02 23:19:39 +01:00
2a1fec2ed2 fix codegen error for comparisons 2024-03-02 23:19:39 +01:00
004048e5a7 fix IR codegen error for b=float>value 2024-03-02 23:19:39 +01:00
b941d6f1e4 new comparison tests 2024-03-02 23:19:39 +01:00
37b346740b fix 6502 casting uword and float to bool 2024-03-02 23:19:39 +01:00
f5e332daf7 remove redundant IR instructions like SNZ 2024-03-02 23:19:39 +01:00
fe9a9fc5cb new if tests 2024-03-02 23:19:39 +01:00
cc57477b99 IR: support for indirect jump after if 2024-03-02 23:19:39 +01:00
a1574a7187 added txt.print_bool, several fixes 2024-03-02 23:19:39 +01:00
a5110b1f96 improved De Morgan rewrite rules 2024-03-02 23:19:39 +01:00
006713fe13 optimize boolean to ubyte assignment (skip type cast) 2024-03-02 23:19:39 +01:00
7868e672e0 ifelse more 2024-03-02 23:19:39 +01:00
e1a133c2c0 ifelse more 2024-03-02 23:19:39 +01:00
c77cd0da39 ifelse more 2024-03-02 23:19:39 +01:00
577333f2c4 new ifelse codegen 2024-03-02 23:19:39 +01:00
7d8cdcbfea more bool fixes and optimizations in codegen 2024-03-02 23:19:39 +01:00
c5c4c6f111 start of new ifelse 2024-03-02 23:19:39 +01:00
73be754680 move in place assignment functions back to AssignmentGen 2024-03-02 23:19:39 +01:00
acd841dbb6 bool changes in 6502 assignment codegen 2024-03-02 23:19:37 +01:00
6b52ba9397 6502 asmgen 2024-03-02 23:19:20 +01:00
10d12f73d6 IR/VM: testing the boolean changes, added in-place and/or. 2024-03-02 23:19:20 +01:00
cd9119655c IR codegen 2024-03-02 23:19:20 +01:00
41afeccd51 compiler stuff 2024-03-02 23:19:20 +01:00
6b87cbb703 optimizers 2024-03-02 23:19:20 +01:00
32afcbfe42 compilerAst BOOL type changes 2024-03-02 23:19:20 +01:00
bc2b38daf4 added PtBool and other changes to intermediate Ast 2024-03-02 23:19:20 +01:00
f40b7b62bb updated unit tests and some basic changes for them 2024-03-02 23:19:20 +01:00
1ca3f64bf0 libraries: add ==0 or !=0 to expressions that depend on implicit conversion from byte to bool 2024-03-02 23:19:20 +01:00
92527b4c1d examples: add ==0 or !=0 to expressions that depend on implicit conversion from byte to bool 2024-03-02 23:19:18 +01:00
c48012c385 tweak ifelse > and <= order 2024-03-02 17:01:31 +01:00
a282b17286 added asm optimizer for <= byte (bcc+beq->bcs) 2024-03-02 15:34:54 +01:00
58d9463f16 consolidate word comparison codegen 2024-03-02 14:00:12 +01:00
047decd552 consolidate byte comparison codegen 2024-03-01 22:21:22 +01:00
82e0877e64 readme 2024-03-01 00:41:11 +01:00
040d75dafa VM now supports indirect jump instruction 2024-02-22 16:19:26 +01:00
4e1686f6e3 fix warnings in gradle build scripts 2024-02-22 10:34:51 +01:00
b5e691f367 IR: fix chunk reachability: via unchopped chunk label directly so that they don't get removed 2024-02-21 23:18:57 +01:00
325f55f22d doc refs duplicates fix 2024-02-21 21:29:17 +01:00
9724f2db7d readthedocs 2024-02-21 21:23:18 +01:00
5f20f321f0 readthedocs tweaks 2024-02-21 21:05:38 +01:00
d4b087ea3f added txt.bell() to make terminal beep/bell sound on most systems 2024-02-21 17:35:37 +01:00
8ff10724d1 fix some docs hyperlinks 2024-02-21 17:11:36 +01:00
1581381467 streamline 2024-02-20 23:35:04 +01:00
96b5a30f60 slightly less strict 2024-02-20 23:01:51 +01:00
0e17a0474a added type check to catch invalid comparisons, fix maze example 2024-02-20 22:53:15 +01:00
b27368175d get rid of problematic rewrite of certain for loops that were causing the end expression to be evaluated every loop iteration 2024-02-20 21:03:21 +01:00
aba36f7c92 update github actions 2024-02-20 01:07:39 +01:00
a3fa946300 fix typo: reaturn -> return (#126) 2024-02-18 22:25:26 +01:00
01bbc2234e fix absorption law optimization, add 2 additional optimizations 2024-02-14 23:12:54 +01:00
58e1864144 Mention AUR package in documentation (#125)
Additionally, small fixes it nano highlighting
2024-02-12 20:42:00 +01:00
88458f5355 faster array copy and fix for length 256 2024-02-11 23:57:38 +01:00
a4f697bae1 faster 2024-02-10 23:56:55 +01:00
8201408f16 fix element size calc when copying array 2024-02-10 22:58:44 +01:00
8b8caa1c2e added math.randrange() and math.randrangew() 2024-02-10 22:16:13 +01:00
4dc50cb551 fix unused subroutine removal not removing all unused subs 2024-02-10 21:26:53 +01:00
5522a305ab add -dumpvars option to dump all allocated variables (zp, normal ram, etc) 2024-02-10 18:42:31 +01:00
d7f72056fc rest of the array copying 2024-02-10 17:16:06 +01:00
64c9c9b7fe hash only on release? 2024-02-10 02:11:39 +01:00
98e1c843e4 hash only on release? 2024-02-10 02:07:37 +01:00
906d9d858c implementing the array copys 2024-02-10 01:40:36 +01:00
16c1309df1 try to add a hash to the artifact 2024-02-10 00:38:32 +01:00
6eacf1bddd added a few more IR syscalls for often used copy routines 2024-02-09 23:56:44 +01:00
6c8c8e11cc fix cpu register overwrites when using @(...) arguments to an asmsub 2024-02-09 19:55:35 +01:00
e941d2665a preparing for new array copy codegen 2024-02-09 18:22:41 +01:00
68669dbef0 fix & of pointervar indexing 2024-02-08 23:03:53 +01:00
6a48de9a9f IR: fix & of array-element 2024-02-08 21:27:53 +01:00
9d6d98930b fix ast printing of & array-element 2024-02-08 21:27:53 +01:00
3cc858db12 Adding Syntax Higlighting for nano (#123) 2024-02-08 21:27:07 +01:00
386a391fd9 added string.lstripped() and string.ltrimmed() 2024-02-07 23:07:45 +01:00
d33aed4ed5 added txt.petscii2scr() and txt.petscii2scr_str() 2024-02-07 22:36:43 +01:00
73ec8c31ad fix 6502 code for zp pointer lookup clobbering registers. 2024-02-07 22:09:04 +01:00
24944ad49e added string.strip() and string.trim() and l/r variants.
fixed memsizer for pointers-to-ubyte.
2024-02-07 02:09:08 +01:00
26ed231f61 version 2024-02-07 00:03:39 +01:00
8485b8429f optimizing +=1/-=1 2024-02-06 23:49:40 +01:00
358215e4dd removed postIncrDecr (still allow ++/-- to be parsed into +=1/-=1) 2024-02-06 18:50:08 +01:00
f874942075 version 10.1 2024-02-04 16:22:43 +01:00
2cadb546d5 optimized in-place memory/pointervar operations some more 2024-02-04 15:17:24 +01:00
344a1b9eb8 cx16: added cx16.get_program_args() and cx16.set_program_args() 2024-02-04 11:35:13 +01:00
3c77f8a020 IR: optimize pointer access 2024-02-04 07:51:50 +01:00
8e00408e3e optimize pointer indexing a bit 2024-02-03 23:24:33 +01:00
abcdfd8e28 optimize postincrdecr on memory a bit 2024-02-03 22:33:27 +01:00
b0f5b6925d getting rid of pointer[idx] in ast, instead always use @(ptr+idx) 2024-02-03 19:22:04 +01:00
ef79d0c43e fix temp variable name clash resulting in faulty code in certain common subexpression optimization 2024-02-03 19:20:28 +01:00
78b4288005 avoid git's line ending translation for this test... 2024-02-03 14:57:25 +01:00
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
c71aa0895f get rid of certain redundant !=0 comparisons in logical expressions 2024-02-01 21:50:01 +01:00
9f8e61789a fix 65c02 codegen for certain in-place byte bitwise invert 2024-01-31 23:55:43 +01:00
932035cdc5 flag "returning a statement" as a syntax error 2024-01-31 21:36:39 +01:00
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
48ef856c0b identified wrong expr eval code - still need solution. Crash for now. 2024-01-30 23:00:50 +01:00
9aea2b22c4 doc improvement, fixes #120 2024-01-30 19:07:18 +01:00
e0055bc431 IR: working on inplace +/- 2024-01-29 22:08:47 +01:00
9553248ed6 IR: integrate inplace assignment ops 2024-01-28 22:33:16 +01:00
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
0800033b47 fixed split-word array in-place element +/- (other operators not yet...) 2024-01-28 22:30:45 +01:00
64d8943b7d fix error when trying to post-inc/decrement a character in a string 2024-01-28 16:47:55 +01:00
444e97b00b todo 2024-01-27 12:31:36 +01:00
1816bda7ea extra check 2024-01-26 00:12:31 +01:00
d4a2031c07 fix certain assignment data type mismatch crash 2024-01-25 21:14:20 +01:00
8cf0b6cf51 IR: optimize code for ==0 and !=0 augmented assigns 2024-01-25 00:30:47 +01:00
f2010bf7a5 IR: better code for array element assignments (w.i.p.) 2024-01-23 00:56:06 +01:00
8f56a7fe69 IR: use INV instead of XOR for bitwise invert 2024-01-22 22:47:54 +01:00
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
84a7e86fe3 clarify progend() 2024-01-22 18:57:25 +01:00
a8c09d6144 removed a problematic bitshift replacement in the const evaluator 2024-01-21 23:05:51 +01:00
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
0f83dc6491 sponsorship link.
error sorting.
version.
2024-01-19 20:04:29 +01:00
cc22861719 Create FUNDING.yml 2024-01-19 19:26:41 +01:00
a14c192ea3 also other targets 2024-01-18 22:31:34 +01:00
b3d98be862 oops, removed a bit too much when getting rid of the noshortcircuit code 2024-01-18 22:00:57 +01:00
43027a4728 IR: optimize rol ror 2024-01-18 21:51:44 +01:00
03831a7394 added cx16.cpu_is_65816() 2024-01-18 19:30:18 +01:00
fdbbd181ea fixes for address-of uword pointer array expressions 2024-01-17 22:51:15 +01:00
69075376dc get rid of the noshortcircuit fallback 2024-01-17 21:24:41 +01:00
504d1440cc fixed rol(),rol2(),ror(),ror2() 2024-01-17 21:02:17 +01:00
9e33b8b8da Added a couple of for examples using descending ranges. (#119) 2024-01-16 20:19:05 +01:00
0cfcc5cd29 fix VM sgn() function for floats 2024-01-16 01:34:55 +01:00
e0de662f8e fix signed word bitshift right (>8 shifts) 2024-01-16 01:08:16 +01:00
66a836d094 added support for reverse() on split word arrays 2024-01-16 00:52:09 +01:00
80095f4962 added support for any() on split word arrays 2024-01-15 23:51:19 +01:00
828d83dbef loadScaledArrayIndexIntoRegister(): useless type arg removed 2024-01-15 22:19:52 +01:00
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
0a356ba73a added containment check of float arrays 2024-01-14 14:14:09 +01:00
41de8caa13 added sprites.set_mousepointer_image(), sprites.set_mousepointer_hand() and sprites.get_data_ptr() 2024-01-14 00:38:56 +01:00
968609d06d IR: fix problems with symbol offsets and unused subroutines/chunks 2024-01-13 16:43:41 +01:00
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
0c1018ec61 dt error details 2024-01-12 17:34:19 +01:00
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
06bedb7adb added palette.get_color() and palette.default_colors[] 2024-01-11 21:27:18 +01:00
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
e8da62aa29 update Kotlin version and libs 2024-01-10 20:31:30 +01:00
ddb2ff4216 IR: use SCS opcode to set carry status flag into register 2024-01-09 23:46:27 +01:00
f27e3478b9 fix const value of AddressOf for certain types 2024-01-09 22:10:25 +01:00
38dc7fb7bd IR: added SCC and SCS instructions 2024-01-09 01:41:37 +01:00
aa4cd13c31 where to place vscode syntax files. 2024-01-08 00:09:21 +01:00
4a4b6c04a1 tweak common subexpression optimization for array lookups 2024-01-07 22:37:15 +01:00
37fa3b34a2 tweak IR 2024-01-07 22:12:09 +01:00
f8084e7955 fix const replacement optimization error on memory mapped variable 2024-01-07 18:48:18 +01:00
4d5119ce3e VM: also set N+Z flags on AND/OR/XOR instructions 2024-01-07 15:26:36 +01:00
d85c347a6c optimize /256 more, and fixed a unsigned byte word cast error 2024-01-07 02:34:05 +01:00
7dd758a753 better optimize WORD & $xx00 , WORD & $00xx 2024-01-06 22:01:21 +01:00
806654fc44 fix invalid const optimization with multiplication 2024-01-06 13:59:13 +01:00
8e6b91cb9e some optimizations 2024-01-06 00:44:00 +01:00
334e6dca28 added string.contains().
fixed string and array containment check for length 1.
2024-01-05 20:46:26 +01:00
f2daa17b92 tweak some not optimizations
cleanup IR typestring
2024-01-05 17:49:56 +01:00
6d9fccacb1 boolean not expression tweaks and optimizations 2024-01-05 13:32:16 +01:00
37638e7ed0 added Absorption laws optimization 2024-01-05 00:36:47 +01:00
8a0e650511 apply De Morgan's laws for logical not, results in smaller code 2024-01-04 23:45:46 +01:00
8ba5a0d90c tweak planet name display in starmap 2024-01-04 21:52:52 +01:00
bfd3edb617 fix expression evaluation bug where intermediate values were overwritten, yielding the wrong result 2024-01-04 21:04:11 +01:00
56ba24962c fixed 'not not x' optimization to just 'x' 2024-01-04 15:02:21 +01:00
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
242a3eec63 fix data type difference error on range from and to values 2024-01-03 21:46:22 +01:00
fee46f2e54 todo 2024-01-03 15:06:27 +01:00
6aed7e429a allow containment check in a range expression ("run time" range expression) 2024-01-03 01:17:13 +01:00
517ea82b99 fixed todos in Ast printer 2024-01-01 23:52:03 +01:00
99c29343de added -printast1 and -printast2 command line options 2024-01-01 22:48:19 +01:00
892fa76883 remove debug output 2024-01-01 20:48:41 +01:00
d446b57d05 fix unittest 2024-01-01 16:11:50 +01:00
0e086d788b removed chained comparisons again, because they caused invalid expression evaluations due to changed semantics. 2024-01-01 16:00:11 +01:00
498841d45d remove binexpr operand rotation that didn't help much at all 2024-01-01 15:12:15 +01:00
d1f8ee1e56 replace most common subexpressions by a single temp variable 2024-01-01 14:57:24 +01:00
07feb5c925 extra check 2023-12-31 17:04:28 +01:00
75fd263e85 fix expericodegen crash related to shortcircuiting 2023-12-31 01:28:17 +01:00
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
89cc7e5fa9 finalize short-circuit eval in IR codegen 2023-12-30 04:26:29 +01:00
265e7aefbf clean up unused codegen for logical ops on words, also fix no-shortcircuit exception 2023-12-30 04:06:02 +01:00
1c55a6c6dc shortcutting part one 2023-12-30 03:54:12 +01:00
8f18b5b8a7 keep distinction between logical and bitwise boolean operators 2023-12-30 01:08:42 +01:00
f790182f0b adding short-circuit boolean expression evaluation (in IR codegen) also -noshortcircuit cli option 2023-12-30 01:08:41 +01:00
813007a5d8 adjusted options of library modules 2023-12-29 22:21:44 +01:00
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
932bbd0381 allow casting of byte<->ubyte and word<->uword 2023-12-29 16:23:24 +01:00
01bd648cb2 added math.crc16() and math.crc32() 2023-12-29 08:00:02 +01:00
779a5606a7 add unittest for aa%bb (without space) to be parsed correctly as modulo, not directive 2023-12-29 05:11:50 +01:00
ccc11e49d2 fix asmgen for uword shift right 8 or more bits 2023-12-29 05:06:09 +01:00
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
5d88717f32 fix non-existing instructions txy/tyx, oops 2023-12-29 03:27:35 +01:00
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
bcc4bf5c2b almost forgot 2023-12-28 20:39:27 +01:00
a0594cbce3 const optimizer now knows about a bunch of library functions, such as math.* 2023-12-28 20:14:13 +01:00
078bfefe41 clarify scoped names a bit more 2023-12-28 16:36:29 +01:00
9c1b11d605 some WARN messages are now INFO 2023-12-28 14:20:47 +01:00
44d82f9190 add unit tests 2023-12-28 13:30:07 +01:00
37fcde30d6 constants have p8c_ prefix instead of p8v_ in the asm 2023-12-28 05:28:32 +01:00
09c6cb4d6b replace unwritten vars by consts. Improved const eval.
Fixed some slight bugs in library code
2023-12-28 05:17:15 +01:00
b428343c2a tweak chained comparisons 2023-12-28 02:31:39 +01:00
dfce292294 allow chained comparisons i<x<j (desugared into: i<x and x<j) 2023-12-28 01:18:59 +01:00
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
2eb137618e refactor block options 2023-12-26 22:13:08 +01:00
4bb2b8ca9b make isArray a computed property by simply checking the datatype 2023-12-26 19:58:08 +01:00
5179562fb2 can be val 2023-12-26 19:39:02 +01:00
0a4de45453 get rid of vardecl.declareddatatype 2023-12-26 19:33:58 +01:00
ffdc658dc8 type error tweaks 2023-12-26 18:49:01 +01:00
7530f4407b ir tag change INLINEASM->ASM 2023-12-26 16:15:19 +01:00
73864c8101 added -check command line option 2023-12-26 15:45:55 +01:00
f948917124 added floats.push() and floats.pop()
fixed vm pop.f
2023-12-26 15:19:49 +01:00
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
38a22fbc99 allow %option no_symbol_prefixing also on module scope 2023-12-26 12:31:18 +01:00
8ae435549d added -slabshigh N and -slabsgolden for memory() slabs 2023-12-23 20:45:30 +01:00
9b113c0cbb added -varsgolden to put BSS into Golden Ram at $0400 2023-12-23 20:11:50 +01:00
0e0fac8c4b BSSHIGHRAM_END more clearly defined (to be inclusive) 2023-12-23 19:05:06 +01:00
4cd9bb8f99 allow Python-style negative array indexing to count from the end 2023-12-23 16:37:28 +01:00
ad9eaeafeb call now returns a word value 2023-12-22 22:24:11 +01:00
6cd392909c added verafx.copy() routine for fast vram-to-vram copying ('blitting') 2023-12-22 17:52:43 +01:00
49ec430592 cx16: added several word Vera-registers as memory-mapped vars as well 2023-12-21 00:28:09 +01:00
09f3fbeb38 doc tweak, adding qualified name example for goto (#116) 2023-12-20 23:51:46 +01:00
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
66d939df0d docs about new asm symbol prefixes 2023-12-20 22:37:46 +01:00
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
299419917e added symbol ambiguity error (variable vs block name for scoped symbols)
fixes #114
2023-12-20 00:28:15 +01:00
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
b7279a3d9e fix 'not in' parsing error
fixes #115
2023-12-19 19:49:25 +01:00
e14b854d7b cx16: added audio kernal routines example 2023-12-19 01:47:05 +01:00
8bd7c601c0 cx16: added all remaining audio kernal routines. added the three x16edit kernal entry points. 2023-12-18 22:16:44 +01:00
997288fa03 added cbm.CLEARST() to reset ST to 0 2023-12-18 01:20:24 +01:00
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
ae66fcac1e added call builtin function for indirect JSR 2023-12-17 15:45:28 +01:00
43944a94eb doc tweaks 2023-12-17 14:47:20 +01:00
eba0bde6f3 Merge branch 'optimize-st'
# Conflicts:
#	examples/test.p8
2023-12-17 02:11:01 +01:00
4544af441b doc tweaks, explain str a bit more 2023-12-17 02:02:59 +01:00
a8be94de6b better error message when attempting to cast a float to integer 2023-12-15 22:28:06 +01:00
b24df31c2b IR: fix codegen for routines returning in CPU Status register flag 2023-12-14 21:16:14 +01:00
332ba8ed7e don't give error when returning uword value in subroutine that returns STR 2023-12-14 02:48:21 +01:00
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
01c2112881 remove PtJump label, just use identifier with dummy 2023-12-13 04:16:49 +01:00
a546c2247d optimize if-else handling of asmsub boolean result in status flags 2023-12-13 04:03:21 +01:00
0da9142009 asm: also work for asmsub that return N or Z flag (Carry already worked) 2023-12-13 02:11:15 +01:00
796add0ee2 add string.isspace and string.isprint 2023-12-13 00:28:34 +01:00
00b32f64e6 tweak for,sort,reverse st use 2023-12-12 20:26:45 +01:00
f97b3f23e2 optimize symbol table for IR 2023-12-12 19:26:27 +01:00
08a079a96e concerns with in for strings 2023-12-11 21:15:48 +01:00
e98e951834 fix chained assignment and multi-vardecl RHS evaluation 2023-12-10 16:44:51 +01:00
2668bf8519 fix void optimization issue 2023-12-09 21:48:22 +01:00
dd4c073e18 version 9.7 2023-12-09 18:54:54 +01:00
c7c72f00c7 document underscores in numeric literals for grouping 2023-12-09 14:07:42 +01:00
ef1c665b9a allow underscores for numerical grouping 2023-12-09 13:13:34 +01:00
d56565be25 fix multi-var decl 2023-12-09 12:32:41 +01:00
e076b3aedc fix multi-var decl in nested scopes 2023-12-09 12:07:09 +01:00
ae3b2ddf5f allow multi var declarations for floats too 2023-12-08 23:29:13 +01:00
1bdc427d73 multi var declarations ubyte x,y,z 2023-12-08 22:18:21 +01:00
6a639ce533 chained assignments x=y=z=42 2023-12-08 01:07:16 +01:00
d91ca8b197 vm: added floats.str_f() 2023-12-07 23:10:27 +01:00
a01c0a283d add check for memory() args to be const, added floats.str_f()
add check for memory() args to be const
2023-12-07 22:39:53 +01:00
5c393091a0 unit test for %encoding 2023-12-07 21:54:01 +01:00
01b680504b Add %encoding to syntax files (#113)
* IDEA `%encoding` syntax

* N++ `%encoding` syntax

* Vim `%encoding` syntax
2023-12-07 21:53:33 +01:00
8e4319cd5a module directive %encoding to set the text encoding for that whole file (iso, petscii, etc.) 2023-12-06 23:54:08 +01:00
5a776dd690 improve KotlinJavaRuntime library ref 2023-12-06 22:52:39 +01:00
cce08d95db unused subroutine warning only for main compilation module 2023-12-06 21:48:56 +01:00
28c1b208c1 optimized calls for float *10 and +0.5 2023-12-06 01:18:07 +01:00
3844bf1f72 fix string.isupper() 2023-12-05 23:52:43 +01:00
745d192563 added floats.normalize() 2023-12-05 22:54:35 +01:00
ee782e92ac fix cast error and vm float parsing 2023-12-05 22:51:15 +01:00
afbc91d1fc added string.isdigit, islower, isupper, isletter 2023-12-05 22:50:20 +01:00
f998888d6d fix some unicode identifier issues 2023-12-05 17:38:23 +01:00
7d8b42d63e allow Unicode letters in identifiers: things like 'knäckebröd' and 'π' are now valid identifiers. Added floats.π constant. 2023-12-05 01:36:54 +01:00
6ebd4e821f improved docs about subroutine scoping, fix possible optimizer crash for inlined sub 2023-12-04 23:23:52 +01:00
d1806bfdc3 added remaining verafx registers 2023-12-03 22:15:29 +01:00
1d2d7155da palette: changed some of the available presets. Also fix sizeof(array) crash. 2023-12-03 17:14:40 +01:00
b09e0a05bf some tweaks to errors about long integer literals 2023-12-03 02:45:26 +01:00
c609e982fe allow const expression intermediate values to be 32 bits integers to avoid needless overflow errors. 2023-12-03 01:48:12 +01:00
2b227b43fe bmx: error for unsupported file version 2023-12-02 23:56:59 +01:00
48f09f71ab fix TODO crash on uword[0] = uword[0] or 128 (byte register assign to word array) 2023-12-02 21:29:14 +01:00
ead8c59bda allow all character encodings on all compilation targets. 2023-12-02 20:59:50 +01:00
db52a9466c fix weird compiler warning for while 1 {..} 2023-12-02 20:24:45 +01:00
1509de390e various fixes
print_f() no longer prints a leading space.
Better error message if using float in for loop.
Fix crash when using non-const as when choice value.
VM print_f() more closely resembles the CBM version.
2023-12-02 18:23:54 +01:00
88a1aa4f3d fix invalid optimization for integers (X/C1)*C2 , only ok for floats because of int rounding 2023-12-01 23:17:49 +01:00
172e78e8f2 ir: ignore empty chunks instead of crashing 2023-12-01 22:49:20 +01:00
36bfef567d comments 2023-12-01 20:20:18 +01:00
e40ebd75a2 floats.parse_f uses kernal VAL if it's present 2023-11-30 23:50:50 +01:00
992732f2cb bmx support to save partial screens ('stamps') 2023-11-30 22:17:57 +01:00
b58a3ba1bb added cx16 sprites.move , movex and movey routines to move sprite by deltas 2023-11-30 20:49:47 +01:00
afe521b0c9 simplify bmx loading 2023-11-29 21:57:17 +01:00
5d9caef45f bmx can load "stamps" 2023-11-29 21:07:22 +01:00
278e2f5605 preparing for working kernal FP VAL_1() call 2023-11-29 00:27:02 +01:00
1e299bf360 better pokef() code 2023-11-28 23:04:27 +01:00
8dfa0bc38c fix a compiler crash in certain vardecl initialization expressions 2023-11-28 21:01:58 +01:00
fde136fb7b bmx module only supports 320 or 640 image widths for now 2023-11-28 20:38:52 +01:00
ee4da1a757 fix floats.parse_f() to use new kernal routine address for VAL
gives error message if it detects issues f.ex. with new kernal version that moves the routine
2023-11-27 23:58:28 +01:00
ae2d96c455 added peekf and pokef builtin functions. Fixed sizeof() to allow number argument as well. 2023-11-27 23:36:02 +01:00
6d8fbe0877 fixed float array indexing with an expression 2023-11-27 20:54:49 +01:00
2fa1d8f2e8 fix vm string hash 2023-11-27 01:27:50 +01:00
533090a68e fix expression result register of square, callfar, string compare functions in certain situations 2023-11-26 23:02:10 +01:00
1dff59e1d6 added string.hash() 2023-11-26 22:14:08 +01:00
44d232f52a optimize for x in something downto 0 2023-11-26 02:24:18 +01:00
5f6cff739a fix bmx palette writing from buffer 2023-11-26 01:50:25 +01:00
2764d235a9 optimizing for x in 0 to something 2023-11-25 21:37:27 +01:00
45debff89f bmx: allow palette to be loaded into memory buffer instead of vram 2023-11-25 17:58:43 +01:00
c45fbe6310 continue stmt added 2023-11-25 01:14:35 +01:00
9ef9c24388 IR: optimize redundant labels 2023-11-25 01:10:17 +01:00
6a40f23578 cx16: added bmx library module and showbmx example 2023-11-24 23:39:05 +01:00
6a0a6b4751 todo 2023-11-24 01:20:10 +01:00
0bee6f6b41 cx16: reorder processing of IRQ handlers 2023-11-24 01:12:27 +01:00
82a15b5a16 65C02 cpu: use TRB and TSB instructions for in-place AND/OR. 2023-11-24 00:50:52 +01:00
11b7c4459e omission 2023-11-23 00:29:31 +01:00
98570ac456 cx16: optimized sys.set_rasterline() 2023-11-23 00:02:04 +01:00
1b2296ad5b move cx16 irq examples to new API, fix some bugs in the handler 2023-11-22 23:25:39 +01:00
16851746d6 new X16 irq handler routines and examples 2023-11-22 20:03:21 +01:00
935450a45f update kotest library 2023-11-22 18:40:07 +01:00
ba67fd318b renamed cx16.VERA_IRQ_LINE_L to VERA_IRQLINE_L and added VERA_SCANLINE_L, to align with official register naming.
Also added a multi-irq example for the X16 to show the updated irq handler semantics.
2023-11-22 18:36:24 +01:00
08ac459a41 breaking change: sys.set_irq() and sys.set_rasterirq() no longer have useKernal parameter! The irq handler routine must return a boolean instead in the A register.
When it returns true it means run the system IRQ handler afterwards. When it returns false, the system handler is NOT ran afterwards.
2023-11-21 23:22:53 +01:00
a83e9d9a0a added sys.save_prog8_internals() and restore_prog8_internals() 2023-11-21 22:00:43 +01:00
62d3f01948 fix name check in inline asm
this no longer removes a subroutine that is otherwise only called from inlined asm.
2023-11-21 01:26:50 +01:00
af5ca2d0b8 vm: treat floats as 64 bits doubles. 0.0 printed as "0". 2023-11-21 00:57:56 +01:00
ab4bcdf12d emudbg no longer clobbers r1 2023-11-20 00:20:48 +01:00
a6756d2cea removed diskio.set_drive(), just set the diskio.drivenumber variable directly
there already wasn't a getter
2023-11-19 22:15:56 +01:00
f81061dd42 error msg and comments 2023-11-18 01:03:34 +01:00
8e2c304b3c txt.waitkey() now returns the key that was pressed 2023-11-17 20:31:19 +01:00
f21adaa3ef fix compiler error caused by removal of string symbol in txt.print() optimization 2023-11-17 19:51:48 +01:00
2637939e62 cx16.vaddr_clone now leaves vera CTRL selected port intact 2023-11-17 19:22:23 +01:00
faf05582f8 improved cx16 emudbg library 2023-11-17 15:07:21 +01:00
161c02ced3 message 2023-11-17 00:37:12 +01:00
ff8de8e42d removing redundant compares 2023-11-16 22:56:19 +01:00
09d506194f note 2023-11-15 22:27:16 +01:00
42db3085df improve the way %option merge works, you can now merge your own code with library code for instance. 2023-11-14 23:04:13 +01:00
ad14c88fde give error when using %option merge in module scope 2023-11-14 21:53:50 +01:00
0c9daf6eaf fix compiler crash on ptrvar[n+1] = ptrvar[2] 2023-11-14 21:46:11 +01:00
86c6530e46 palette: more accurate color conversion from 8 to 4 bits channels
set_rgb8(), color8to4(), channel8to4()
2023-11-14 20:40:48 +01:00
159f80d629 next version 2023-11-14 19:06:47 +01:00
aa949165c7 diskio.f_open_w() error handling back to what it was before
Otherwise it eats the status message. Added comment that you have to check this manually to be sure if the call succeeded or not!
2023-11-12 21:14:06 +01:00
d22359b6e7 removed cx16.FB_cursor_position2() because it was only for use in the graphics module 2023-11-12 16:40:13 +01:00
d73709653d remove unused interned strings in the resulting code (for example from removed if/else blocks) 2023-11-12 05:28:24 +01:00
405926e811 oops 2023-11-11 14:31:48 +01:00
36758f41a4 fixed diskio.f_open_w() error handling, finally added f_seek_w() to be able to seek in files for writing. 2023-11-11 14:26:40 +01:00
7ebc9c79cf added string.append()
cleanup redundant diskio prefixes
2023-11-10 23:53:59 +01:00
e0668b55b9 fix gfx2 safe_disc coloring 2023-11-10 01:08:13 +01:00
76c09da961 make pokemon() be like poke, but also return the old value in the memory location. 2023-11-09 22:48:44 +01:00
7e3b8c2c59 fix compiler crash on certain subroutine inlining attempts. 2023-11-09 21:16:12 +01:00
ecca854c7c Added cx16.edkeyvec and cx16.set_chrin_keyhandler(). mention the Github actions builds. 2023-11-09 01:03:31 +01:00
3b0d7ea960 better const-evaluation of addressOf a memory mapped variable 2023-11-08 22:04:41 +01:00
f70fa42eac more accurate palette conversion 2023-11-08 01:33:55 +01:00
5698de6cf4 feat: requirements.txt for convertsprite.py (#112) 2023-11-08 01:32:41 +01:00
c5a333a904 CX16: diskio.f_write() now uses fast MCIOUT block writes, including hiram bank boundary wrap-over 2023-11-08 01:12:49 +01:00
ff324955dd Feature/read cursor position (#111)
* feat: add ability to read cursor position on CBM machines

* feat: implement plot()/column() for atari target; add get_cursor(), get_column(), row(), and get_row()

* feat: implement wait_key() for Commodore targets; add get_cursor(), get_column(), row(), get_row()

* feat: really implement waitkey() on CBM targets

* fix: make waitkey void for compatibility with atari
2023-11-07 22:19:16 +01:00
70436f5dca cx16.vpeek() use VERA_DATA0 instead of 1, to not cause ADDRSEL to be != 0 (interferes with kernal) 2023-11-07 22:09:53 +01:00
31177a2b1b added sys.disable_caseswitch() and sys.enable_caseswitch() 2023-11-07 00:27:34 +01:00
4de012fc49 added notes to textio about PETSCII vs Screencode encoding. 2023-11-06 23:18:24 +01:00
ee2888e744 verafx.mult/muls now return upper 16 bits of the result in r0 2023-11-06 21:55:58 +01:00
efe4df92dc optimize when with const value (remove other choices from code) 2023-11-06 00:08:07 +01:00
723ab54f97 optimized all circle routines a little more. Added gfx2/monogfx safe_circle and safe_disc. Warning for when on const value. 2023-11-05 21:29:59 +01:00
d9389afc66 fix compiler crash on certain constant expressions 2023-11-05 13:59:08 +01:00
e7178ee496 optimized comparison with word variables 2023-11-05 00:20:12 +01:00
d5f35bb3fb added gfx2.init_mode() 2023-11-04 14:53:08 +01:00
72f1a779f2 optimize monogfx.fill() and gfx2.fill(), also don't read outside screen area 2023-11-04 14:30:51 +01:00
3277544295 optimize assigning word array value to byte variable 2023-11-04 00:33:50 +01:00
98d2c64d5d fix assembly error for uword[3] @zp @split word_addrs 2023-11-03 00:39:43 +01:00
f68b46fc60 add a %zpallowed option to specify the range of zeropage register that can be used 2023-11-03 00:19:25 +01:00
d54ab856e7 fix parameter passing bug introduced recently (byte not converted to word) 2023-11-02 00:31:35 +01:00
16b24fadea gfx2 future mode, upgrate to Kotlin 1.9.20 2023-11-01 23:18:44 +01:00
b3803cbdf1 more opportunities to use LDA(zp) instead of LDA(zp),Y on 65c02 2023-10-31 21:26:55 +01:00
2ceaa25181 optimized code for (infrequently used) logical operations on word array 2023-10-29 23:41:34 +01:00
513611c5a6 IR: using EXT more 2023-10-29 02:57:21 +01:00
7ec4ba40ad optimize asmsub arg evaluation order and stack usage 2023-10-28 17:29:00 +02:00
92374e122b IR: optimize concat with msb 0 into ext 2023-10-28 12:53:41 +02:00
94f12732ab add math.diff() and math.diffw() 2023-10-27 22:36:43 +02:00
0904712a00 remove last trace of getTempVar (arry index expression)
tiny optimization
2023-10-27 21:41:52 +02:00
32becdbced add monogfx lib to virtual target 2023-10-24 00:16:25 +02:00
34aa21f7d9 improve function call arg type casting 2023-10-22 22:33:35 +02:00
cc81dd7d3e remove useless close calls from diskio load 2023-10-22 17:24:05 +02:00
335213b55f tweaks 2023-10-21 02:16:58 +02:00
13ab4166c0 new kotest library version 2023-10-19 21:57:06 +02:00
3dc5a0e7f8 some arrays can be in BSS 2023-10-18 23:59:37 +02:00
e15c5cde53 tiny fill() optimization 2023-10-18 23:11:16 +02:00
d88c09b098 fix signed byte to word casting issue uw = 8888 + (bb as ubyte) 2023-10-17 22:54:33 +02:00
893b383bdf fix signed byte to word sign extension in assignment 2023-10-17 03:08:37 +02:00
dd7c9d62e6 remove assigment splitter, it now caused code bloat instead of more efficient code 2023-10-16 02:07:22 +02:00
97c5c90eff fix codegen for var1>>=var2 and var1<<=var2 when var2 is zero 2023-10-16 00:04:21 +02:00
1fb94e7a7b monogfx and gfx2: flood fill uses optimized horizontal line drawing 2023-10-15 23:19:11 +02:00
daca87c6d0 added -breakinstr compiler option 2023-10-15 21:55:09 +02:00
203ec5fa46 implement taking address of array var with variable index 2023-10-15 20:24:48 +02:00
9ea69c07b8 optimize word array reads with indexvar 2023-10-14 07:30:54 +02:00
68539d6cc9 micro tweaks adpcm.p8 2023-10-13 00:55:56 +02:00
f75fd0811e restructure play-adpcm example code, stream-wav can now play stereo adpcm wavs 2023-10-11 17:37:42 +02:00
836bc9d456 added verafx.available() 2023-10-10 22:12:21 +02:00
a37769aafe cx16 adpcm example is now able to decode and play stereo music as well as mono. 2023-10-10 02:41:20 +02:00
68e62e4bd2 added cx16.MCIOUT() kernal call
correct case of several other cx16 kernal calls.

corrected to upper case: cx16 kernal calls CLOSE_ALL, LKUPLA, LKUPSA, JSRFAR, PRIMM, MACPTR.
2023-10-09 22:44:36 +02:00
a5cd3728c9 3d rotation multiplications now using verafx acceleration 2023-10-05 22:36:30 +02:00
a48ce35f0b added %option verafxmuls 2023-10-05 22:06:33 +02:00
e1835b5775 removed dysfunctional c128.graphics library module 2023-10-05 21:03:47 +02:00
433832b329 gfx2.clear_screen and monogfx.clear_screen() now have color parameter to clear the screen with
this is much faster than filling a rectangle of the full screen size with a color.
2023-10-05 21:00:39 +02:00
ee81da14d6 cx16: removed monochrome modes from gfx2 (use monogfx instead). New screen mode numbering!
programs will now be a lot smaller than before if they use gfx2 (or monogfx if they were only using monochrome drawing)
monogfx also fixes some drawing errors with small horizontal lines, and stippled vertical lines.
2023-10-05 02:12:46 +02:00
6395d1908e cx16: added monogfx library module, replaces gfx2 for monochrome screenmodes. 2023-10-04 22:32:13 +02:00
989a5a2f8a some notes about array alignment 2023-10-04 01:10:36 +02:00
b7a622c68e fix alignment of uninitialized arrays in aligned blocks (make them initialized with zeros so they don't end up in the BSS section)
fix alignment of uninitialized arrays in aligned blocks (make them initialized with zeros so they don't end up in the BSS section)
2023-10-04 00:12:36 +02:00
a8507b437d add verafx.transparency() 2023-10-03 01:47:52 +02:00
e505bf9ccf added "emudbg" library (cx16 only) to interface with the emulator 2023-10-02 22:23:09 +02:00
a289b32053 Revert "added -verafxmul compiler option to use vera fx multiplication routine on cx16"
This reverts commit 690782bf.
It was too risky, using vera (especially fx) transparently in multiple places especially perhaps in IRQ handlers will create havoc unless much intricate care is taken to save/restore the vera state. Better to do vera fx explicitly where the programmer has full control.
2023-10-02 21:08:52 +02:00
c3f1f09ad1 added verafx.clear() 2023-10-02 01:34:56 +02:00
70ee2026ff fix gfx2 screen fill broken when using verafx 2023-10-02 00:12:48 +02:00
690782bf60 added -verafxmul compiler option to use vera fx multiplication routine on cx16 2023-10-01 22:44:45 +02:00
755cc4835e \n (newline) now also maps to Petscii $0d (return), like \r.
It used to map to $8d (shift-return)
2023-09-29 01:49:15 +02:00
a684ea46e4 fix c64 zp test and improve error for text encoding problem 2023-09-29 01:25:05 +02:00
8fbe13f99d c64: $a5 removed from free ZP (it's actually used by kernal disk routines) 2023-09-29 00:28:04 +02:00
452e9e275f diskio module: set correct read or write i/o channel every time f_read or f_write is called 2023-09-28 23:39:37 +02:00
cd40088636 vm: added math.mul16_last_upper() 2023-09-28 03:18:49 +02:00
9b9e6f4af5 added math.mul16_last_upper() to fetch the upper 16 bits of the last word multiplication 2023-09-25 23:59:57 +02:00
ae6eeadf54 doc about range step value has to be a constant 2023-09-25 23:19:32 +02:00
5268b05060 added bonkram chunk to chunkfile example 2023-09-25 22:24:40 +02:00
390263a34e added cx16 verafx library module 2023-09-24 23:00:40 +02:00
55646edc3e added cx16 chunkedfile example 2023-09-24 20:56:36 +02:00
8d177beb78 fix possible register corruption when calling asmsubs that require Carry flag as a parameter 2023-09-24 14:03:31 +02:00
1da0c59182 vm: remove BNER opcode -> CMP + BSTNE 2023-09-23 11:47:24 +02:00
36e8f10d2b vm: remove BEQR opcode -> CMP + BSTEQ 2023-09-23 11:42:58 +02:00
cdf5a8f20f vm: remove BNE opcode -> CMPI + BSTNE 2023-09-23 11:22:33 +02:00
eb64d92333 vm: remove BEQ opcode -> CMPI + BSTEQ 2023-09-23 11:21:43 +02:00
eb55da63ef weird 2023-09-23 11:21:17 +02:00
918302f79b ir: fix possible crash in validity check about PREPARECALL 2023-09-23 01:35:18 +02:00
9d7131d9f6 vm: setting status bits 2023-09-22 22:50:20 +02:00
229c1114dd vm: fixed array initialization values with address-ofs 2023-09-19 23:54:18 +02:00
885df9156f todo 2023-09-19 00:08:17 +02:00
c319233ddc ir: added preparecall 'meta' instruction for functioncalls 2023-09-18 23:22:03 +02:00
958b5c0780 Merge branch 'addrof-arrayelt'
# Conflicts:
#	docs/source/todo.rst
2023-09-18 04:48:45 +02:00
880c0a5da8 allow taking address of array element 2023-09-18 04:37:41 +02:00
237c6dc856 allow taking address of array element 2023-09-18 04:29:15 +02:00
ccf6e32bf9 adding setlsb() and setmsb() builtin functions to 6502 codegen 2023-09-17 15:16:47 +02:00
a1874f6f00 adding setlsb() and setmsb() builtin functions to 6502 codegen 2023-09-17 01:48:29 +02:00
95e4490a8a adding setlsb() and setmsb() builtin functions 2023-09-15 02:39:16 +02:00
31c132c2eb several optimizations and compiler error fix for @(&var) and @(&var+1) 2023-09-14 23:04:23 +02:00
00b0ec58b4 update to Antlr 4.13.1 2023-09-14 21:11:55 +02:00
a1d0e5bb65 added list of software to docs 2023-09-13 21:51:48 +02:00
03e0d4b2e8 reducing expression codegen complexity (no longer splitting conditional expressions, and using r9 as temp var) 2023-09-13 01:08:42 +02:00
6afdd4e6fd preparing next version 2023-09-12 21:53:49 +02:00
b500a0d477 c64: added a couple of routines that calculate the correct memory locations for video ram and sprite pointers etc. based on current VIC-II memory setup.
the examples with sprites, now use it.
2023-09-08 21:27:38 +02:00
dd2463a440 proper fix for the previous commit. + fix for i/o channel reset in diskio.f_seek()
it wasn't the adressing mode, it was that it assumed the pointer variable was always in zeropage (which might not be)
2023-09-07 22:17:46 +02:00
23a8bebd9e fix invalid addressing mode on 6502 cpu for bytevalue +/- bytearray[i] 2023-09-07 21:40:07 +02:00
3caf9108ad finalizing 9.4.1 release 2023-09-06 21:18:01 +02:00
bde4be8231 fix VM indexed instructions to only use lsb part of the index 2023-09-06 02:44:04 +02:00
0bbbb12ed2 fix bench8 examples 2023-09-05 23:40:54 +02:00
b570bdaed7 fix codegen for array[i] += float expression 2023-09-05 22:38:52 +02:00
8c0843cc87 fix an invalid 6502 instruction on c64 in certain float assignment 2023-09-05 21:54:52 +02:00
31458ffd81 examples cleanup and improving c64 graphics module (shift bitmap to higher ram area) 2023-09-05 20:39:12 +02:00
c15c10a94e fixed 'unroll CONSTANTEXPR' compiler errors 2023-09-05 01:03:35 +02:00
9fca978725 optimized plasma examples even more 2023-09-05 00:23:50 +02:00
b125901717 added cx16 plasma example 2023-09-04 23:54:13 +02:00
eb018ae660 code optimization for bytearray[x] +/- bytearray[y]
use adc array,y or sbc array,y instead of tempvar
2023-09-04 23:01:53 +02:00
7e5a9474fe improve plasma example 2023-09-04 20:35:43 +02:00
525a9b5036 prepare parser to allow chained array indexing later 2023-09-03 19:06:47 +02:00
c3fbdf34ca fixed c64 float problem 2023-09-03 16:40:10 +02:00
48bd51e1a5 c64 float problem 2023-09-03 16:29:01 +02:00
10d0b03a90 use less tempvars 2023-09-03 01:32:47 +02:00
e1b3582f08 fix wordvar -= @(memory) 2023-09-03 01:12:26 +02:00
95be1c9e22 fix optimized swapped in-place byte comparisons 2023-09-03 00:47:55 +02:00
1ce8fe06d5 fix in-place <= for bytes 2023-09-03 00:01:11 +02:00
15c649024e float problems on c64 2023-09-02 23:09:55 +02:00
e97303c226 fix word multiplication to not clobber r0 and r1 anymore
This was causing corruption in certain programs such as the cx16/amiga example.
The problem was introduced in 9.4 with the new multiply_words routine
2023-09-02 20:52:16 +02:00
3b786c819d avoid using temp var even more 2023-09-01 23:47:01 +02:00
04959dbd8b optimize asm: don't use temp var for some additions 2023-09-01 22:24:17 +02:00
5cd4b874ea tweak sprites module 2023-09-01 21:25:19 +02:00
f14ea1b3de micro optimization to save 2 cycles: change some pha+pla into tax+txa 2023-09-01 20:37:24 +02:00
9cc0cda0fb added sprites library module (cx16 only) 2023-09-01 17:35:07 +02:00
09a7a4bbe5 optimize comparison against zero 2023-09-01 02:28:11 +02:00
cfea8b3745 save a cycle 2023-09-01 00:50:24 +02:00
28bf0b61ce added math.log2() and math.log2w() 2023-09-01 00:42:15 +02:00
2dc2429735 tweaks to the cx16 sprite example 2023-08-31 23:24:46 +02:00
83d4592526 tweaks to the cx16 sprite example 2023-08-31 22:33:49 +02:00
2d528c26ae added cx16 sprite demo 2023-08-31 16:56:52 +02:00
66b3dce794 doc tweak 2023-08-30 13:16:39 +02:00
93f77a1045 version 9.4 2023-08-29 12:27:09 +02:00
aa4d23a3d5 fix register stack saving on certain expression code that was broken on 6502 but not on 65c02 2023-08-29 11:50:35 +02:00
2d7ebff8e9 fix shadowing warnings in asm and library code 2023-08-29 11:00:53 +02:00
bad9dd3b3b mention shadowing warnings from assembler 2023-08-28 16:55:28 +02:00
2f4e517857 update to Kotlin 1.9.10 2023-08-28 16:45:59 +02:00
ff35ba3696 added warnshadow cli option to enable assembler warnings about symbol shadowing 2023-08-28 16:41:46 +02:00
72768e7fad todo 2023-08-28 16:10:02 +02:00
77f3852cdc added floats.parse_f() 2023-08-16 14:47:20 +02:00
66857ca477 prepare parser to be more flexible with array indexed expressions 2023-08-15 13:07:01 +02:00
75514fc7af fix some invalid instructions on 6502 (instead of 65c02) target for bit shifts 2023-08-14 21:58:26 +02:00
be06d871b6 fix code for bitwise shifts by zero 2023-08-14 21:49:13 +02:00
f98ee326b4 error when doing txt.print('@') where "@" was intended (byte for string parameter) 2023-08-14 19:25:31 +02:00
bc8126eb16 2x faster word multiplication routine 2023-08-14 18:11:30 +02:00
4c8beefdcb slightly faster integer bytes multiplication routine 2023-08-14 17:00:16 +02:00
bbb6c53457 slightly faster sqrt() routine for integers 2023-08-14 17:00:02 +02:00
d8991894e3 added pet stubs for cbm.SETTIM,RDTIM,RDTIM16 2023-08-14 14:49:59 +02:00
c7b7dcfd03 made pet textio more compatible with the other platforms by putting the (dummy) color arguments back 2023-08-14 13:51:15 +02:00
2c9e50873c use math.square for optimized X*X calculation (words only).
Added IR SQUARE instruction.
2023-08-14 01:05:17 +02:00
923367296d fix reset_system() on PET, added some missing kernal routines 2023-08-13 01:46:25 +02:00
151a206617 experimental Commodore PET target 2023-08-12 23:25:07 +02:00
e403c4cf99 version 9.3 2023-08-12 17:58:10 +02:00
e3fbe37f9f fixed optimized code for >= and <= 2023-08-12 13:45:08 +02:00
dc870cd5ea fixed optimized code for > and < 2023-08-12 13:15:32 +02:00
584be44743 fix compiler error on float comparison expressions 2023-08-12 00:09:38 +02:00
5fffd35ec1 IR: fix augmented assignment operators 2023-08-11 18:24:37 +02:00
b92e22e4a6 IR: fix for loop over range with step 2023-08-11 03:05:47 +02:00
3e6d16a7a8 add error message for invalid step size in range expression 2023-08-11 02:35:52 +02:00
ecbcc277b8 improve -varshigh documentation 2023-08-10 00:17:50 +02:00
dff1d9e4dd cleanup range expression doc 2023-08-09 22:58:04 +02:00
7c0bde7310 parser: allow curly brace on next line for asmsub too
downgrade antlr4 one version again to what is used in IntelliJ's antlr plugin, to avoid potential version conflicts
2023-08-09 20:01:12 +02:00
a82d21ac05 fixed gfx2.plot in mode 1+5 with certain combinations of color and stipple 2023-08-08 00:01:43 +02:00
0bf8378fcb fixed gfx2.horizontal_line problem with monochrome stippling mode (regression since version 9.0)
todo
2023-08-07 22:56:07 +02:00
017ef8a837 optimization of > and <= in expressions 2023-08-07 21:23:31 +02:00
0d63cdcb96 optimization of < and >= in expressions 2023-08-07 04:54:35 +02:00
68a6f99c9f optimization of < in expressions 2023-08-07 02:32:07 +02:00
60781bcfc4 optimization of == and != in expressions 2023-08-07 01:25:41 +02:00
77fa2e2722 optimization in + or - assignment to word array 2023-08-05 23:28:40 +02:00
c36afd872e optimization in assignment to memory 2023-08-04 23:54:11 +02:00
7e58a4c130 optimization in assignment to array 2023-08-04 23:06:55 +02:00
19a4bf1088 clean up AugmentableAssignmentAsmGen a bit 2023-08-04 21:48:02 +02:00
9678bbae4b dedup 2023-08-02 23:19:52 +02:00
a4d093afa1 added -sourcelines cli option to include src lines in generated assembly (which is now off by default) 2023-08-02 23:05:24 +02:00
ba788bcf0f put the original p8 source lines into the generated assembly as comments (not only the line numbers). 2023-08-02 02:18:13 +02:00
f2c62bee7e docs 2023-08-01 22:49:55 +02:00
548721e306 docs 2023-07-31 22:17:43 +02:00
1ae950a638 Merge branch 'remove_evalstack'
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
2023-07-31 21:57:43 +02:00
c9385e93fe fix postincr/decr on indexed pointervariables 2023-07-31 20:13:49 +02:00
9bb16e293c vm: fix postincr/decr on indexed pointervariables 2023-07-31 19:37:30 +02:00
c223702ea0 code cleanups 2023-07-30 18:42:45 +02:00
9167ba499d Merge branch 'remove_evalstack' 2023-07-30 17:49:35 +02:00
2d7e95e1b6 release 9.2.1 2023-07-30 17:39:18 +02:00
0cba736446 Merge branch 'master' into remove_evalstack 2023-07-30 14:53:40 +02:00
0816a57032 never add rts to inline asmsubs and always inline them regardless of optimization setting
otherwise they can't specify a sequence of assembly instructions that should be inserted in-place, such as those that manipulate the cpu stack.
for instance cx16.irqsafe_set_irqd() / cx16.irqsafe_clear_irqd()
2023-07-30 14:52:37 +02:00
a0ab0bd3e2 Merge branch 'master' into remove_evalstack
# Conflicts:
#	examples/test.p8
2023-07-29 18:57:06 +02:00
b89ad4b328 don't optimize empty where choice away! It would call the else clause incorrectly. 2023-07-29 18:25:52 +02:00
6cda76a116 comments 2023-07-29 17:32:27 +02:00
c112b327ab tiny optimization 2023-07-29 17:04:41 +02:00
46c12a8899 fix byte in array assignment,
remove no longer needed array assignment ast transformation
2023-07-28 22:40:06 +02:00
c5219dfb3f fix assignment of register into byte array 2023-07-28 22:16:01 +02:00
4a8ee6815a merge 2023-07-28 03:34:58 +02:00
e1b6bb154a Merge branch 'master' into remove_evalstack
# Conflicts:
#	compiler/res/prog8lib/cx16/gfx2.p8
#	docs/source/todo.rst
#	examples/test.p8
2023-07-28 02:09:45 +02:00
b19c282269 release 9.2 2023-07-28 01:40:14 +02:00
e520921746 todo 2023-07-26 23:16:43 +02:00
970642244b optimized gfx2.text() for hires 4c mode 2023-07-26 04:17:44 +02:00
3b90be2d9e gfx2.text() per-pixel positioning implemented for screen modes 1 and 5 2023-07-25 00:43:45 +02:00
2f756f1e3a fix and optimize inplace invert and negate 2023-07-24 23:28:32 +02:00
78e84182f0 todo 2023-07-24 22:36:17 +02:00
65a7a8caf8 fix and optimize gfx2.position2(), added cx16.vaddr_clone() 2023-07-24 00:04:47 +02:00
4c6a2f5df9 emphasize index value size on pointer var indexing 2023-07-23 00:11:18 +02:00
fea297e409 cleanup some compilation warnings 2023-07-22 23:44:26 +02:00
7cf6aba625 Merge branch 'master' into remove_evalstack
# Conflicts:
#	examples/test.p8
2023-07-22 23:37:20 +02:00
3bbc00cc8c more caution notices about symbols in inlined asm 2023-07-22 23:22:06 +02:00
70ed2b4203 fix compilation of large bitshifts 2023-07-22 23:08:22 +02:00
0adce9b9c6 removed complexity restriction on array indexing expressions 2023-07-22 22:11:30 +02:00
0e781d18fa cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 23:04:21 +02:00
4575a8fffe cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 22:40:07 +02:00
10d0ff252b ignore buildversion changes 2023-07-21 00:14:06 +02:00
c7d54570cc IR: sXX, CONCAT instructions now use 3 register format 2023-07-21 00:07:56 +02:00
7136b33f2e cx16: change reset_system() to use Reset SMC sequence instead of hard reboot 2023-07-20 01:59:20 +02:00
70a78e74f6 get rid of binexpr splitter 2023-07-20 01:36:43 +02:00
d5707b7bf3 rebuilding floating point stack evaluation (using cpu stack) 2023-07-20 00:45:04 +02:00
9f247901d4 Merge branch 'master' into remove_evalstack
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
#	compiler/src/prog8/buildversion/BuildVersion.kt
#	examples/test.p8
2023-07-16 23:45:04 +02:00
5659742d97 fixed assigning byte to word not clearing msb sometimes 2023-07-16 23:16:32 +02:00
450eaf7c4a fixed lsb() to uword problem 2023-07-16 20:05:59 +02:00
47485e4b49 added more missing codegen for bit shifts 2023-07-16 17:42:10 +02:00
64254e758d cleaned up cx16 keyboardhandler example and some compiler warnings for redundant else 2023-07-16 13:23:53 +02:00
c1aa5d4e47 IR: optimized when statement translation 2023-07-16 12:10:46 +02:00
ab8173637a remove redundant asm optimizer 2023-07-16 11:15:28 +02:00
3841cef497 implemented missing bitshift codegen (non-stack) 2023-07-15 22:26:56 +02:00
b717f1c7eb little refactor of huge if statement 2023-07-15 22:26:56 +02:00
da57f76de3 fix augassign 2023-07-15 22:26:56 +02:00
4784f1c65a remove eval stack from documentation 2023-07-15 22:26:56 +02:00
41af63b333 remove even more X register save/restore code 2023-07-15 22:26:54 +02:00
e2bb0de24d clean up X register save/store in compiler code, remove temp vars for register saving 2023-07-15 22:25:58 +02:00
b791fae9ce remove stack based ExpressionAsmGen 2023-07-15 22:24:22 +02:00
6033a9e20c remove optfloatx option 2023-07-15 22:24:22 +02:00
9e8c8973d8 remove eval stack references from asm code 2023-07-15 22:24:22 +02:00
3933bf5c1a remove eval stack references from p8 code 2023-07-15 22:24:22 +02:00
708e296774 remove eval stack assignment source and targets 2023-07-15 22:24:22 +02:00
84925ab69c remove eval stack options 2023-07-15 22:24:19 +02:00
b3cb9b7fe2 added optimizer to remove needless pha/pla pairs 2023-07-15 22:19:48 +02:00
9cb61fa34d tweaks 2023-07-15 20:46:14 +02:00
7c219d235c fixed possible type mismatch error in when statements 2023-07-14 23:35:58 +02:00
6938c79f88 IR: added CMPI instruction 2023-07-14 23:17:29 +02:00
b8284a147d allow boolean when conditions, optimize into a regular if 2023-07-11 21:33:29 +02:00
15ee90e99c no error about missing target when -vm is used.
also version 9.1
2023-07-11 18:13:49 +02:00
795f80b4ec fix forloop 6502 codegen in case of descending word values 2023-07-11 00:33:12 +02:00
6b6427492d fix forloop 6502 codegen in case of descending word values 2023-07-10 23:10:16 +02:00
6055b8c3dc IR: fix forloop codegen for steps != 1 2023-07-10 21:36:44 +02:00
a98cb50d55 Revert "ir: SCC now sets all bits to 1 (or 0)"
This reverts commit 7245aece4f.
2023-07-09 23:16:13 +02:00
e98bbc1c52 todo 2023-07-09 22:29:54 +02:00
7245aece4f ir: SCC now sets all bits to 1 (or 0) 2023-07-08 23:16:01 +02:00
60cbb02822 vm: actually fix EXT(S) in vm too 2023-07-08 23:05:03 +02:00
4e863ecdac vm: fixed abs() and word-to-string conversion 2023-07-08 22:57:16 +02:00
5037033fcf ir: EXT and EXTS opcodes now have 2 registers to avoid type clash 2023-07-08 22:42:11 +02:00
e6b158bc97 @(..) argument must be of type UWORD 2023-07-08 22:34:47 +02:00
4cc0dfa10b comment 2023-07-08 11:42:29 +02:00
4ced8889d3 cx16: fix signature return values of cx16.screen_mode(), add get_screen_mode() and set_screen_mode() convenience routines 2023-07-08 11:37:29 +02:00
d26967a87d ir doc 2023-07-07 22:35:05 +02:00
fc8955941b slight optimization for certain word multiplications 2023-07-07 21:30:37 +02:00
071a80360f ir: fix some problem with comparison against zero 2023-07-07 21:17:28 +02:00
d2154f5f2e remove empty when choices, fixes ir compilation error on those 2023-07-07 20:34:24 +02:00
334d382bfa ir: JUMPI instruction added to support indirect jumps 2023-07-07 19:10:39 +02:00
90c4b00f74 ir: fix any() all() reverse() sort() on memory mapped arrays and on byte arrays 2023-07-07 17:25:32 +02:00
71261525e8 fix containment check on memory mapped arrays 2023-07-07 17:07:34 +02:00
3126959576 ir: several fixes 2023-07-07 16:53:32 +02:00
02e51d8282 ir: fix initial chunk linking 2023-07-07 00:30:56 +02:00
ffb2027a19 repeat loop count now always rounded to integer 2023-07-06 23:58:02 +02:00
70c9ab9074 upgrade libraries 2023-07-06 23:33:58 +02:00
6d1fdf1ba6 upgrade to Kotlin 1.9.0 2023-07-06 23:03:47 +02:00
1f7180d9a8 math.multiply_words returns lower 16 bits of the result also in AY (to avoid repeating some load instructions) 2023-07-06 22:54:13 +02:00
b4e94ae4dd optimizer: avoid symbol name clash when inlining subroutine 2023-07-05 23:15:04 +02:00
07c606bfc9 optimizer: don't replace for loop with repeat loop (the loop variable might be used elsewhere!) 2023-07-05 21:16:17 +02:00
e705a8bd89 discord info 2023-07-04 23:50:16 +02:00
b3bdfb7f1f more info about building the compiler 2023-07-04 22:41:38 +02:00
5af1aeb092 added block comment /* ...... */ 2023-07-04 00:46:29 +02:00
be64fa674a doc 2023-07-03 22:44:50 +02:00
204f5591a9 todos 2023-07-03 21:57:32 +02:00
ee3e3a3a40 optimize text rendering in gfx2 2023-07-03 21:45:09 +02:00
f9200a2b75 fix IR loader for romsub calls (calls to an address) 2023-07-02 23:41:15 +02:00
f570b70827 fix type error with returning an array from a subroutine returning uword 2023-07-02 22:09:19 +02:00
0db141eeac todo 2023-07-02 21:19:33 +02:00
acb2ee53bb Merge branch 'prefixing' 2023-07-02 21:15:30 +02:00
c544b7f5ba fixing up p8_ prefixing 2023-07-02 21:15:05 +02:00
c0024e97e5 fix doc version 2023-07-02 21:01:11 +02:00
bdf8aa9168 get rid of newexpr compiler option 2023-07-02 15:26:04 +02:00
de5ce0f515 tiny optimization and doc 2023-07-02 11:17:18 +02:00
bb95484c8a uniform symbol prefixing with p8_ 2023-07-02 06:15:09 +02:00
cad18b8a3a uniform symbol prefixing with p8_ 2023-07-02 06:15:02 +02:00
0f6a98751a tiny optimization 2023-07-02 06:13:22 +02:00
aac5a4c27f optimize word repeat loop codegen 2023-07-02 04:51:22 +02:00
d3f6415387 vm: fix repeat 256 2023-07-02 02:38:35 +02:00
04da44eb98 fix certain inefficient codegen when assigning a type casted value 2023-06-29 22:56:26 +02:00
7649be97b1 add git hash to compiler header output 2023-06-29 21:01:02 +02:00
c9ef777e0f fix rest of possible temp variable conflicts 2023-06-28 23:24:48 +02:00
c0cb2438d5 1-letter symbols now also prefixed with 'p8p_'
to avoid assembly errors caused by confusing variable 'a' with register 'a' etc.
2023-06-28 23:17:59 +02:00
30c531b39e attempting to fix array expression inplace assign 2023-06-28 00:38:08 +02:00
bf703a8a66 unittest 2023-06-27 23:43:35 +02:00
e7b631b087 allow comment lines inside array initializer value 2023-06-27 23:30:37 +02:00
a9f5dc036c fix cpu stack corruption in array assignment codegen 2023-06-27 18:49:49 +02:00
0a83b51e00 allow more curly brace styles 2023-06-27 01:59:22 +02:00
eab63ecc6c allow curly brace on next line also after subroutine and when 2023-06-27 01:29:25 +02:00
b0794cf35e added hiram bank number to -varshigh 2023-06-27 00:27:34 +02:00
5b9e71a27d docs 2023-06-25 21:35:30 +02:00
eae41de27d improve errors generated for undefined symbols 2023-06-25 15:19:51 +02:00
e9163aa3a7 added cx16.save_virtual_registers() and cx16.restore_virtual_registers() 2023-06-24 21:04:47 +02:00
8c617515ba don't prefix 3-letter symbols too aggressively (could cause some compilation errors) 2023-06-23 23:36:59 +02:00
04e4e71f2e uword == str is now possible (sugar for string.compare) 2023-06-22 00:20:30 +02:00
a587482edf optimize dangling else 2023-06-18 13:46:02 +02:00
0aac9350d5 rename math.atan() to math.atan2() 2023-06-18 13:05:36 +02:00
f56c12ee4e cx16 spotlight example 2023-06-18 12:49:22 +02:00
4bb9ae61f2 library source links 2023-06-18 02:31:45 +02:00
ff7f3484e4 atan 2023-06-17 23:01:47 +02:00
5da3abe6b4 fix silent typecast on return statements that could lose data (word->byte) 2023-06-17 14:44:36 +02:00
c0b398e0ce add various math.atan() routines 2023-06-17 00:43:33 +02:00
3de10adac2 bump required 64tass version 2023-06-16 23:24:31 +02:00
1b573d6552 add note about lacking fp parse routine 2023-06-16 00:12:52 +02:00
2a96f93919 vm: fix compiler error when dealing with label 2023-06-14 22:14:47 +02:00
c6b2639ca4 fix compiler crash due to missing 6502 codegen
(assigning a direct memory read byte to a cx16 virtual register)
2023-06-14 21:10:01 +02:00
b9abf37a7e fix invalid code when subroutines are defined in a repeat loop 2023-06-13 00:46:32 +02:00
373cbb4144 gradle build error explained 2023-06-11 17:51:18 +02:00
a521982576 fix subroutine inline problem with strings 2023-06-09 21:45:05 +02:00
a77fde577c update GitHub action steps 2023-06-09 19:51:04 +02:00
ea6926e57d fix float expression crash: fl = abs/sqrt (fl)+0.5 2023-06-09 19:28:34 +02:00
ba25b7fee6 fix diskio.diskname(). cx16: add diskio.curdir() 2023-06-07 22:38:51 +02:00
7ee162d98b preparing version 9.0 2023-06-05 19:47:00 +02:00
380f557c45 vm: implement split incr/decr 2023-06-03 22:22:13 +02:00
1bdae53f4e fix unit tests 2023-06-03 21:39:34 +02:00
9314c346da -target option is now required; c64 no longer the default 2023-06-03 19:14:45 +02:00
bfaad1388c IR: handle split arrays without new custom opcodes 2023-06-03 01:51:02 +02:00
0b580ad05d v9 upgrading doc 2023-06-01 20:23:04 +02:00
bb35a80177 %option splitarrays now also at module level 2023-05-31 21:50:41 +02:00
24fc95ac81 fix atari target syslib 2023-05-31 20:58:00 +02:00
8f864417c4 added %option splitarrays (block level) 2023-05-31 18:49:21 +02:00
bb9d29b061 fix an array literal assignment type error for word arrays 2023-05-30 22:46:37 +02:00
b9d8ec1463 add -splitarrays command line option 2023-05-30 19:08:34 +02:00
1842a7660d fix compiler crash on missing arguments for clamp,min,max 2023-05-30 18:13:58 +02:00
5caa2f5536 attempt to get newer 64tass from debian testing repo 2023-05-29 23:46:47 +02:00
d6078be8b7 attempt to get newer 64tass from debian testing repo 2023-05-29 23:44:10 +02:00
cf60723f14 attempt to get newer 64tass from debian testing repo 2023-05-29 23:43:08 +02:00
f7ff0a2b1d attempt to get newer 64tass from debian testing repo 2023-05-29 23:39:00 +02:00
cc49664b2f attempt to get newer 64tass from debian testing repo 2023-05-29 23:34:34 +02:00
99fe74f026 attempt to get newer 64tass from debian testing repo 2023-05-29 23:31:23 +02:00
b021869eeb attempt to get newer 64tass from debian testing repo 2023-05-29 23:24:48 +02:00
b8806d163b attempt to get newer 64tass from debian testing repo 2023-05-29 23:22:05 +02:00
1116aae1de attempt to get newer 64tass from debian testing repo 2023-05-29 23:18:15 +02:00
5e5f60253b attempt to get newer 64tass from debian testing repo 2023-05-29 23:14:22 +02:00
bbc02752c9 use split word arrays in various examples, fix codegen issue, docs 2023-05-29 15:34:33 +02:00
9896bc110e fix some split array issues in 6502 codegen 2023-05-28 22:49:33 +02:00
ca60f8ecdd Merge branch 'master' into split-arrays 2023-05-28 22:35:16 +02:00
544acd1e35 Merge branch 'v8_maintenance' 2023-05-28 22:30:52 +02:00
6e07602d77 fix psg initial envelope maxvol setting 2023-05-28 22:30:34 +02:00
82898f7bba fix some split array issues in 6502 codegen 2023-05-28 22:24:56 +02:00
d61283a8bc Merge branch 'master' into split-arrays 2023-05-28 14:25:37 +02:00
1ee3f826cc fix sqrt() regression 2023-05-28 14:23:47 +02:00
4a00a5ba9e use split word arrays in various examples 2023-05-28 13:51:58 +02:00
39eda67867 Merge branch 'master' into split-arrays
# Conflicts:
#	examples/test.p8
2023-05-28 13:28:43 +02:00
a99d38fdaa Merge branch 'v8_maintenance'
# Conflicts:
#	examples/test.p8
2023-05-28 13:26:05 +02:00
0eb2d437e2 fix compiler error and codegen fault on signed value bitwise operation 2023-05-28 13:13:11 +02:00
3ac9036c79 more split array stuff for 6502 2023-05-27 22:44:45 +02:00
c94e292176 more split array stuff 2023-05-27 12:47:11 +02:00
91d87c2d9b Merge branch 'master' into split-arrays 2023-05-26 20:22:30 +02:00
ff472f69c0 update gradle wrapper to 8.1.1 2023-05-26 20:21:34 +02:00
e18119e24c Merge branch 'master' into split-arrays 2023-05-26 19:25:57 +02:00
4a592dc64c kotlin 1.8.21 2023-05-26 19:20:56 +02:00
d9e13201dd fix kotlin version IDE warning 2023-05-26 19:14:19 +02:00
5c75b19c5d fix kotlin version IDE warning 2023-05-26 19:13:21 +02:00
52a77db60f adding split array type 2023-05-26 19:11:07 +02:00
0513c250fb Merge branch 'v8_maintenance' 2023-05-23 20:42:51 +02:00
48864ad6cf add a unit test that checks for 64tass availability 2023-05-23 20:42:36 +02:00
cdbccad21e optimized gfx2 plot and horizontal_line a bit more 2023-05-23 20:29:17 +02:00
e15bc68c9b added gfx2.fill() flood fill routine 2023-05-23 00:50:10 +02:00
8bffd7672d added sys.irqsafe_set_irqd()/irqsafe_clear_irqd() 2023-05-22 21:13:20 +02:00
61df5b3060 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/prog8lib/cx16/syslib.p8
2023-05-22 20:43:05 +02:00
b5255444cd irq-safe irqd handling for RDTIM16 2023-05-22 20:36:33 +02:00
0c94e377fc Merge branch 'v8_maintenance' 2023-05-21 16:09:31 +02:00
8e5c67b4b2 ir: don't refuse complicated array lookup expressions 2023-05-21 16:07:19 +02:00
b24f2f1756 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/prog8lib/cx16/syslib.p8
#	examples/test.p8
2023-05-21 15:05:17 +02:00
c69c17de42 cx16 avoid ram bank issue with RDTIM in sys.wait() and c64.RDTIM16() 2023-05-21 15:03:33 +02:00
061617122a Merge branch 'v8_maintenance'
# Conflicts:
#	examples/test.p8
2023-05-20 18:07:57 +02:00
125ce3240f expr operands assignment refactor 2023-05-20 18:04:46 +02:00
7215efe167 fix expr eval error in certain situations
such as pokew() with 2 complex operands
2023-05-20 17:42:35 +02:00
06d1570142 cx16: added diskio.save_raw() headerless save routine 2023-05-20 00:00:50 +02:00
093c370faa todo 2023-05-19 01:26:15 +02:00
aec9574737 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/version.txt
#	docs/source/todo.rst
#	examples/test.p8
2023-05-18 22:47:06 +02:00
7ceb76cff5 fix compiler crash on certain operands type mismatch 2023-05-18 22:46:00 +02:00
300e2fe9f8 IR: wrong attempt at optimizing register usage by reusing registers inside different code chunks 2023-05-18 21:57:21 +02:00
91e1643627 update 3rd party libraries 2023-05-18 11:47:30 +02:00
91421b0c62 IR handy sequence shortcut functions 2023-05-18 11:32:20 +02:00
40f611664f upgr 2023-05-18 00:04:31 +02:00
dcba4f4098 fix resultregister crash 2023-05-18 00:00:37 +02:00
c098ad2b3b fix vm minf/maxf 2023-05-17 23:18:14 +02:00
b43223cb7a added clamp() builtin function and floats.clampf() 2023-05-17 23:12:58 +02:00
e243531dab upgrading 2023-05-17 00:49:47 +02:00
1af38e62bc removed floats.fabs() and floats.sqrt()/fsqrt() 2023-05-17 00:46:15 +02:00
f37f062cdc fix for loop pre-check 2023-05-17 00:33:55 +02:00
7e734214dc v8_maintenance branch made 2023-05-15 23:01:43 +02:00
05d152746f Merge branch 'master' into version_9 2023-05-15 22:43:03 +02:00
dea7f37553 vm: fix % result when dividing by 0 2023-05-15 20:33:20 +02:00
415c599310 update cx16 keyhandler example to r43 keyboard changes 2023-05-14 23:38:16 +02:00
70cd4fedbe Revert "update cx16 keyhandler example to r43 keyboard changes"
This reverts commit 1e6d7673bc.
2023-05-14 23:29:04 +02:00
1e6d7673bc update cx16 keyhandler example to r43 keyboard changes 2023-05-14 23:11:24 +02:00
b4963b725b Merge branch 'master' into version_9
# Conflicts:
#	compiler/res/version.txt
2023-05-14 22:19:23 +02:00
0371ffa4ce 'amiga' example using iso font 2023-05-14 21:55:35 +02:00
6a664a7e15 Merge branch 'master' into version_9 2023-05-14 21:03:08 +02:00
88ce9300bc fix parse cpureg in IR regspec 2023-05-14 21:02:40 +02:00
85cf0e311c Merge branch 'master' into version_9
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	docs/source/todo.rst
#	intermediate/src/prog8/intermediate/IRInstructions.kt
2023-05-14 20:47:09 +02:00
0e3d75cfeb move irType() to intermediate module 2023-05-14 20:44:32 +02:00
630c8a5faa IR: fix romsub encoding 2023-05-14 18:08:06 +02:00
905921a684 IR: new (sys)call instructions that encapsulate the full subroutine call
to fix the bugs resulting from nesting subroutine calls (as param to another call etc)
2023-05-14 15:20:25 +02:00
1e469b3b0f Merge branch 'master' into version_9
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2023-05-09 22:45:21 +02:00
bff3c4f95c IR now converts IRInlineAsmChunk (of type IR) into regular code chunks directly.
.p8ir files usually won't contain <INLINEASM> nodes any longer
2023-05-09 21:04:31 +02:00
bd2bcb6994 Merge branch 'master' into version_9
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt
#	compiler/res/prog8lib/c128/syslib.p8
#	compiler/res/prog8lib/c64/syslib.p8
#	compiler/res/prog8lib/cx16/syslib.p8
#	docs/source/todo.rst
#	examples/test.p8
#	intermediate/src/prog8/intermediate/IRInstructions.kt
2023-05-08 23:17:52 +02:00
4c8898a639 fix typecheck crash on certain byte to word assignments 2023-05-08 23:02:48 +02:00
97df33ab1a IR: fix byte to word assignment not doing value extension 2023-05-08 22:47:00 +02:00
ef46fb2685 refactor 2023-05-08 21:51:55 +02:00
d5d6dd3614 optimize typecast expr 2023-05-08 03:30:14 +02:00
6c233c6a0a optimize add/sub expr 2023-05-08 02:41:34 +02:00
6db715d879 optimize multiplication expr 2023-05-08 02:10:54 +02:00
ab02e8a546 optimize more carry flag assembly 2023-05-07 23:55:34 +02:00
8cbfe64f19 optimize some carry flag assembly 2023-05-07 23:27:49 +02:00
fd1e9971e4 asmsub Pc params and returnvalue must be boolean 2023-05-07 22:59:30 +02:00
68336a76c5 optimized word comparison expressions 2023-05-07 20:40:48 +02:00
393e914a86 optimized word equality comparison expressions 2023-05-07 18:55:17 +02:00
ffb54110e9 optimized byte comparison expressions 2023-05-07 15:15:58 +02:00
533d825f1a optimized ubyte comparison expressions 2023-05-07 14:47:31 +02:00
c65279b672 optimized logical expressions more 2023-05-07 13:29:45 +02:00
f9926beeef fix cx16.psg irq issue 2023-05-04 00:16:24 +02:00
add8a777d8 IR: binarydata fixes 2023-05-03 22:31:04 +02:00
21bc505d85 for loops no longer execute when from var already reached beyond the end 2023-05-03 00:43:03 +02:00
3fc49c001e IR: fix for-loop codegen when step<0 2023-05-02 23:12:11 +02:00
3d69a95c49 IR: fix for-loop codegen when step<0 2023-05-02 23:09:42 +02:00
d81fdf6d6b for loops... 2023-05-02 22:55:58 +02:00
87d3109ffb diskio f_seek_w() abandoned due to unreliability 2023-05-02 19:33:49 +02:00
180dbbb521 cleaning up the diskio modules
for cx16: removed cx16diskio (merged everything into its regular diskio module)
for cx16: the load() and load_raw() routines that took an extra ram bank parameter are gone. You have to cx16.rambank() yourself before calling load().
2023-05-02 03:31:11 +02:00
24aac7cee5 cleaning up the diskio modules 2023-05-02 02:15:22 +02:00
53e18a5387 Api change: drivenumber parameter removed from all routines in diskio and cx16diskio modules 2023-05-02 01:48:56 +02:00
92062d056d divmod() now works on multiple data types including float.
divmodw() has been removed
2023-05-02 01:19:53 +02:00
06368ab0a1 sqrt() now works on multiple data types including float.
no need to use floats.sqrtf() anymore
2023-05-02 01:19:53 +02:00
38efe25c68 abs() now works on multiple data types including float.
no need to use floats.fabs() anymore
2023-05-02 01:19:53 +02:00
319079de7a sqrt 2023-05-02 01:19:53 +02:00
025bf900a5 min max docs, added floats.minf() and maxf() 2023-05-02 01:19:53 +02:00
2885f4f7b1 fix 2023-05-02 01:19:53 +02:00
c07eda15b1 adding min() and max() 2023-05-02 01:19:53 +02:00
4274296cf3 api change: new 'cbm' module that now contains the common CBM kernal variables and routines. 2023-05-02 01:19:53 +02:00
76a203d4df api change: rename builtin func sqrt16 to sqrtw 2023-05-02 01:19:53 +02:00
24f37e2062 fix 2023-05-02 01:19:36 +02:00
f465b2e2a0 some improvements to IR peephole optimizer 2023-05-02 00:29:04 +02:00
ce00e49a89 version 8.12 2023-04-30 14:04:54 +02:00
d494f9d66b fix 2023-04-29 18:04:08 +02:00
c35a183a64 extra fix 2023-04-29 17:24:01 +02:00
9cdd5fe7f2 fix byte to word sign extension error in certain cases 2023-04-29 17:14:50 +02:00
c21428215e fix possible mkword() error 2023-04-29 14:39:14 +02:00
64d5af46f5 fix IDEA kotlin version 2023-04-29 14:23:40 +02:00
25846ea18a fix zsound stream example (missing sound file) 2023-04-29 13:02:24 +02:00
798383596d fix %option merge possible error 2023-04-29 00:01:59 +02:00
9ca71bc937 fix %option merge not choosing correct block to merge into 2023-04-28 23:52:02 +02:00
5407429ec0 improve error message 2023-04-28 23:32:19 +02:00
ee5c94f6db c128: fix key status zp location symbols 2023-04-28 20:43:26 +02:00
91045afbee document limited fp support 2023-04-28 18:18:41 +02:00
3f64782023 c128: remove floats module 2023-04-28 17:48:54 +02:00
f8d35f9502 c128: no FP support 2023-04-28 17:43:42 +02:00
ea78d3ec9a c128: better ZP definition 2023-04-28 17:08:56 +02:00
e056a28316 c128: fix memory bank resetting 2023-04-28 04:02:07 +02:00
0bea721c2e docs 2023-04-27 01:26:25 +02:00
e1b89494d0 tiny psg improvement to avoid clicks more on changing freq or envelope, added cx16.vpoke_mask() 2023-04-26 22:45:32 +02:00
cd8e7f3912 psg comment 2023-04-24 01:23:03 +02:00
50604c25c2 remove obsolete comments, updated links and docs. 2023-04-23 15:13:53 +02:00
aa6b2357d8 fix void warnings 2023-04-18 23:47:31 +02:00
5b2d29bef6 improved and added a few system routines for the cx16 2023-04-18 23:20:28 +02:00
a296d26328 api change: renamed cx16.push/pop_vera_context() to save/restore_vera_context()
this better reflects its capability because it doesn't use a stack, only a single buffer
2023-04-17 23:37:15 +02:00
d01a26ec61 fix occasional crash when indexing an undefined array variable 2023-04-16 05:23:06 +02:00
efd7d6f0c0 tweak IR call args setting now via special SETPARAM instruction 2023-04-14 02:10:39 +02:00
b55be093be tweak IR 2023-04-11 22:48:20 +02:00
7c1d5cadd7 fix sort and reverse on strings on 6502 codegen 2023-04-10 19:33:24 +02:00
dd1592b03b ir syscalls args via stack instead of fixed r65500+ 2023-04-10 18:02:37 +02:00
9b37ac483f vm fix str to word conversion
ir SYSCALL puts result(s) on value stack,  instead of on hardcoded r0, r1
2023-04-10 16:26:42 +02:00
090820958e ir divmod returns its results on valuestack, to keep consistency with the rule that only 1 register can be a returnvalue 2023-04-10 15:26:30 +02:00
ac21e1be5c vm syscall instruction no longer fixed to r0 2023-04-10 13:44:05 +02:00
5196443b26 fix 2023-04-10 12:16:52 +02:00
c8531cbeb1 remove unused variables from IR output 2023-04-09 23:09:30 +02:00
c560abedba fix compiler crash on rol/ror array value 2023-04-09 22:29:11 +02:00
9b952fbc44 tweaking IR instruction set branch instructions 2023-04-09 22:17:19 +02:00
ccdf05e922 tweaking IR instruction formats 2023-04-09 16:12:16 +02:00
c3d74f2ae9 fix golden ram area for x16, remove romsub restriction
note: romsubs still won't work in the VM but at least they compile again
2023-04-08 00:40:52 +02:00
f47498888c optimize imports 2023-04-07 22:34:23 +02:00
5665a7f0cb also track ir reg types 2023-04-07 22:24:17 +02:00
b8178c6c8d Merge remote-tracking branch 'origin/master'
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	docs/source/todo.rst
2023-04-06 21:25:06 +02:00
4a0f15eb88 some loose ends 2023-04-06 21:19:21 +02:00
c4f53fe525 IR: small optimization 2023-04-05 22:55:54 +02:00
8c93ec52de IR: fix augmented assignments 2023-04-05 22:13:18 +02:00
befe0fff2a IR: fix comparison codegen errors in newexpr path 2023-04-05 00:15:09 +02:00
b6a837cbea fix boolean array with initialization value 2023-04-04 22:11:51 +02:00
4861973899 vm: fix float arrays init values 2023-04-04 00:06:55 +02:00
c593e4b500 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	docs/source/memorymap.odg
#	docs/source/memorymap.svg
2023-04-03 23:04:29 +02:00
5bf78c20d4 update to Kotlin 1.8.20, docs update 2023-04-03 23:04:00 +02:00
5c672130e6 update to Kotlin 1.8.20 2023-04-03 22:42:27 +02:00
d8214d4f12 fix IR array indexing for newexpr 2023-04-03 03:13:35 +02:00
64d1f09ce0 new diagrams 2023-04-03 00:32:12 +02:00
47d0f0ea40 implement missing operators in IR code gen 2023-04-01 02:29:33 +02:00
2d85fd093e Merge branch 'new-expr-codegen'
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt
#	examples/test.p8
2023-03-29 23:56:16 +02:00
d936568b76 added divmod() and divmodw() builtin functions to efficiently compute division and remainder in a single call 2023-03-29 23:46:44 +02:00
4598a83e8e fixing new comparisons 2023-03-29 22:06:32 +02:00
f4bf00ad31 fix string compare and ifelse 2023-03-28 22:46:01 +02:00
07fde7f6cc fix IR same register error 2023-03-28 20:01:26 +02:00
729209574e fixing str compares codegen 2023-03-28 20:01:26 +02:00
f28206d989 new attempt 2023-03-28 20:01:26 +02:00
0c81b32cac todo 2023-03-28 20:01:26 +02:00
11216017cb fix IR same register error 2023-03-28 20:00:21 +02:00
a7b9f53967 fix word comparison bug in asmgen 2023-03-26 23:44:06 +02:00
1fa2e2e37d 3rd party library versions upgrades 2023-03-26 21:36:21 +02:00
f67d5faeb7 allow .123 as float literal syntax. Fixes #103 2023-03-26 21:09:15 +02:00
5cbf859458 cleanup 2023-03-26 15:08:57 +02:00
629ed74d09 got rid of rpn deadend code... 2023-03-25 18:45:17 +01:00
ca2af2ca63 todo 2023-03-25 18:23:33 +01:00
52ab089615 rpn: implement more comparisons 2023-03-25 18:21:10 +01:00
01461a196d implementing optimized comparisons 2023-03-25 00:08:21 +01:00
04832f052a working on doing comparison codegen differently 2023-03-25 00:08:21 +01:00
c8b2c8ae50 extra asmvars now also moved into BSS section instead of taking up space inline 2023-03-25 00:00:29 +01:00
1b81c7fb22 fix warnings 2023-03-24 22:50:01 +01:00
9ccda0247e Merge pull request #102 from Frosty-J/vera
DC_VER0 through 3
2023-03-24 02:10:23 +01:00
a7df4dcf25 added cx16 bubbleuniverse example 2023-03-24 01:59:00 +01:00
d91f47c791 fix cx16 graphics.plot() colors, and FB_set_palette definition 2023-03-24 01:56:29 +01:00
a9ac4e7f44 Even more VERA_DC constants! 2023-03-23 17:29:28 +00:00
fc3ec57437 fix wrong branch in in-place byte equality expression 2023-03-23 00:45:47 +01:00
266f6ab919 check 2023-03-22 20:15:24 +01:00
6218c1c00b fix too greedy expression simplification
could cause problems when variables occur multiple times in the same expression.
Fixes #101
2023-03-22 18:25:28 +01:00
cc81d6fe82 remove traces of ** operator 2023-03-22 00:51:58 +01:00
69f9102f2d rtd fix attempt 2023-03-22 00:31:23 +01:00
beb9275982 rtd fix attempt 2023-03-22 00:16:33 +01:00
abe48713f2 rtd fix attempt 2023-03-22 00:12:47 +01:00
82cfaf2fbb rtd fix attempt 2023-03-22 00:10:18 +01:00
3d3bc4738f rtd fix attempt 2023-03-22 00:07:01 +01:00
2d0746f5a4 rtd fix attempt 2023-03-21 23:52:49 +01:00
9c71e2f1c8 rpn optimizations 2023-03-21 18:41:37 +01:00
134fd62da8 RPN: better handling of bit shifts 2023-03-21 02:58:26 +01:00
2afd283582 optimize RPN 2023-03-21 00:05:32 +01:00
c66734bab0 fix cx16 ubyte to float cast (wrong rom routine) 2023-03-20 23:25:28 +01:00
8e56a61f95 tweak 2023-03-20 22:41:58 +01:00
d265271148 fix rpn variable depth clobber and type error 2023-03-20 22:18:10 +01:00
b40e397b28 fix rpn result type mismatch 2023-03-20 00:58:48 +01:00
35ff1d996a only reuse actual counter vars 2023-03-19 21:53:49 +01:00
deea0b05cb tweak cx16 system init and reset to not reset Vera any more
uses new audio routine to silence the audio
2023-03-19 21:16:23 +01:00
c50c9ca545 Merge branch 'rpn-expressions' 2023-03-19 17:36:20 +01:00
a819b4a5a5 fix RPN issues 2023-03-19 17:35:28 +01:00
df2d7d4734 fix RPN comparison exprs 2023-03-19 16:08:48 +01:00
79ce4098cf todo 2023-03-19 01:34:55 +01:00
374464a1f8 weird condition operator... 2023-03-19 01:32:20 +01:00
c8d0bf27af get rid of useless scope param 2023-03-19 00:58:45 +01:00
6e4ae034b2 more Rpn optimizations 2023-03-19 00:48:12 +01:00
52b560e72d more Rpn optimizations 2023-03-18 19:13:32 +01:00
9b971ad222 fix PeekW and PokeW optimizations 2023-03-18 17:36:32 +01:00
3613162d09 fix RPN string comparisons 2023-03-18 16:55:03 +01:00
3a272e998d Merge branch 'master' into rpn-expressions 2023-03-18 14:42:38 +01:00
d4c750beb4 fix cx16/bdmusic and sincos examples 2023-03-18 14:42:15 +01:00
84b31e65e1 more rpn optimization 2023-03-18 14:23:17 +01:00
7b802bfd3d Merge branch 'master' into rpn-expressions
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
2023-03-18 13:39:14 +01:00
f9c4632b8d c64: remove 2 problematic ZP locations from the free list when using floating point 2023-03-18 13:36:19 +01:00
e4764cd8a6 fix typo in comparison function and in pointer optimization 2023-03-18 12:55:35 +01:00
dd78a3a686 fix typo in comparison function 2023-03-18 12:52:50 +01:00
94c06e13f4 implementing Rpn optimizations 2 2023-03-18 12:43:45 +01:00
e8bebe5a75 implementing Rpn optimizations 2023-03-18 01:13:02 +01:00
5b0e1b4f9e a little rpn refactor 2023-03-17 23:04:56 +01:00
8c0a93779b added first implementation of RPN 6502 codegen - all via stackeval still 2023-03-17 22:28:22 +01:00
9241479da4 add "-rpn" command line switch to transform exprs to RPN in codegen 2023-03-17 22:28:22 +01:00
8ffca93cd5 added transform routine for expr -> RPN 2023-03-17 22:28:22 +01:00
7fea0c124a introduce PtRpn node to replace PtBinaryExpression later 2023-03-17 22:28:22 +01:00
20dbdb20d2 renamed the cx16 VIA register variables to more meaningful names 2023-03-17 22:28:09 +01:00
e6b8e2e8be attempt at doc fix 2023-03-17 22:15:21 +01:00
7c5b7f77cc attempt at doc fix 2023-03-17 22:12:47 +01:00
de84547a21 attempt at doc fix 2023-03-17 22:06:06 +01:00
44676756ae don't print weird position link for library files 2023-03-17 00:50:17 +01:00
b399b0f182 don't print weird position link for dummy positions 2023-03-16 23:37:33 +01:00
1152191f48 add optimization: replace simple for loops by repeat loop 2023-03-15 21:11:37 +01:00
af1b07ad44 add more referencesIdentifier() on ast nodes 2023-03-15 20:44:24 +01:00
b8113fff1e todo 2023-03-15 01:05:48 +01:00
ff6948cf2d syntax defs for unroll 2023-03-14 23:52:07 +01:00
fd25e85d59 added unroll loop construct 2023-03-14 23:37:49 +01:00
c07cd72e85 restored the non=problematic asm optimization steps... 2023-03-14 22:30:50 +01:00
e2c101206c removed a problematic asm optimization step that could result in dysfunctional code when writing to I/O addresses 2023-03-14 22:14:48 +01:00
92276b5769 IR fix unneeded register allocated for array indexing with variable 2023-03-14 21:24:44 +01:00
a2133f61a8 get rid of all the require() checks that test result regs to be different 2023-03-14 01:01:46 +01:00
199adbbcf0 IR: don't allow to have 2 same registers on instructions 2023-03-14 00:45:41 +01:00
dc316fd7b4 IR: more optimal branch instructions for comparisons against zero 2023-03-13 23:17:53 +01:00
025183602f refactor IR returnregs 6 2023-03-13 21:35:23 +01:00
db4619a9d9 refactor IR returnregs 5 2023-03-13 04:16:50 +01:00
451e527b7c refactor IR returnregs 4 2023-03-13 03:54:16 +01:00
54dd3a00df refactor IR returnregs 3 2023-03-13 03:20:06 +01:00
03c5dab79d refactor IR returnregs 2 2023-03-13 02:50:41 +01:00
1fdee861e8 refactor IR returnregs 2023-03-13 00:32:48 +01:00
c12bf991b3 reintegrate into existing IR optimizer 2023-03-12 22:16:20 +01:00
78a097585d new IR call and return instructions to deal with returnregisters 2023-03-12 21:54:59 +01:00
39132327cc added optimizer for IR code
with two very simple optimizations
2023-03-12 20:30:51 +01:00
dc32318cec fix possible string error on inlined subroutines 2023-03-12 18:16:48 +01:00
592f74124c fix startup subroutine linking in VM 2023-03-12 16:09:55 +01:00
e5e63cc5ac catch wrong repeat value 2023-03-11 16:13:02 +01:00
f40e0f786d txt.width() and txt.height() added for vm target 2023-03-11 16:05:45 +01:00
ebd9f1471b fix crash when using const word as pointer and implement 2 missing assign codegen paths 2023-03-11 15:39:03 +01:00
d76547ead4 don't crash on certain undefined symbols, give proper error instead
Also the error handlers in unit tests now de-duplicate messages just like the compiler itself does
2023-03-11 14:58:41 +01:00
4600772e05 fix pokew mistake 2023-03-11 01:03:34 +01:00
ed597423cd fix problem with initializing certain array decls with single value 2023-03-11 00:43:30 +01:00
f20ca06f85 give correct error when using memory mapped var as array pointer 2023-03-11 00:26:19 +01:00
a636d3f394 give correct error on attempt to const array 2023-03-10 23:46:13 +01:00
043df18daa set X to bottom part of eval stack in irq handler. fixes #94 2023-03-10 23:29:34 +01:00
96996bf18e be less aggressive with translating adds/subs into auto inc/decrements, to avoid code bloat 2023-03-10 23:01:55 +01:00
f350137a14 fix array in place assignments
fixes balls and snow examples amongst others
2023-03-10 04:07:50 +01:00
b7a6f3ec75 fix compiler not optimizing x+=1 into x++ anymore 2023-03-10 02:45:25 +01:00
6c34672549 array in-place assignment problem 2023-03-10 02:02:47 +01:00
e779a07bce allow when with byte 1,2,3 for word variables without having to cast the values to word explicitly 2023-03-09 22:15:56 +01:00
9a36e8ba3b todo 2023-03-09 00:00:03 +01:00
c968bacb01 fix pokew() crash with certain address expressions 2023-03-08 23:29:57 +01:00
25199dfb43 change tokenizer so that A,X,Y now are parsed correctly as identifiers as well 2023-03-08 22:57:19 +01:00
48fed4e6fb slight tweak to codegenerator backend interface 2023-03-08 00:14:38 +01:00
fc253237c9 fix issues with reporting inlined subroutines as unused 2023-03-07 23:47:14 +01:00
589948c7f4 fix IR translateIfElseNonZeroComparison for ints + floats 2023-03-07 23:07:51 +01:00
7e69690605 fix IR translateIfFollowedByJustGoto for ints + floats 2023-03-07 22:04:02 +01:00
95f498ba9b fix IR translateIfElseZeroComparison for ints + floats 2023-03-07 21:26:34 +01:00
fd07ae5225 fix various IR file and symboltable issues 2023-03-07 19:40:11 +01:00
8acd94fc89 avoid work 2023-03-05 12:32:58 +01:00
1436480eab added a few more comparison expression optimizations 2023-03-04 16:01:40 +01:00
448d176c24 fix vm crash on empty string 2023-03-04 15:35:54 +01:00
fd269453a4 todos 2023-03-04 14:14:01 +01:00
b3b380964c remove searchParameter() from lookups
it shouldn't be needed to look up subroutine parameters by scoped name
2023-03-04 13:24:33 +01:00
6e9025ebf2 cx16 fix irq statusbit handling and kefrenbars example 2023-03-03 21:58:08 +01:00
3922691b3c limit to 48828 hz sample rate (vera max) 2023-03-03 18:04:21 +01:00
0545b77cf4 ask for filename 2023-03-03 17:24:16 +01:00
6b3f39fa1a oops 2023-03-03 17:17:19 +01:00
3114ab87dc add 8 bit sample width support 2023-03-03 17:12:44 +01:00
00bc99cc7b added cx16/stream-wav example, refactor pcmaudio code 2023-03-03 14:18:13 +01:00
540b3ae2f4 tweak BinaryExpression splitting 2023-02-28 21:45:38 +01:00
dbfe4140e1 improved import search paths 2023-02-28 20:08:11 +01:00
d3675ec254 gone, deprecated 2023-02-27 23:41:22 +01:00
ded2483fc0 cx16 startup code now properly turns off mouse cursor 2023-02-27 23:35:42 +01:00
e62ea388e0 tweak cx16 adpcm example 2023-02-24 01:38:03 +01:00
f20356e9be cx16.callfar signature has been changed to be easier to use 2023-02-23 23:06:20 +01:00
d282a2d846 remove cx16.callrom() just use callfar 2023-02-23 23:02:56 +01:00
4641ac46e7 extra question in porting guide for high ram 2023-02-22 22:56:43 +01:00
ba9268a09e added -varshigh compiler option to move BSS section.
Documented BSS a bit in the manual.
2023-02-22 22:44:29 +01:00
fb9902c536 avoid const fold loop on const bool thing=true
fixes #97
2023-02-22 21:27:08 +01:00
5318ba6c6e shrink evalstack from 2 to 1 page
c64=$cf00-$cfff, x16: $0700-$07ff
2023-02-21 22:52:04 +01:00
fd5ebef488 cx16 startup code now also selects ram bank 1 2023-02-21 21:53:32 +01:00
d9e4f39ddc memset BSS section to zero all at once, less individual var=0 assigns 2023-02-21 00:26:21 +01:00
435b9d8973 get rid of 'noreinit' option for now, because it resulted in unreliable code 2023-02-20 23:29:16 +01:00
0ea70ba656 fix proper initialization of zeropagevars with 'noreinit' 2023-02-20 23:05:27 +01:00
92a07b87d2 clearer 2023-02-20 02:32:36 +01:00
c3c82282ba reinitGlobals option is clearer than the inverse 2023-02-19 19:09:29 +01:00
adc15c24ef introduce bss segments 2023-02-19 18:12:37 +01:00
dddf9a9396 remove explicit 'bss' from St var, changed to 'uninitialized' 2023-02-19 16:50:06 +01:00
9ca6860ffa tweak 2023-02-19 15:08:16 +01:00
f7dd388954 remove unsupported floats.FTOSWRDAY routine. Fixes #96 2023-02-17 18:05:46 +01:00
6012839f0e todo 2023-02-16 23:06:09 +01:00
8e9cbab053 todo 2023-02-16 22:53:16 +01:00
aaf375a57b move some utility methods into Pt Ast nodes itself 2023-02-16 22:45:35 +01:00
3cce985f03 check float bits 2023-02-16 22:22:12 +01:00
c59df6ec20 optimize isZpVar 2023-02-16 00:41:20 +01:00
5c3f41f64d reintroduce explicit PtAugmentedAssign ast node 2023-02-15 22:54:32 +01:00
cf3523f49f Merge branch 'codegen-on-new-ast' 2023-02-14 22:48:11 +01:00
db794752cb fix ast error on inline sub 2023-02-14 22:37:33 +01:00
bceaebe856 fix crash on sort/reverse unused arrays
fixes #95
2023-02-14 00:26:29 +01:00
3916de2921 attempt to clarify docs of cx16.numbanks() 2023-02-13 23:45:53 +01:00
9e0f8c1a97 remove avg() from syntax defs, it doesn't exist anymore 2023-02-13 22:31:06 +01:00
0cbc56b82e remove unused ast print func 2023-02-13 00:19:48 +01:00
b95608f68a new common ICodeGeneratorBackend interface for all code generator classes 2023-02-12 23:52:54 +01:00
b6e5dbd06c optimized away VarDecl.subroutineParameter 2023-02-12 23:19:35 +01:00
914f19be86 version 8.9 2023-02-12 17:38:13 +01:00
f09bcf3fcf Merge branch 'master' into codegen-on-new-ast 2023-02-12 17:36:18 +01:00
d0b18dec8e shuffle variable sorting around to attempt smaller compiled programs 2023-02-12 17:34:33 +01:00
75d486b124 fix variable node casting 2023-02-12 17:04:58 +01:00
4914609485 local varnames and fix uninitialized parents 2023-02-12 16:00:58 +01:00
75bd66326a fix variable zpwish 2023-02-11 15:18:57 +01:00
8f904f75bb Merge branch 'master' into codegen-on-new-ast 2023-02-11 14:40:23 +01:00
549c598f51 variables sorted in asm 2023-02-11 14:35:56 +01:00
ed68d604d6 fix break as indirect jump
fix subroutine param scoped name
2023-02-11 01:21:27 +01:00
f83752f43b update compiler internals diagram 2023-02-09 23:15:19 +01:00
86c22636eb Merge branch 'master' into codegen-on-new-ast 2023-02-09 23:05:54 +01:00
30d20a453b tweak SymbolTable and fix its unittest 2023-02-09 22:58:21 +01:00
fe29d8a23f tweak codegen of inline sub 2023-02-09 21:59:09 +01:00
694d088160 some cleanups about asmsub return registers and types 2023-02-09 03:19:57 +01:00
6aabbffc62 some cleanups 2023-02-09 02:34:18 +01:00
7b59bc8d12 avoid division by zero if host fs hyperload is used which loads instantly 2023-02-08 01:37:49 +01:00
79d0fb0b52 cx16.numbanks() now returns a word because the result can be >255 2023-02-08 00:51:34 +01:00
edf56d34f8 doc about no conditional compilation, fixes #93
also added a note to MEMTOP about 0 result
2023-02-06 23:36:19 +01:00
623329fb33 fix 2023-02-05 17:08:24 +01:00
9f0074eef9 Merge branch 'master' into codegen-on-new-ast
# Conflicts:
#	codeCore/src/prog8/code/ast/AstStatements.kt
2023-02-05 16:44:30 +01:00
6733253826 added printer for Pt Ast tree 2023-02-05 16:42:06 +01:00
f117805129 order 2023-02-05 12:36:32 +01:00
c75b1581d2 lookup via new ST 2023-02-05 01:15:23 +01:00
109e118aba fix sub return register 2023-02-03 21:16:44 +01:00
201b77d5b6 boolean vs byte cast fixing, and pointervar error 2023-02-02 00:57:20 +01:00
a5ca08f33d fix popCpuStack to load values into asmsub register params 2023-02-01 22:00:37 +01:00
86210c4513 clarification 2023-02-01 20:58:40 +01:00
988a3e4446 group the three Pt nodes that represent a variable in the p8 source under single interface IPtVariable 2023-01-31 23:29:15 +01:00
0f5cd22bb7 more codegen fixes 2023-01-31 22:57:26 +01:00
2f5bed36b3 remove bool to ubyte typecasts 2023-01-31 01:25:44 +01:00
5b6534bb28 fix symbol lookup in new ast and minor codegen errors 2023-01-31 00:18:21 +01:00
e31e5b2477 got rid of PtScopeVarsDecls 2023-01-29 13:49:27 +01:00
07d5fafe2e Merge branch 'master' into codegen-on-new-ast
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt
2023-01-29 13:34:00 +01:00
e08da659e5 got rid of PtScopeVarsDecls node, just insert variable nodes directly 2023-01-29 13:25:15 +01:00
8a4979f44c vm target 'zeropage' more robust 2023-01-29 12:47:12 +01:00
e67464325f fix missing symboltable entries for asmgen 2023-01-28 00:00:23 +01:00
94c9b0d23b Merge branch 'master' into codegen-on-new-ast 2023-01-27 22:14:57 +01:00
e9ec310d8a upgrade to kotlin 1.8.0 2023-01-27 22:14:10 +01:00
c78d1e3c39 implemented Pt findTarget and siblings 2023-01-27 01:51:21 +01:00
e94319145f test 2023-01-26 01:41:44 +01:00
3f3b01b5f6 Merge branch 'master' into codegen-on-new-ast 2023-01-26 01:40:30 +01:00
19a2791c65 vm target can't use asmsub at all, give better error for that 2023-01-26 01:38:13 +01:00
4e8ccf0ef3 Merge branch 'master' into codegen-on-new-ast 2023-01-26 00:38:54 +01:00
f1a7d5ecf7 docs 2023-01-26 00:37:30 +01:00
8b05abb80d proper error when attempting to refer to parameters of asmsub by name 2023-01-25 23:41:08 +01:00
48c9349ce9 working on codegen fixes 2023-01-25 01:57:25 +01:00
117d848466 consolidate builtin function definitions into codeCore 2023-01-25 00:23:00 +01:00
9a2df072cc tiny correction 2023-01-24 22:48:44 +01:00
99c62aab36 Merge branch 'master' into codegen-on-new-ast
# Conflicts:
#	examples/test.p8
2023-01-24 01:51:20 +01:00
224278e07a correct openjdk-11 sdk setting in project files instead of just 11 2023-01-24 01:49:38 +01:00
74b69e191e restructure keyboardhandler example due to X register bug, discussed in #94 2023-01-24 01:30:57 +01:00
8cda8a727c update vtui example to vtui 1.0 2023-01-24 01:00:21 +01:00
a3c0c7c96f Merge branch 'master' into codegen-on-new-ast
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt
#	examples/test.p8
2023-01-22 18:30:37 +01:00
4403e4ed62 optimize node renames 2023-01-22 18:26:37 +01:00
9b209823f6 simple test 2023-01-22 17:10:52 +01:00
b2cb125bd4 more 6502 codegen on new Pt-AST. 2023-01-22 17:10:52 +01:00
5e8f767642 6502 codegen on new Pt-AST. 2023-01-22 17:10:52 +01:00
6ee270d9d8 make name a var in new ast to allow cheap renames 2023-01-22 17:10:04 +01:00
44fa309d20 tweak action 2023-01-21 15:29:11 +01:00
58d88f3dd4 github action and update tool docs 2023-01-21 14:47:32 +01:00
e980c23177 github action 2023-01-21 14:25:17 +01:00
75224321bb github action 2023-01-21 14:19:01 +01:00
801af05b20 github action 2023-01-21 14:02:08 +01:00
7611dbbddc fix action 2023-01-21 13:47:09 +01:00
6d40ca15bc github action 2023-01-21 13:39:30 +01:00
32c1c19224 tweak sys.wait() routines on various targets
add warning to docs about FP usage in IRQ
2023-01-20 03:29:10 +01:00
bbf6357222 remove workaround for black cursor at boot as this was recently fixed in the kernal rom. 2023-01-17 23:27:27 +01:00
dc16629c24 todo 2023-01-04 23:57:59 +01:00
3718b9d768 less joins 2023-01-02 02:10:38 +01:00
c25eb088ec redo 8e730ef93d to avoid larger code generated 2023-01-01 23:43:33 +01:00
3feb3e52f8 optimizing scoped names in zeropage 2022-12-31 03:57:51 +01:00
8e730ef93d optimizing scoped names more and fix scoping of identifier names in arrays (pointers) in SymbolTable 2022-12-31 03:20:20 +01:00
e0913a39ab optimizing 2022-12-30 18:50:45 +01:00
7a27fbc001 add params for future changes 2022-12-30 17:43:55 +01:00
ee0dbdad35 don't reshuffle 'start' routine to the top. Fixes zsound examples. 2022-12-30 17:12:01 +01:00
9225f88f89 diskio comments 2022-12-30 15:49:53 +01:00
a04839dd6b vm: add property for custom breakpoint handler 2022-12-30 15:10:13 +01:00
002006517a rewrite bool=bool^1 into bool=not bool 2022-12-29 19:42:38 +01:00
f5b202d438 fix ast type error in float cast to bool 2022-12-28 22:18:21 +01:00
a7df094ff4 don't allow ~ on booleans, also introduce SZ and SNZ instructions in IR to complete the conditional-set instruction list. 2022-12-28 21:19:38 +01:00
1e6fa77633 ir: 4 new instructions to branch on signed <0, >0, <=0, >=0 2022-12-28 13:14:20 +01:00
eb4cff202c removed redundant branch opcodes in IR: BLT(S), BLE(S). Just use swapped BGT(S), BGE(S). 2022-12-28 12:41:05 +01:00
7ee777f405 vm/ir: for loop is now correctly skipped if loopvar>endvar
this is different still in the 6502 codegen, where it wraps around $00!
2022-12-27 18:12:41 +01:00
81bd5c784e don't remove consecutive assigns to IO space location 2022-12-24 18:01:54 +01:00
b526e132a7 better warning + don't remove non-trivial initializer expression for unused variables 2022-12-24 17:22:30 +01:00
1860f66de5 allow "x not in array" as equivalent to "not x in array"
update antlr parsing lib
2022-12-23 17:59:56 +01:00
ded9ada9bc allow "not xx in array" expression in 6502 codegen
fix compiler crash on certain bool to byte casts
2022-12-23 17:07:34 +01:00
d0e6a2eb8b fix compiler crash on hoisting certain vardecls from inner scopes 2022-12-22 18:49:53 +01:00
4e103a1963 making snow example more interesting 2022-12-22 13:04:26 +01:00
475e927178 version 8.8 2022-12-17 23:00:49 +01:00
ca7932c4f0 no longer do return value optimization with tempvar, this caused invalid code sometimes. 2022-12-14 22:33:16 +01:00
8ab47d3321 fix_autostart_square() now preserves X register correctly 2022-12-14 01:07:44 +01:00
def7e87151 fixed silly if-goto expression code in IR codegen where it used too many branching instructions 2022-12-12 22:47:15 +01:00
27568c2bef fixed silly code generated by some NOT-expressions (unused temporary) 2022-12-12 21:57:22 +01:00
0694a187d7 unsigned>0 now optimized into unsigned!=0 2022-12-12 20:37:57 +01:00
832601b36b workaround for black square issue at start 2022-12-11 11:48:41 +01:00
578969c34c optimize redundant rts/bra or rts/jmp generation in when statement 2022-12-10 17:21:15 +01:00
d1d0115aed removed unused option 'keepIR' 2022-12-09 18:44:44 +01:00
c89e6ebfab clarify 2022-12-08 22:21:45 +01:00
ca1089b881 optimized codegen for logical expressions with simple right operand (such as c64.READST() & $40 ) 2022-12-06 20:23:56 +01:00
a1d04f2aad added more $03xx vector definitions to C64/C128/CX16 syslib 2022-12-06 20:23:56 +01:00
bf0604133c fix error in IR for inline asm and BSS vars. 2022-12-04 16:48:44 +01:00
a82b2da16e Fix some FP related assignment issues in 6502 codegen. 2022-12-04 13:03:38 +01:00
f2273c0acc fix several FP rom routine addresses on cx16. 2022-12-03 19:56:54 +01:00
17bedac96c vm: memory is randomized on start instead of 0. P8ir file now has BSS segment. Vm clears BSS vars to 0. 2022-12-03 17:46:06 +01:00
4831fad27a x16 emulators are now launched with PULSE_LATENCY_MSEC=10 env setting to mitigate static noise 2022-12-03 16:19:26 +01:00
5e896cf582 preparing to add Golden RAM 2022-12-03 00:21:31 +01:00
add3491c57 fix possible vardecl issue for prefixed params 2022-11-30 22:56:54 +01:00
f470576822 it's now possible to use symbols that are the same name as 6502 instructions
because these are now prefixed internally before generating assembly.
2022-11-30 18:39:56 +01:00
10760a53a8 optimize cmp word equal/notequal 2022-11-29 20:14:35 +01:00
eee805183c don't overwrite temp vars in complex comparison expressions. Fixes #89 2022-11-29 04:13:25 +01:00
b8fb391022 - ir codegen now allows subroutine having the same name as its block
this is not possible for the 6502 codegen due to 64tass scoping limitation
2022-11-28 21:54:33 +01:00
3c698f1584 fileseek for writing not right now 2022-11-27 21:52:18 +01:00
2fad52d684 the adpcm example can now read wav files directly (so no need anymore to extract the binary frame data from them) 2022-11-27 21:37:40 +01:00
ec64a68a71 fixed compiler crash: unsigned = (-(unsigned as word) as uword) 2022-11-27 17:25:47 +01:00
db55562f6a fixed adpcm playback 2022-11-27 16:36:30 +01:00
d8409a9d2b fix compiler crash: if uwordvar > label 2022-11-26 14:39:03 +01:00
0d0ce6eec1 adpcm plays pcm 2022-11-24 21:03:50 +01:00
483f313eda ir: keep correct child node order in blocks 2022-11-24 01:19:48 +01:00
7b6c742178 fixed diskio.f_read() for small read sizes 2022-11-24 00:23:37 +01:00
d4a35ba6ff got rid of diskio.have_first_byte overhead 2022-11-23 21:53:36 +01:00
68b112837a fix cx16logo.logo() printing correct newlines 2022-11-23 02:25:20 +01:00
e2f20ebf94 fix crash on empty conditional branch statement (if_cc { } ) 2022-11-23 02:14:48 +01:00
f870e4965a added cx16diskio.f_seek() function to seek to a position in an opened file
f_open uses channel 12 now, f_open_w uses 13
2022-11-23 01:48:04 +01:00
7ebcb219d6 void func() now gives warning if func doesn't return a value 2022-11-22 22:54:40 +01:00
c21913a66b ir: keep order of children in block 2022-11-22 02:04:24 +01:00
77e956a29f API change: diskio.list_files doesn't have an internal buffer anymore, you now have to supply a buffer + size yourself. Renamed to list_filenames 2022-11-20 23:27:22 +01:00
08275c406a added chdir/mkdir/rmdir/relabel to cx16diskio 2022-11-20 22:59:44 +01:00
2931e1b87b diskio file lister routines now also put file type (prg, seq, dir) in new diskio.list_filetype variable 2022-11-20 20:22:09 +01:00
153b422496 cx16: retain display mode (composite etc) 2022-11-20 19:19:01 +01:00
0f6a6d6fea attempt to make gfx2 screen mode 0 cleanup more robust on real hardware 2022-11-18 22:53:28 +01:00
91fdb3e2d4 ir: store labels in blocks, but still useless 2022-11-17 00:37:45 +01:00
d8e87bd881 make uword xx = 1<<shift into a word shifting 2022-11-16 01:39:34 +01:00
922033c1b2 main block element order now remains the same as in source 2022-11-16 00:32:00 +01:00
df1793efbf fixed: word << 12 is suddenly an uword (with optimizer on) 2022-11-15 03:00:41 +01:00
836a2700f2 func(x>>1) no longer uses slow stack eval 2022-11-15 02:49:40 +01:00
8f3aaf77a1 fix optimizer hanging on uword xx :: xx >>= 8 / xx=msb(xx) 2022-11-15 01:40:13 +01:00
00c059e5b1 adding cx16/adpcm example 2022-11-15 01:17:28 +01:00
f4f355c74a added cx16/diskspeed example 2022-11-14 17:55:55 +01:00
b465fc5aaf fix bug in word array containment check (prog8_lib.containment_wordarray) that could hang the loop 2022-11-12 23:19:01 +01:00
2d78eaa48d fix gfx2 text color, added cx16 snow example 2022-11-12 22:08:07 +01:00
d08451bccc ir: Block can now contain inline binary 2022-11-12 20:17:23 +01:00
d8e785aed0 ir: fix too greedy chunk removal 2022-11-12 19:56:54 +01:00
267b6f49b5 IRFileReader parses the p8ir file with xml parser 2022-11-12 16:51:20 +01:00
e6688f4b9d clearer error for VM limitation cannot load label address as value 2022-11-12 13:45:02 +01:00
9d7b9771c2 p8ir file format is now valid XML 2022-11-11 23:35:52 +01:00
136a9a39de kotlin 1.7.21 2022-11-10 22:52:07 +01:00
3dcf628fdb fixed subroutine name shadow check 2022-11-10 22:51:37 +01:00
e614e9787a ir: write values as hex into p8ir file 2022-11-08 21:59:05 +01:00
e426fc0922 version 8.7 2022-11-06 22:58:39 +01:00
5d4bfffc7e float.rndseedf() now takes float seed value and is consistent for all CBM compilation targets 2022-11-06 22:53:57 +01:00
207cdaf7a4 fix kefrenbars example (use gfx2 instead of kernal routines) 2022-11-06 17:33:30 +01:00
7315b581ce added gfx2.pget(x,y) to get the pixel color value 2022-11-06 13:40:55 +01:00
38efaae7b2 ir/vm: syscall params in high base register to avoid push/pop 2022-11-06 12:52:09 +01:00
469e042216 vm: replaced prog8_lib.string_compare and others with syscalls 2022-11-04 23:12:13 +01:00
0f1a4b9d8f fixed certain type check error when passing boolean value to ubyte function parameter
fixed virtual machine string comparison syscall
2022-11-03 23:06:03 +01:00
7303c00296 vm: prog8lib.wordarray_contains() fixed 2022-11-03 22:48:47 +01:00
fc55b34d84 ir: fix asmsub multi-value return codegen 2022-11-03 22:29:41 +01:00
6f67fc0e02 ir: get rid of '_' symbol prefix 2022-11-03 21:54:53 +01:00
562d722ad5 codegen: added missing codegen for float array inplace modification 2022-11-03 20:08:46 +01:00
144c1ba3a6 ir: fix float instruction value in formatspec 2022-11-03 19:08:38 +01:00
06b032af91 refactor 2022-11-03 00:20:31 +01:00
3603140114 ir: fix unused code remover 2022-11-02 23:54:52 +01:00
e094785cbd ir: fix unused code remover 2022-11-02 23:16:51 +01:00
e7408224ac ir: remove position tracking from codechunk for now 2022-11-02 22:12:42 +01:00
e67c05c274 ir: fix asmsub contents not appearing in IR file 2022-11-02 20:50:51 +01:00
b22804efaf ir: fix inlineasm linking 2022-10-31 23:59:33 +01:00
890f55f91a fixup compiler internals diagram 2022-10-31 00:39:43 +01:00
cc5fc0b892 Merge branch 'master' into labeledchunks
# Conflicts:
#	examples/test.p8
2022-10-30 23:46:44 +01:00
5efe2b027a ir: fix chunk linkage in optimizer 2022-10-30 23:42:41 +01:00
5b6569d0f9 ir: fix overwriting chunk label 2022-10-30 19:03:02 +01:00
0eda7ac498 vm: don't crash on empty code chunks 2022-10-30 17:05:08 +01:00
a5ef353484 ir: fix memory mapped var as for loop counter 2022-10-30 14:54:47 +01:00
67a36d8d31 more robust 'return' statement checks in subroutines 2022-10-30 14:41:28 +01:00
7cc3cc3990 ir: fix non-code chunk linkage 2022-10-30 12:55:06 +01:00
dc0edc4c2b break also in for 2022-10-29 23:34:59 +02:00
71d2f091e5 Merge pull request #88 from markjreed/fix-mouse_config2
fix: don't ignore shape argument to cx16.mouse_config2
2022-10-29 23:22:14 +02:00
c2f062a391 fix: don't ignore shape argument to cx16.mouse_config2 2022-10-29 17:10:06 -04:00
224f490455 Merge branch 'master' into labeledchunks
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	examples/test.p8
2022-10-29 18:26:09 +02:00
5b35232ab4 fix "fpReg1 out of bounds" crash for vm target for in-place float array assignment. #85 2022-10-29 17:04:39 +02:00
6d6db70e42 remove type widening for bit shifts, to be consistent with other arithmetic operations. Fixes #83 2022-10-29 16:29:41 +02:00
6830e15b4e print warning when bit shifts are too large and result in 0. #83 2022-10-29 15:23:39 +02:00
3f07cad35d remove missing feature from docs 2022-10-29 14:31:40 +02:00
e951340033 BASIC, VICE, C64, zeropage spelling 2022-10-29 14:17:40 +02:00
db8912a735 Kernal spelling 2022-10-29 14:10:11 +02:00
0e297731a3 PETSCII spelling 2022-10-29 14:07:04 +02:00
f20c4f98ac Merge pull request #86 from Frosty-J/docs
Fix typos in documentation
2022-10-29 12:57:55 +02:00
05e60cc7c0 fix array type typo 2022-10-29 12:57:33 +02:00
55b4469767 Merge pull request #87 from Frosty-J/basicsafe
`%zeropage basicsafe` in Hello World
2022-10-29 12:31:28 +02:00
f15516e478 Bracket space 2022-10-29 00:25:54 +01:00
17ceadbadf %zeropage basicsafe in Hello World 2022-10-28 22:49:23 +01:00
8c25b2b316 CommanderX16 -> Commander X16 2022-10-28 22:47:14 +01:00
8b1ae404a3 Commodore-64 -> Commodore 64 2022-10-28 22:45:09 +01:00
13534cd4a9 lowlevel -> low-level 2022-10-28 22:40:36 +01:00
abfb345503 ofcourse -> of course 2022-10-28 22:39:54 +01:00
42ae935496 Various typo fixes 2022-10-28 22:39:15 +01:00
434515d957 fix: array[x] = ~array[x] no longer crashes the codegen 2022-10-27 23:56:38 +02:00
094f7803b7 fix: array[x] = -array[x] no longer crashes the codegen 2022-10-27 23:20:40 +02:00
b0c7bad391 fix: array[x] = -value no longer crashes the codegen 2022-10-27 21:58:37 +02:00
e9a4a905ef preparing to fix the array indexing compiler issue 2022-10-26 23:53:17 +02:00
7b6cd0cfbe cx16.macptr() now has additional argument in the carry flag, to reflect recent X16 kernal api change.
Also now allow bool type for status flag args and returnvalues.
2022-10-26 20:41:10 +02:00
b718b12083 ir/vm fix chunk linkage 2022-10-26 00:12:56 +02:00
cfa7258ff4 various 2022-10-25 23:18:42 +02:00
b70e0a0870 mention syntax highlighting files in the docs 2022-10-25 21:24:38 +02:00
da8eb464b8 add cx16diskio.vload_raw() to load headerless files into vram 2022-10-25 21:12:11 +02:00
8f9d1cfa30 fix regression: indexing pointer variable with word (>255) didn't work anymore since release 8.2 or so 2022-10-24 23:43:47 +02:00
585009ac5c ir: fix syscall numbers and more 2022-10-24 01:57:37 +02:00
30ee65fd14 ir: ensure that block and sub labels are also on the first chunk in said block/sub 2022-10-23 18:54:08 +02:00
76428b16f0 Merge branch 'master' into labeledchunks
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt
#	docs/source/todo.rst
#	examples/test.p8
#	virtualmachine/src/prog8/vm/VirtualMachine.kt
2022-10-23 12:19:02 +02:00
0d7b14e2d8 fix crash when assigning certain memory read to word variable. Fixes #82 2022-10-23 11:57:23 +02:00
a9d19d02b3 helpful error for programs still using the old builtin rnd() and rndw() 2022-10-22 22:36:44 +02:00
adcbe55307 replaced integer RNG with smaller and faster routine. 2022-10-22 22:01:57 +02:00
aa99a7df64 seed info 2022-10-22 17:54:24 +02:00
00afa1ce52 ir: replace RND opcode by syscalls 2022-10-22 17:20:46 +02:00
e94bf4c63c replace rnd()/rndw() builtin functions by regular routines in math module 2022-10-22 17:02:43 +02:00
ec5adffdc2 rnd()/rndf() routines can now be seeded with new rndseed()/rndseedf() routines. fixes #80 2022-10-22 13:34:22 +02:00
733c17ad3a improve docs on if syntax. fixes #81 2022-10-19 23:53:15 +02:00
53b0b562e6 fix check for routine that returns multiple values but in status bit. Fixes #79 2022-10-19 23:23:49 +02:00
fabae6e970 ir: fix handling of labeled chunks 2022-10-16 23:53:17 +02:00
a9f9c40d8a ir: fix handling of labeled chunks 2022-10-13 00:56:44 +02:00
6fc89607d3 ir: moving to labeled chunks, no more IRLabel nodes 2022-10-07 00:34:56 +02:00
2340760f53 rename 2022-10-04 22:54:14 +02:00
39d6d2857e ir: change inline binary a bit 2022-10-04 00:57:08 +02:00
7b722a0001 ir: fix count register uses 2022-10-04 00:25:55 +02:00
e7682119e0 ir: count register uses 2022-10-02 15:56:06 +02:00
af6be44676 ir: adding register usage inspections
fix compiler problems with untrimmed inlined asm, and when only a single return statement is present in a subroutine
2022-09-30 20:25:00 +02:00
5a8f97a0b6 ir: adding last missing features to be able to encode all of Prog8 2022-09-30 16:01:00 +02:00
0d4dd385b8 added '%ir' to write inline IR code, '%asm' is now only for real 6502 assembly.
(%ir is probably only used in the library modules for the virtual machine target)
2022-09-30 15:12:26 +02:00
94f0f3e966 ir: join code chunks 2022-09-30 02:47:33 +02:00
43e31765e5 kotlin 1.7.20 2022-09-29 18:41:20 +02:00
7c1bdfe713 ir: uninitialized vars remain empty, bss section classifier (unused for now as there are no segements yet) 2022-09-28 16:56:50 +02:00
9f09784b55 version 8.6.2 2022-09-27 22:45:48 +02:00
e7a3a89bfb fix windows issue 2022-09-27 22:41:48 +02:00
7ea7e63f44 use require() more often 2022-09-27 18:27:55 +02:00
1d2ce2cbeb consolidate IR line parse function 2022-09-27 18:02:57 +02:00
06cf2e0bd7 vm: fix memory slabs (bsieve example) 2022-09-27 16:32:44 +02:00
9d219ae4b9 refactor 2022-09-27 03:32:39 +02:00
71f5a6c50e remove p8virt from compiler diagram 2022-09-27 02:52:29 +02:00
90b2be2bf4 vm: new memory initialization of array vars 2022-09-27 02:43:50 +02:00
db1aa8fcbd vm: new translation of IRProgram into vm program list 2022-09-27 01:50:00 +02:00
11c000f764 moved codeGenVirtual module into virtualmachine module 2022-09-26 20:00:40 +02:00
4d6dcbd173 ir: consolidate IRCodeInstruction and Instruction 2022-09-26 19:46:44 +02:00
0da117efd2 vm: get rid of .p8virt file and cruft 2022-09-26 19:28:40 +02:00
533c368e32 make IRFileReader's file source more general 2022-09-26 14:47:28 +02:00
8883513b0e attempt to fix readthedocs.io build 2022-09-25 22:19:32 +02:00
455 changed files with 55215 additions and 29686 deletions

13
.github/FUNDING.yml vendored Normal file
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']

51
.github/workflows/all-ci.yml vendored Normal file
View File

@ -0,0 +1,51 @@
name: Build and Test the Prog8 compiler
on:
push:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: build and install recent 64tass
run: |
sudo apt-get install -y make build-essential
git clone --depth=1 https://github.com/irmen/64tass
cd 64tass
make -j4
sudo make install
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: 11
distribution: adopt
- name: Build and test with Gradle
run: ./gradlew build shadowJar --no-daemon
- name: Create compiler shadowJar artifact
uses: actions/upload-artifact@v4
with:
name: prog8-compiler-jar-zipped
path: compiler/build/libs/*-all.jar
- name: Calculate hash
uses: MCJack123/ghaction-generate-release-hashes@v4
if: github.event_name == 'release'
with:
get-assets: true
hash-type: sha256
file-name: hashes.txt
- name: Upload hashes
uses: actions/upload-artifact@v4
if: github.event_name == 'release'
with:
name: Artifact Hashes
path: hashes.txt

2
.gitignore vendored
View File

@ -15,6 +15,7 @@ out/
parser/**/*.interp
parser/**/*.tokens
parser/**/*.java
compiler/src/prog8/buildversion/*
*.py[cod]
*.egg
*.egg-info
@ -32,3 +33,4 @@ compiler/lib/
/prog8compiler.jar
sd*.img
*.d64

View File

@ -9,6 +9,15 @@
</inspection_tool>
<inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="sphinx_rtd_theme" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="false" />
<option name="processLiterals" value="true" />

9
.idea/kotlinc.xml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="11" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.20" />
</component>
</project>

View File

@ -1,19 +1,23 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22" />
<CLASSES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
<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 />
<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://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
<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>

View File

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

View File

@ -1,25 +0,0 @@
<component name="libraryTable">
<library name="github.hypfvieh.dbus.java" type="repository">
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.1/dbus-java-3.3.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.6/jnr-unixsocket-0.38.6.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.2/jnr-ffi-2.2.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1-native.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.1/asm-9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.1/asm-commons-9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.1/asm-analysis-9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.1/asm-tree-9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.1/asm-util-9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.1/jnr-constants-0.10.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.4/jnr-enxio-0.32.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.5/jnr-posix-3.1.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -1,10 +0,0 @@
<component name="libraryTable">
<library name="glassfish.javax.json" type="repository">
<properties include-transitive-deps="false" maven-id="org.glassfish:javax.json:1.1.4" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

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

View File

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

View File

@ -1,51 +1,42 @@
<component name="libraryTable">
<library name="io.kotest.runner.junit5.jvm" type="repository">
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.3.2" />
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.8.0" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.3.2/kotest-runner-junit5-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.3.2/kotest-framework-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.3.2/kotest-assertions-shared-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.1/kotlinx-coroutines-test-jvm-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.3.2/kotest-common-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.3.2/kotest-framework-engine-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.146/classgraph-4.8.146.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.8.0/kotest-runner-junit5-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.8.0/kotest-framework-api-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.8.0/kotest-assertions-shared-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.7.0/kotlinx-coroutines-test-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.8.0/kotest-common-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.8.0/kotest-framework-engine-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.162/classgraph-4.8.162.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.1/kotlinx-coroutines-debug-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.3.2/kotest-framework-discovery-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.3.2/kotest-assertions-core-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.3.2/kotest-assertions-api-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.3.2/kotest-extensions-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.12.3/mockk-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-common/1.12.3/mockk-common-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl/1.12.3/mockk-dsl-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.3/mockk-dsl-jvm-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.12.3/mockk-agent-jvm-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api/1.12.3/mockk-agent-api-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-common/1.12.3/mockk-agent-common-1.12.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.6/byte-buddy-1.12.6.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.6/byte-buddy-agent-1.12.6.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.3.2/kotest-framework-concurrency-jvm-5.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.8.0/kotest-framework-discovery-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.8.0/kotest-assertions-core-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.8.0/kotest-assertions-api-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.8.0/kotest-extensions-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.8.0/kotest-framework-concurrency-jvm-5.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -1,8 +1,8 @@
<component name="libraryTable">
<library name="jetbrains.kotlinx.cli.jvm" type="repository">
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.4" />
<properties include-transitive-deps="false" maven-id="org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.4/kotlinx-cli-jvm-0.3.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-cli-jvm/0.3.6/kotlinx-cli-jvm-0.3.6.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -1,13 +1,10 @@
<component name="libraryTable">
<library name="michael.bull.kotlin.result.jvm" type="repository">
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.20/kotlin-result-jvm-1.1.20.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.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -1,11 +0,0 @@
<component name="libraryTable">
<library name="slf4j.simple" type="repository">
<properties maven-id="org.slf4j:slf4j-simple:1.7.36" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-simple/1.7.36/slf4j-simple-1.7.36.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -1,17 +0,0 @@
<component name="libraryTable">
<library name="takes" type="repository">
<properties maven-id="org.takes:takes:1.20" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.20/takes-1.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.50/cactoos-0.50.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

5
.idea/misc.xml generated
View File

@ -16,10 +16,13 @@
</list>
</option>
</component>
<component name="Black">
<option name="sdkName" value="Python 3.11" />
</component>
<component name="FrameworkDetectionExcludesConfiguration">
<type id="Python" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

2
.idea/modules.xml generated
View File

@ -10,10 +10,8 @@
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />

View File

@ -7,23 +7,21 @@ version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
os: ubuntu-22.04
tools:
python: "3.9"
# You can also specify other tool versions:
# nodejs: "16"
# rust: "1.55"
# golang: "1.17"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf
python: "3.12"
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
fail_on_warning: true
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf
- epub

View File

@ -1,35 +0,0 @@
#### Just a few remarks upfront:
* There is the (gradle/IDEA) module `parser`: that's the parser generated by ANTLR4, in Java. The only file to be edited here is the grammar, `prog8.g4`.
* Then we have the module `compilerAst` - in Kotlin - which uses `parser` and adds AST nodes. Here we put our additions to the generated thing, *including any tests of the parsing stage*.
- the name is a bit misleading, as this module isn't (or, resp. shouldn't be; see below) about *compiling*, only the parsing stage
- also, the tree that comes out isn't much of an *abstraction*, but rather still more or less a parse tree (this might very well change).
- **However, let's not *yet* rename the module.** We'll find a good name during refactoring.
#### Problems with `compilerAst`:
* `ModuleImporter.kt`, doing (Prog8-) module resolution. That's not the parser's job.
* `ParsingFailedError` (in `ModuleParsing.kt`): this exception (it is actually *not* a `java.lang.Error`...) is thrown in a number of places, where other exceptions would make more sense. For example: not finding a file should just yield a `NoSuchFileException`, not this one. The other problem with it is that it does not provide any additional information about the source of parsing error, in particular a `Position`.
* During parsing, character literals are turned into UBYTEs (since there is no basic type e.g. CHAR). That's bad because it depends on a specific character encoding (`IStringEncoding` in `compilerAst/src/prog8/ast/AstToplevel.kt`) of/for some target platform. Note that *strings* are indeed encoded later, in the `compiler` module.
* The same argument applies to `IMemSizer`, and - not entirely sure about that - `IBuiltinFunctions`.
#### Steps to take, in conceptual (!) order:
(note: all these steps have been implemented, rejected or otherwise solved now.)
1. introduce an abstraction `SourceCode` that encapsulates the origin and actual loading of Prog8 source code
- from the local file system (use case: user programs)
- from resources (prog8lib)
- from plain strings (for testing)
2. add subclass `ParseError : ParsingFailedError` which adds information about the *source of parsing error* (`SourceCode` and `Position`). We cannot just replace `ParsingFailedError` right away because it is so widely used (even in the `compiler` module). Therefore we'll just subclass for the time being, add more and more tests requiring the new one to be thrown (or, resp., NOT to be thrown), and gradually transition.
3. introduce a minimal interface to the outside, input: `SourceCode`, output: a tree with a `Module` node as the root
- this will be the Kotlin singleton `Prog8Parser` with the main method `parseModule`
- plus, optionally, method's for registering/unregistering a listener with the parser
- the *only* exception ever thrown / reported to listeners (TBD) will be `ParseError`
- anything related to the lexer, error strategies, character/token streams is hidden from the outside
- to make a clear distinction between the *generated* parser (and lexer) vs. `Prog8Parser`, and to discourage directly using the generated stuff, we'll rename the existing `prog8Parser`/`prog8Lexer` to `Prog8ANTLRParser` and `Prog8ANTLRLexer` and move them to package `prog8.parser.generated`
4. introduce AST node `CharLiteral` and keep them until after identifier resolution and type checking; insert there an AST transformation step that turns them in UBYTE constants (literals)
5. remove uses of `IStringEncoding` from module `compilerAst` - none should be necessary anymore
6. move `IStringEncoding` to module `compiler`
7. same with `ModuleImporter`, then rewrite that (addressing #46)
8. refactor AST nodes and grammar: less generated parse tree nodes (`XyzContext`), less intermediary stuff (private classes in `Antrl2Kotlin.kt`), more compact code. Also: nicer names such as simply `StringLiteral` instead of `StringLiteralValue`
9. re-think `IStringEncoding` to address #38

12
Makefile Normal file
View File

@ -0,0 +1,12 @@
# super simple Makefile to lauch the main gradle targets to build and/or test the prog8 compiler
.PHONY: all test
all:
gradle installdist installshadowdist
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"
test:
gradle build
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"

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,11 +10,35 @@ 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:
https://prog8.readthedocs.io/
How to get it/build it
----------------------
- Download the latest [official release](https://github.com/irmen/prog8/releases) from github.
- Or, if you want/need a bleeding edge development version, you can:
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
- Alternatively, you can also install the compiler as a package on some linux distros:
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
Community
---------
Most of the development on Prog8 and the use of it is currently centered around
the [Commander X16](https://www.commanderx16.com/) retro computer. Their [discord server](https://discord.gg/nS2PqEC) contains a small channel
dedicated to Prog8. Other than that, use the issue tracker on github.
Software license
----------------
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
@ -26,8 +51,8 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code:
What does Prog8 provide?
------------------------
- reduction of source code length over raw assembly
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
- all advantages of a higher level language over having to write assembly code manually
- programs run very fast because compilation to native machine code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects.
- modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings)
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
@ -35,6 +60,7 @@ What does Prog8 provide?
- automatic static variable allocations, automatic string and array variables and string sharing
- subroutines with input parameters and result values
- high-level program optimizations
- no need for forward declarations
- small program boilerplate/compilersupport overhead
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
- conditional branches
@ -59,6 +85,8 @@ What does Prog8 provide?
- "c64": Commodore-64 (6502 like CPU)
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- "pet32": Commodore PET (experimental)
- "atari": Atari 8 bit such as 800XL (experimental)
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
@ -76,7 +104,12 @@ IntelliJ IDEA with the Kotlin plugin).
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
and a recent emulator version (R42 or newer) for the CommanderX16, such as [x16emu](https://cx16forum.com/forum/viewforum.php?f=30)
(preferred, this is the official emulator. If required, source code is [here](https://github.com/X16Community/x16-emulator/)).
There is also [Box16](https://github.com/indigodarkwolf/box16) which has powerful debugging features.
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
Look in the [syntax-files](https://github.com/irmen/prog8/tree/master/syntax-files) directory in the github repository to find them.
Example code

View File

@ -26,16 +26,16 @@ compileTestKotlin {
dependencies {
// should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
srcDir "${project.projectDir}/src"
}
resources {
srcDirs = ["${project.projectDir}/res"]
srcDir "${project.projectDir}/res"
}
}
}

View File

@ -1,8 +1,7 @@
package prog8
package prog8.code
/**
* By convention, the right side of an `Either` is used to hold successful values.
*
*/
sealed class Either<out L, out R> {

View File

@ -1,5 +1,7 @@
package prog8.code
import prog8.code.ast.PtNode
import prog8.code.ast.PtProgram
import prog8.code.core.*
@ -7,23 +9,32 @@ import prog8.code.core.*
* Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
*/
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
fun print() = printIndented(0)
override fun printProperties() { }
class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) {
/**
* The table as a flat mapping of scoped names to the StNode.
* This gives the fastest lookup possible (no need to traverse tree nodes)
*/
val flat: Map<List<String>, StNode> by lazy {
val result = mutableMapOf<List<String>, StNode>()
fun flatten(node: StNode) {
result[node.scopedName] = node
node.children.values.forEach { flatten(it) }
private var cachedFlat: Map<String, StNode>? = null
val flat: Map<String, StNode> get() {
if(cachedFlat!=null)
return cachedFlat!!
val result = mutableMapOf<String, StNode>()
fun collect(node: StNode) {
for(child in node.children) {
result[child.value.scopedName] = child.value
collect(child.value)
}
}
children.values.forEach { flatten(it) }
result
collect(this)
cachedFlat = result
return result
}
fun resetCachedFlat() {
cachedFlat = null
}
val allVariables: Collection<StStaticVariable> by lazy {
@ -55,10 +66,30 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
}
val allMemorySlabs: Collection<StMemorySlab> by lazy {
children.mapNotNull { if (it.value.type == StNodeType.MEMORYSLAB) it.value as StMemorySlab else null }
val vars = mutableListOf<StMemorySlab>()
fun collect(node: StNode) {
for(child in node.children) {
if(child.value.type== StNodeType.MEMORYSLAB)
vars.add(child.value as StMemorySlab)
else
collect(child.value)
}
}
collect(this)
vars
}
override fun lookup(scopedName: List<String>) = flat[scopedName]
override fun lookup(scopedName: String) = flat[scopedName]
fun getLength(name: String): Int? {
val node = flat[name]
return when(node) {
is StMemVar -> node.length
is StMemorySlab -> node.size.toInt()
is StStaticVariable -> node.length
else -> null
}
}
}
@ -79,44 +110,24 @@ enum class StNodeType {
open class StNode(val name: String,
val type: StNodeType,
val position: Position,
val astNode: PtNode,
val children: MutableMap<String, StNode> = mutableMapOf()
) {
lateinit var parent: StNode
val scopedName: List<String> by lazy {
if(type== StNodeType.GLOBAL)
emptyList()
else
parent.scopedName + name
}
val scopedName: String by lazy { scopedNameList.joinToString(".") }
fun lookup(name: String) =
lookupUnqualified(name)
open fun lookup(scopedName: List<String>) =
if(scopedName.size>1) lookupQualified(scopedName) else lookupUnqualified(scopedName[0])
fun lookupOrElse(name: String, default: () -> StNode) =
lookupUnqualified(name) ?: default()
fun lookupOrElse(scopedName: List<String>, default: () -> StNode) =
lookup(scopedName) ?: default()
open fun lookup(scopedName: String) =
lookup(scopedName.split('.'))
private fun lookupQualified(scopedName: List<String>): StNode? {
// a scoped name refers to a name in another namespace, and always stars from the root.
var node = this
while(node.type!= StNodeType.GLOBAL)
node = node.parent
fun lookupUnscopedOrElse(name: String, default: () -> StNode) =
lookupUnscoped(name) ?: default()
for(name in scopedName) {
if(name in node.children)
node = node.children.getValue(name)
else
return null
}
return node
}
fun lookupOrElse(scopedName: String, default: () -> StNode): StNode =
lookup(scopedName.split('.')) ?: default()
private fun lookupUnqualified(name: String): StNode? {
fun lookupUnscoped(name: String): StNode? {
// first consider the builtin functions
var globalscope = this
while(globalscope.type!= StNodeType.GLOBAL)
@ -138,33 +149,32 @@ open class StNode(val name: String,
}
}
fun printIndented(indent: Int) {
print(" ".repeat(indent))
when(type) {
StNodeType.GLOBAL -> print("SYMBOL-TABLE:")
StNodeType.BLOCK -> print("(B) ")
StNodeType.SUBROUTINE -> print("(S) ")
StNodeType.LABEL -> print("(L) ")
StNodeType.STATICVAR -> print("(V) ")
StNodeType.MEMVAR -> print("(M) ")
StNodeType.MEMORYSLAB -> print("(MS) ")
StNodeType.CONSTANT -> print("(C) ")
StNodeType.BUILTINFUNC -> print("(F) ")
StNodeType.ROMSUB -> print("(R) ")
}
printProperties()
println()
children.forEach { (_, node) -> node.printIndented(indent+1) }
}
open fun printProperties() {
print("$name ")
}
fun add(child: StNode) {
children[child.name] = child
child.parent = this
}
private val scopedNameList: List<String> by lazy {
if(type==StNodeType.GLOBAL)
emptyList()
else
parent.scopedNameList + name
}
private fun lookup(scopedName: List<String>): StNode? {
// a scoped name refers to a name in another namespace, and always stars from the root.
var node = this
while(node.type!=StNodeType.GLOBAL)
node = node.parent
for(name in scopedName) {
if(name in node.children)
node = node.children.getValue(name)
else
return null
}
return node
}
}
class StStaticVariable(name: String,
@ -172,48 +182,48 @@ class StStaticVariable(name: String,
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
val onetimeInitializationStringValue: StString?,
val onetimeInitializationArrayValue: StArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish,
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish, // used in the variable allocator
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null
init {
if(length!=null) {
require(onetimeInitializationNumericValue == null)
if(onetimeInitializationArrayValue!=null)
require(length == onetimeInitializationArrayValue.size)
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
}
if(onetimeInitializationNumericValue!=null)
require(dt in NumericDatatypes)
if(onetimeInitializationArrayValue!=null)
if(onetimeInitializationNumericValue!=null) {
require(dt in NumericDatatypes || dt==DataType.BOOL)
}
if(onetimeInitializationArrayValue!=null) {
require(dt in ArrayDatatypes)
require(length==onetimeInitializationArrayValue.size)
}
if(onetimeInitializationStringValue!=null) {
require(dt == DataType.STR)
require(length == onetimeInitializationStringValue.first.length+1)
}
}
override fun printProperties() {
print("$name dt=$dt zpw=$zpwish")
}
}
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
StNode(name, StNodeType.CONSTANT, position) {
override fun printProperties() {
print("$name dt=$dt value=$value")
}
}
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
StNode(name, StNodeType.CONSTANT, astNode)
class StMemVar(name: String,
val dt: DataType,
val address: UInt,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
position: Position) :
StNode(name, StNodeType.MEMVAR, position) {
override fun printProperties() {
print("$name dt=$dt address=${address.toHex()}")
astNode: PtNode) :
StNode(name, StNodeType.MEMVAR, astNode) {
init{
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
if(dt in ArrayDatatypes || dt == DataType.STR)
require(length!=null) { "memory mapped array or string must have known length" }
}
}
@ -221,37 +231,27 @@ class StMemorySlab(
name: String,
val size: UInt,
val align: UInt,
position: Position
astNode: PtNode
):
StNode(name, StNodeType.MEMORYSLAB, position) {
override fun printProperties() {
print("$name size=$size align=$align")
}
}
StNode(name, StNodeType.MEMORYSLAB, astNode)
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, position: Position) :
StNode(name, StNodeType.SUBROUTINE, position) {
override fun printProperties() {
print(name)
}
}
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
StNode(name, StNodeType.SUBROUTINE, astNode)
class StRomSub(name: String,
val address: UInt,
val address: UInt?, // null in case of asmsub, specified in case of romsub
val parameters: List<StRomSubParameter>,
val returns: List<RegisterOrStatusflag>,
position: Position) :
StNode(name, StNodeType.ROMSUB, position) {
override fun printProperties() {
print("$name address=${address.toHex()}")
}
}
val returns: List<StRomSubParameter>,
astNode: PtNode) :
StNode(name, StNodeType.ROMSUB, astNode)
class StSubroutineParameter(val name: String, val type: DataType)
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOf: List<String>?)
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?)
typealias StString = Pair<String, Encoding>
typealias StArray = List<StArrayElement>

View File

@ -0,0 +1,207 @@
package prog8.code
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.VMTarget
import java.util.*
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
fun make(): SymbolTable {
val st = SymbolTable(program)
BuiltinFunctions.forEach {
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
}
val scopestack = Stack<StNode>()
scopestack.push(st)
program.children.forEach {
addToSt(it, scopestack)
}
require(scopestack.size==1)
if(options.compTarget.name != VMTarget.NAME) {
listOf(
PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
).forEach {
it.parent = program
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
}
}
return st
}
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
val stNode = when(node) {
is PtAsmSub -> {
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
StRomSub(node.name, node.address, parameters, returns, node)
}
is PtBlock -> {
StNode(node.name, StNodeType.BLOCK, node)
}
is PtConstant -> {
StConstant(node.name, node.type, node.value, node)
}
is PtLabel -> {
StNode(node.name, StNodeType.LABEL, node)
}
is PtMemMapped -> {
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
}
is PtSub -> {
val params = node.parameters.map {StSubroutineParameter(it.name, it.type) }
StSub(node.name, params, node.returntype, node)
}
is PtVariable -> {
val initialNumeric: Double?
val initialString: StString?
val initialArray: StArray?
val numElements: Int?
val value = node.value
if(value!=null) {
val number = (value as? PtNumber)?.number
initialNumeric = if(number==0.0) null else number // 0 as init value -> just uninitialized
when (value) {
is PtString -> {
initialString = StString(value.value, value.encoding)
initialArray = null
numElements = value.value.length + 1 // include the terminating 0-byte
}
is PtArray -> {
initialArray = makeInitialArray(value)
initialString = null
numElements = initialArray.size
require(node.arraySize?.toInt()==numElements)
}
else -> {
initialString = null
initialArray = null
numElements = node.arraySize?.toInt()
}
}
} else {
initialNumeric = null
initialArray = null
initialString = null
numElements = node.arraySize?.toInt()
}
// if(node.type in SplitWordArrayTypes) {
// ... split array also add _lsb and _msb to symboltable?
// }
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
}
is PtBuiltinFunctionCall -> {
if(node.name=="memory") {
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
require(node.name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val slabname = (node.args[0] as PtString).value
val size = (node.args[1] as PtNumber).number.toUInt()
val align = (node.args[2] as PtNumber).number.toUInt()
// don't add memory slabs in nested scope, just put them in the top level of the ST
scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
}
null
}
else -> null // node is not present in the ST
}
if(stNode!=null) {
scope.peek().add(stNode)
scope.push(stNode)
}
node.children.forEach {
addToSt(it, scope)
}
if(stNode!=null)
scope.pop()
}
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
return value.children.map {
when(it) {
is PtAddressOf -> {
if(it.isFromArrayElement)
TODO("address-of array element $it in initial array value")
StArrayElement(null, it.identifier.name, null)
}
is PtIdentifier -> StArrayElement(null, it.name, null)
is PtNumber -> StArrayElement(it.number, null, null)
is PtBool -> StArrayElement(null, null, it.value)
else -> throw AssemblyError("invalid array element $it")
}
}
}
}
// override fun visit(decl: VarDecl) {
// val node =
// when(decl.type) {
// VarDeclType.VAR -> {
// var initialNumeric = (decl.value as? NumericLiteral)?.number
// if(initialNumeric==0.0)
// initialNumeric=null // variable will go into BSS and this will be set to 0
// val initialStringLit = decl.value as? StringLiteral
// val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
// val initialArrayLit = decl.value as? ArrayLiteral
// val initialArray = makeInitialArray(initialArrayLit)
// if(decl.isArray && decl.datatype !in ArrayDatatypes)
// throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
// val numElements =
// if(decl.isArray)
// decl.arraysize!!.constIndex()
// else if(initialStringLit!=null)
// initialStringLit.value.length+1 // include the terminating 0-byte
// else
// null
// val bss = if(decl.datatype==DataType.STR)
// false
// else if(decl.isArray)
// initialArray.isNullOrEmpty()
// else
// initialNumeric == null
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
// StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, astNode, decl.position)
// }
// VarDeclType.CONST -> {
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
// StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, astNode, decl.position)
// }
// VarDeclType.MEMORY -> {
// val numElements =
// if(decl.isArray)
// decl.arraysize!!.constIndex()
// else null
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
// StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, astNode, decl.position)
// }
// }
// scopestack.peek().add(node)
// // st.origAstLinks[decl] = node
// }
//
// private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
// if(arrayLit==null)
// return null
// return arrayLit.value.map {
// when(it){
// is AddressOf -> {
// val scopedName = it.identifier.targetNameAndType(program).first
// StArrayElement(null, scopedName)
// }
// is IdentifierReference -> {
// val scopedName = it.targetNameAndType(program).first
// StArrayElement(null, scopedName)
// }
// is NumericLiteral -> StArrayElement(it.number, null)
// else -> throw FatalAstException("weird element dt in array literal")
// }
// }.toList()
// }
//

View File

@ -1,6 +1,9 @@
package prog8.code.ast
import prog8.code.core.*
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.core.Position
import prog8.code.core.SourceCode
import java.nio.file.Path
// New simplified AST for the code generator.
@ -11,16 +14,6 @@ sealed class PtNode(val position: Position) {
val children = mutableListOf<PtNode>()
lateinit var parent: PtNode
fun printIndented(indent: Int) {
print(" ".repeat(indent))
print("${this.javaClass.simpleName} ")
printProperties()
println()
children.forEach { it.printIndented(indent+1) }
}
abstract fun printProperties()
fun add(child: PtNode) {
children.add(child)
child.parent = this
@ -34,25 +27,30 @@ sealed class PtNode(val position: Position) {
fun definingBlock() = findParentNode<PtBlock>(this)
fun definingSub() = findParentNode<PtSub>(this)
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
fun definingISub() = findParentNode<IPtSubroutine>(this)
}
class PtNodeGroup : PtNode(Position.DUMMY) {
override fun printProperties() {}
}
sealed interface IPtStatementContainer
abstract class PtNamedNode(val name: String, position: Position): PtNode(position) {
val scopedName: List<String> by lazy {
var namedParent: PtNode = this.parent
if(namedParent is PtProgram)
listOf(name)
else {
while (namedParent !is PtNamedNode)
namedParent = namedParent.parent
namedParent.scopedName + name
class PtNodeGroup : PtNode(Position.DUMMY), IPtStatementContainer
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
// Note that as an exception, the 'name' is not read-only
// but a var. This is to allow for cheap node renames.
val scopedName: String
get() {
var namedParent: PtNode = this.parent
return if(namedParent is PtProgram)
name
else {
while (namedParent !is PtNamedNode)
namedParent = namedParent.parent
namedParent.scopedName + "." + name
}
}
}
}
@ -61,10 +59,6 @@ class PtProgram(
val memsizer: IMemSizer,
val encoding: IStringEncoding
) : PtNode(Position.DUMMY) {
fun print() = printIndented(0)
override fun printProperties() {
print("'$name'")
}
// fun allModuleDirectives(): Sequence<PtDirective> =
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
@ -73,62 +67,52 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
// returns the main.start subroutine if it exists
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
?.children
?.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 alignment: BlockAlignment,
val source: SourceCode, // taken from the module the block is defined in.
val options: Options,
position: Position
) : PtNamedNode(name, position) {
override fun printProperties() {
print("$name addr=$address library=$library forceOutput=$forceOutput alignment=$alignment")
}
) : 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)
}
class PtInlineAssembly(val assembly: String, position: Position) : PtNode(position) {
override fun printProperties() {}
}
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print(name)
class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
init {
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
}
}
class PtBreakpoint(position: Position): PtNode(position) {
override fun printProperties() {}
}
class PtLabel(name: String, position: Position) : PtNamedNode(name, position)
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) {
override fun printProperties() {
print("filename=$file offset=$offset length=$length")
}
}
class PtBreakpoint(position: Position): PtNode(position)
class PtNop(position: Position): PtNode(position) {
override fun printProperties() {}
}
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position)
class PtScopeVarsDecls(position: Position): PtNode(position) {
override fun printProperties() {}
}
class PtNop(position: Position): PtNode(position)
// find the parent node of a specific type or interface

View File

@ -1,17 +1,14 @@
package prog8.code.ast
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.Position
import prog8.code.core.*
import java.util.*
import kotlin.math.round
import kotlin.math.abs
import kotlin.math.truncate
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type==DataType.UNDEFINED) {
@Suppress("LeakingThis")
when(this) {
@ -23,39 +20,138 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
}
override fun printProperties() {
print(type)
}
infix fun isSameAs(other: PtExpression): Boolean {
return when(this) {
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtAddressOf -> {
if(other !is PtAddressOf)
return false
if (other.type!==type || !(other.identifier isSameAs identifier))
return false
if(other.children.size!=children.size)
return false
if(children.size==1)
return true
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 -> {
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.targetName==targetName
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
is PtNumber -> other is PtNumber && other.type==type && other.number==number
is PtBool -> other is PtBool && other.value==value
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
else -> false
}
}
infix fun isSameAs(target: PtAssignTarget): Boolean {
return when {
target.memory != null && this is PtMemoryByte-> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
}
}
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
fun isSimple(): Boolean {
return when(this) {
is PtAddressOf -> true
is PtArray -> true
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
is PtBinaryExpression -> false
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
is PtMachineRegister -> true
is PtMemoryByte -> address is PtNumber || address is PtIdentifier
is PtBool -> true
is PtNumber -> true
is PtPrefix -> value.isSimple()
is PtRange -> true
is PtString -> true
is PtTypeCast -> value.isSimple()
}
}
/*
fun clone(): PtExpression {
fun withClonedChildrenFrom(orig: PtExpression, clone: PtExpression): PtExpression {
orig.children.forEach { clone.add((it as PtExpression).clone()) }
return clone
}
when(this) {
is PtAddressOf -> return withClonedChildrenFrom(this, PtAddressOf(position))
is PtArray -> return withClonedChildrenFrom(this, PtArray(type, position))
is PtArrayIndexer -> return withClonedChildrenFrom(this, PtArrayIndexer(type, position))
is PtBinaryExpression -> return withClonedChildrenFrom(this, PtBinaryExpression(operator, type, position))
is PtBuiltinFunctionCall -> return withClonedChildrenFrom(this, PtBuiltinFunctionCall(name, void, hasNoSideEffects, type, position))
is PtContainmentCheck -> return withClonedChildrenFrom(this, PtContainmentCheck(position))
is PtFunctionCall -> return withClonedChildrenFrom(this, PtFunctionCall(name, void, type, position))
is PtIdentifier -> return withClonedChildrenFrom(this, PtIdentifier(name, type, position))
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
is PtBool -> return withClonedChildrenFrom(this, PtBool(value, position))
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
is PtTypeCast -> return withClonedChildrenFrom(this, PtTypeCast(type, position))
}
}
*/
}
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
val identifier: PtIdentifier
get() = children.single() as PtIdentifier
get() = children[0] as PtIdentifier
val arrayIndexExpr: PtExpression?
get() = if(children.size==2) children[1] as PtExpression else null
val isFromArrayElement: Boolean
get() = children.size==2
}
class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, 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
init {
require(elementType in NumericDatatypesWithBoolean)
}
}
@ -66,6 +162,9 @@ class PtArray(type: DataType, position: Position): PtExpression(type, position)
return false
return type==other.type && children == other.children
}
val size: Int
get() = children.size
}
@ -81,26 +180,25 @@ class PtBuiltinFunctionCall(val name: String,
val args: List<PtExpression>
get() = children.map { it as PtExpression }
override fun printProperties() {
print("$name void=$void noSideFx=$hasNoSideEffects")
}
}
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
val left: PtExpression
get() = children[0] as PtExpression
val right: PtExpression
get() = children[1] as PtExpression
override fun printProperties() {
print("$operator -> $type")
init {
if(operator in ComparisonOperators + LogicalOperators)
require(type==DataType.BOOL)
else
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
}
}
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) {
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
val element: PtExpression
get() = children[0] as PtExpression
val iterable: PtIdentifier
@ -108,7 +206,7 @@ class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, posit
}
class PtFunctionCall(val functionName: List<String>,
class PtFunctionCall(val name: String,
val void: Boolean,
type: DataType,
position: Position) : PtExpression(type, position) {
@ -119,40 +217,60 @@ class PtFunctionCall(val functionName: List<String>,
val args: List<PtExpression>
get() = children.map { it as PtExpression }
override fun printProperties() {
print("${functionName.joinToString(".")} void=$void")
}
}
class PtIdentifier(val ref: List<String>, val targetName: List<String>, type: DataType, position: Position) : PtExpression(type, position) {
override fun printProperties() {
print("$ref --> $targetName $type")
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) {
override fun toString(): String {
return "[PtIdentifier:$name $type $position]"
}
fun copy() = PtIdentifier(name, type, position)
}
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
val address: PtExpression
get() = children.single() as PtExpression
override fun printProperties() {}
}
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
companion object {
fun fromNumber(number: Number, position: Position): PtBool =
PtBool(if(number==0.0) false else true, position)
}
override fun hashCode(): Int = Objects.hash(type, value)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtBool)
return false
return value==other.value
}
override fun toString() = "PtBool:$value"
fun asInt(): Int = if(value) 1 else 0
}
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type!=DataType.FLOAT) {
val rounded = round(number)
if (rounded != number)
throw IllegalArgumentException("refused rounding of float to avoid loss of precision @$position")
}
companion object {
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
}
override fun printProperties() {
print("$number ($type)")
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("use PtBool instead")
if(type!=DataType.FLOAT) {
val trunc = truncate(number)
if (trunc != number)
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
}
}
override fun hashCode(): Int = Objects.hash(type, number)
@ -160,10 +278,15 @@ 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)
override fun toString() = "PtNumber:$type:$number"
}
@ -172,13 +295,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
get() = children.single() as PtExpression
init {
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
if(operator !in setOf("+", "-", "~"))
throw IllegalArgumentException("invalid prefix operator: $operator")
}
override fun printProperties() {
print(operator)
require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" }
}
}
@ -191,15 +308,36 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
val step: PtNumber
get() = children[2] as PtNumber
override fun printProperties() {}
fun toConstantIntegerRange(): IntProgression? {
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
return when {
fromVal <= toVal -> when {
stepVal <= 0 -> IntRange.EMPTY
stepVal == 1 -> fromVal..toVal
else -> fromVal..toVal step stepVal
}
else -> when {
stepVal >= 0 -> IntRange.EMPTY
stepVal == -1 -> fromVal downTo toVal
else -> fromVal downTo toVal step abs(stepVal)
}
}
}
val fromLv = from as? PtNumber
val toLv = to as? PtNumber
val stepLv = step as? PtNumber
if(fromLv==null || toLv==null || stepLv==null)
return null
val fromVal = fromLv.number.toInt()
val toVal = toLv.number.toInt()
val stepVal = stepLv.number.toInt()
return makeRange(fromVal, toVal, stepVal)
}
}
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
override fun printProperties() {
print("$encoding:\"$value\"")
}
override fun hashCode(): Int = Objects.hash(value, encoding)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtString)
@ -215,13 +353,9 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
}
// special node that isn't created from compiling user code, but used internally
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) {
override fun printProperties() {
print("reg=$register $type")
}
}
// special node that isn't created from compiling user code, but used internally in the Intermediate Code
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else if(expr is PtBool) expr.asInt().toDouble() else null
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else if(expr is PtBool) expr.asInt() else null

View File

@ -0,0 +1,181 @@
package prog8.code.ast
import prog8.code.core.*
/**
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
* passing it as a String to the specified receiver function.
*/
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
fun txt(node: PtNode): String {
return when(node) {
is PtAssignTarget -> "<target>"
is PtAssignment -> "<assign>"
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
is PtBreakpoint -> "%breakpoint"
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> {
if(node.isFromArrayElement)
"& array-element"
else
"&"
}
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
is PtBuiltinFunctionCall -> {
val str = if(node.void) "void " else ""
str + node.name + "()"
}
is PtContainmentCheck -> "in"
is PtFunctionCall -> {
val str = if(node.void) "void " else ""
str + node.name + "()"
}
is PtIdentifier -> "${node.name} ${type(node.type)}"
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
is PtMemoryByte -> "@()"
is PtNumber -> {
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
"$numstr ${type(node.type)}"
}
is PtBool -> node.value.toString()
is PtPrefix -> node.operator
is PtRange -> "<range>"
is PtString -> "\"${node.value.escape()}\""
is PtTypeCast -> "as ${node.type.name.lowercase()}"
is PtForLoop -> "for"
is PtIfElse -> "ifelse"
is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}"
is PtInlineAssembly -> {
if(node.isIR)
"%ir {{ ...${node.assembly.length} characters... }}"
else
"%asm {{ ...${node.assembly.length} characters... }}"
}
is PtJump -> {
if(node.identifier!=null)
"goto ${node.identifier.name}"
else if(node.address!=null)
"goto ${node.address.toHex()}"
else
"???"
}
is PtAsmSub -> {
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 {
"-> ${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"
} else {
str + "romsub ${node.address.toHex()} = ${node.name}($params) $clobbers $returns"
}
}
is PtBlock -> {
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 -> {
val value = when(node.type) {
DataType.BOOL -> if(node.value==0.0) "false" else "true"
in IntegerDatatypes -> node.value.toInt().toString()
else -> node.value.toString()
}
"const ${node.type.name.lowercase()} ${node.name} = $value"
}
is PtLabel -> "${node.name}:"
is PtMemMapped -> {
if(node.type in ArrayDatatypes) {
val arraysize = if(node.arraySize==null) "" else node.arraySize.toString()
val eltType = ArrayToElementTypes.getValue(node.type)
"&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}"
} else {
"&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}"
}
}
is PtSub -> {
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()}"
str
}
is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
}
else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] $split ${node.name}"
}
else
"${node.type.name.lowercase()} ${node.name}"
if(node.value!=null)
str + " = " + txt(node.value)
else
str
}
is PtNodeGroup -> if(node.children.isNotEmpty()) "<group>" else ""
is PtNop -> "nop"
is PtProgram -> "PROGRAM ${node.name}"
is PtRepeatLoop -> "repeat"
is PtReturn -> "return"
is PtSubroutineParameter -> "${node.type.name.lowercase()} ${node.name}"
is PtWhen -> "when"
is PtWhenChoice -> {
if(node.isElse)
"else"
else
"->"
}
}
}
if(root is PtProgram) {
output(txt(root))
root.children.forEach {
walkAst(it) { node, depth ->
val txt = txt(node)
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
if(!library || !skipLibraries) {
if (txt.isNotEmpty())
output(" ".repeat(depth) + txt(node))
}
}
}
println()
} else {
walkAst(root) { node, depth ->
val txt = txt(node)
val library = if(node is PtBlock) node.library else node.definingBlock()?.library==true
if(!library || !skipLibraries) {
if (txt.isNotEmpty())
output(" ".repeat(depth) + txt(node))
}
}
}
}
fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Unit) {
fun recurse(node: PtNode, depth: Int) {
act(node, depth)
node.children.forEach { recurse(it, depth+1) }
}
recurse(root, 0)
}

View File

@ -3,87 +3,54 @@ package prog8.code.ast
import prog8.code.core.*
sealed interface IPtSubroutine {
val name: String
}
class PtAsmSub(
name: String,
val address: UInt?,
val clobbers: Set<CpuRegister>,
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
val returnTypes: List<DataType>, // TODO join with register as Pairs ?
val retvalRegisters: List<RegisterOrStatusflag>,
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
val inline: Boolean,
position: Position
) : PtNamedNode(name, position) {
override fun printProperties() {
print("$name inline=$inline")
}
}
) : PtNamedNode(name, position), IPtSubroutine
class PtSub(
name: String,
val parameters: List<PtSubroutineParameter>,
val returntype: DataType?,
val inline: Boolean,
position: Position
) : PtNamedNode(name, position) {
override fun printProperties() {
print(name)
}
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes })
throw AssemblyError("non-numeric parameter")
if(returntype!=null && returntype !in NumericDatatypes)
throw AssemblyError("non-numeric returntype $returntype")
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
throw AssemblyError("non-numeric/non-bool parameter")
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
throw AssemblyError("non-numeric/non-bool returntype $returntype")
parameters.forEach { it.parent=this }
}
}
class PtSubroutineParameter(val name: String, val type: DataType, position: Position): PtNode(position) {
override fun printProperties() {
print("$type $name")
}
}
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
class PtAssignment(position: Position) : PtNode(position) {
sealed interface IPtAssignment {
val children: MutableList<PtNode>
val target: PtAssignTarget
get() = children[0] as PtAssignTarget
val value: PtExpression
get() = children[1] as PtExpression
override fun printProperties() { }
val isInplaceAssign: Boolean by lazy {
val target = target.children.single() as PtExpression
when(val source = value) {
is PtArrayIndexer -> {
if(target is PtArrayIndexer && source.type==target.type) {
if(target.variable isSameAs source.variable) {
target.index isSameAs source.index
}
}
false
}
is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.targetName==source.targetName
is PtMachineRegister -> target is PtMachineRegister && target.register==source.register
is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address
is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number
is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier
is PtPrefix -> {
(target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value)
||
(target is PtIdentifier && (source.value as? PtIdentifier)?.targetName==target.targetName)
}
is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value
is PtBinaryExpression ->
target isSameAs source.left
else -> false
}
}
}
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
class PtAssignTarget(position: Position) : PtNode(position) {
val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier
@ -102,7 +69,7 @@ class PtAssignTarget(position: Position) : PtNode(position) {
}
}
override fun printProperties() {}
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this)
}
@ -111,10 +78,6 @@ class PtConditionalBranch(val condition: BranchCondition, position: Position) :
get() = children[0] as PtNodeGroup
val falseScope: PtNodeGroup
get() = children[1] as PtNodeGroup
override fun printProperties() {
print(condition)
}
}
@ -125,41 +88,26 @@ class PtForLoop(position: Position) : PtNode(position) {
get() = children[1] as PtExpression
val statements: PtNodeGroup
get() = children[2] as PtNodeGroup
override fun printProperties() {}
}
class PtIfElse(position: Position) : PtNode(position) {
val condition: PtBinaryExpression
get() = children[0] as PtBinaryExpression
val condition: PtExpression
get() = children[0] as PtExpression
val ifScope: PtNodeGroup
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup
get() = children[2] as PtNodeGroup
override fun printProperties() {}
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
}
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) {
override fun printProperties() {
identifier?.printProperties()
if(address!=null) print(address.toHex())
if(generatedLabel!=null) print(generatedLabel)
}
}
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
val target: PtAssignTarget
get() = children.single() as PtAssignTarget
override fun printProperties() {
print(operator)
init {
identifier?.let {it.parent = this }
}
}
@ -169,8 +117,6 @@ class PtRepeatLoop(position: Position) : PtNode(position) {
get() = children[0] as PtExpression
val statements: PtNodeGroup
get() = children[1] as PtNodeGroup
override fun printProperties() {}
}
@ -183,28 +129,28 @@ class PtReturn(position: Position) : PtNode(position) {
else
null
}
override fun printProperties() {}
}
class PtVariable(name: String, val type: DataType, var value: PtExpression?, var arraySize: UInt?, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("$type $name")
sealed interface IPtVariable {
val name: String
val type: DataType
}
class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
init {
value?.let {it.parent=this}
}
}
class PtConstant(name: String, val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("$type $name = $value")
}
}
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
class PtMemMapped(name: String, val type: DataType, val address: UInt, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("&$type $name = ${address.toHex()}")
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
init {
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
}
}
@ -214,8 +160,6 @@ class PtWhen(position: Position) : PtNode(position) {
get() = children[0] as PtExpression
val choices: PtNodeGroup
get() = children[1] as PtNodeGroup
override fun printProperties() {}
}
@ -224,5 +168,4 @@ class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
get() = children[0] as PtNodeGroup
val statements: PtNodeGroup
get() = children[1] as PtNodeGroup
override fun printProperties() {}
}

View File

@ -0,0 +1,136 @@
package prog8.code.core
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String {
val paramConvs = params.mapIndexed { index, it ->
when {
it.reg!=null -> "$index:${it.reg}"
it.variable -> "$index:variable"
else -> "$index:???"
}
}
val returnConv =
when {
returns.reg!=null -> returns.reg.toString()
returns.floatFac1 -> "floatFAC1"
else -> "<no returnvalue>"
}
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
}
}
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
class FSignature(val pure: Boolean, // does it have side effects?
val parameters: List<FParam>,
val returnType: DataType?) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
val returns: ReturnConvention = when (returnType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(returnType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
null -> ReturnConvention(null, null, false)
else -> {
// return type depends on arg type
when (val paramType = actualParamTypes.first()) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(paramType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
else -> ReturnConvention(paramType, null, false)
}
}
}
return when {
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
actualParamTypes.size==1 -> {
// one parameter goes via register/registerpair
val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
}
CallConvention(listOf(paramConv), returns)
}
else -> {
// multiple parameters go via variables
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
CallConvention(paramConvs, returns)
}
}
}
}
val BuiltinFunctions: Map<String, FSignature> = mapOf(
// this set of function have no return value and operate in-place:
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
// cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow:
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT),
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
"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),
"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")

View File

@ -8,20 +8,34 @@ class CompilationOptions(val output: OutputType,
val launcher: CbmPrgLauncherType,
val zeropage: ZeropageType,
val zpReserved: List<UIntRange>,
val zpAllowed: List<UIntRange>,
val floats: Boolean,
val noSysInit: Boolean,
val compTarget: ICompilationTarget,
// these are set later, based on command line arguments or options in the source code:
var loadAddress: UInt,
var slowCodegenWarnings: Boolean = false,
var warnSymbolShadowing: Boolean = false,
var optimize: Boolean = false,
var optimizeFloatExpressions: Boolean = false,
var dontReinitGlobals: Boolean = false,
var asmQuiet: Boolean = false,
var asmListfile: Boolean = false,
var includeSourcelines: Boolean = false,
var dumpVariables: Boolean = false,
var experimentalCodegen: Boolean = false,
var keepIR: Boolean = false,
var evalStackBaseAddress: UInt? = null,
var varsHighBank: Int? = null,
var varsGolden: Boolean = false,
var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false,
var strictBool: Boolean = true,
var breakpointCpuInstruction: String? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()
)
) {
init {
compTarget.machine.initializeMemoryAreas(this)
}
companion object {
val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u)
}
}

View File

@ -58,6 +58,7 @@ fun String.unescape(): String {
'\\' -> '\\'
'n' -> '\n'
'r' -> '\r'
't' -> '\t'
'"' -> '"'
'\'' -> '\''
'u' -> {

View File

@ -1,17 +1,20 @@
package prog8.code.core
enum class DataType {
UBYTE, // pass by value
BYTE, // pass by value
UWORD, // pass by value
WORD, // pass by value
FLOAT, // pass by value
BOOL, // pass by value
UBYTE, // pass by value 8 bits unsigned
BYTE, // pass by value 8 bits signed
UWORD, // pass by value 16 bits unsigned
WORD, // pass by value 16 bits signed
LONG, // pass by value 32 bits signed
FLOAT, // pass by value machine dependent
BOOL, // pass by value bit 0 of a 8 bit byte
STR, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
ARRAY_W, // pass by reference
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference
UNDEFINED;
@ -21,11 +24,12 @@ enum class DataType {
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
UWORD -> targetType.oneOf(UWORD, FLOAT)
WORD -> targetType.oneOf(WORD, FLOAT)
BOOL -> targetType == BOOL
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
LONG -> targetType.oneOf(LONG, FLOAT)
FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this
@ -37,16 +41,17 @@ enum class DataType {
infix fun largerThan(other: DataType) =
when {
this == other -> false
this in ByteDatatypes -> false
this in WordDatatypes -> other in ByteDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> false
this in ByteDatatypesWithBoolean -> false
this in WordDatatypes -> other in ByteDatatypesWithBoolean
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
this == STR && other == UWORD || this == UWORD && other == STR -> false
else -> true
}
infix fun equalsSize(other: DataType) =
when {
this == other -> true
this in ByteDatatypes -> other in ByteDatatypes
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
this in WordDatatypes -> other in WordDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> true
else -> false
@ -74,6 +79,13 @@ enum class RegisterOrPair {
companion object {
val names by lazy { values().map { it.toString()} }
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
return when(cpu) {
CpuRegister.A -> A
CpuRegister.X -> X
CpuRegister.Y -> Y
}
}
}
fun asCpuRegister(): CpuRegister = when(this) {
@ -108,26 +120,29 @@ enum class BranchCondition {
PL, // PL == POS
POS,
VS,
VC,
VC
}
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL
)
val PassByValueDatatypes = NumericDatatypes
val PassByValueDatatypes = NumericDatatypesWithBoolean
val PassByReferenceDatatypes = IterableDatatypes
val ArrayToElementTypes = mapOf(
DataType.STR to DataType.UBYTE,
@ -135,6 +150,8 @@ val ArrayToElementTypes = mapOf(
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_W_SPLIT to DataType.WORD,
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL
)
@ -144,8 +161,10 @@ 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(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
@ -153,6 +172,10 @@ val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
)
val CpuRegisters = setOf(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
)
enum class OutputType {

View File

@ -1,12 +0,0 @@
package prog8.code.core
interface IAssemblyGenerator {
fun compileToAssembly(): IAssemblyProgram?
}
interface IAssemblyProgram {
val name: String
fun assemble(options: CompilationOptions): Boolean
}
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"

View File

@ -0,0 +1,17 @@
package prog8.code.core
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
interface ICodeGeneratorBackend {
fun generate(program: PtProgram,
symbolTable: SymbolTable,
options: CompilationOptions,
errors: IErrorReporter): IAssemblyProgram?
}
interface IAssemblyProgram {
val name: String
fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean
}

View File

@ -3,9 +3,7 @@ package prog8.code.core
interface ICompilationTarget: IStringEncoding, IMemSizer {
val name: String
val machine: IMachineDefinition
val supportedEncodings: Set<Encoding>
val defaultEncoding: Encoding
override fun encodeString(str: String, encoding: Encoding): List<UByte>
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
}

View File

@ -3,10 +3,14 @@ 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.")
}
fun noErrorForLine(position: Position): Boolean
}

View File

@ -13,23 +13,20 @@ interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
var ESTACK_LO: UInt
var ESTACK_HI: UInt
val PROGRAM_LOAD_ADDRESS : UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
val BSSGOLDENRAM_START: UInt
val BSSGOLDENRAM_END: UInt
val opcodeNames: Set<String>
var zeropage: Zeropage
val cpu: CpuType
var zeropage: Zeropage
var golden: GoldenRam
fun initializeZeropage(compilerOptions: CompilationOptions)
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean
fun overrideEvalStack(evalStackBaseAddress: UInt) {
require(evalStackBaseAddress and 255u == 0u)
ESTACK_LO = evalStackBaseAddress
ESTACK_HI = evalStackBaseAddress + 256u
}
}

View File

@ -9,6 +9,8 @@ enum class Encoding(val prefix: String) {
}
interface IStringEncoding {
val defaultEncoding: Encoding
fun encodeString(str: String, encoding: Encoding): List<UByte>
fun decodeString(bytes: List<UByte>, encoding: Encoding): String
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
}

View File

@ -5,21 +5,31 @@ import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
class ZeropageAllocationError(message: String) : Exception(message)
class MemAllocationError(message: String) : Exception(message)
abstract class Zeropage(protected val options: CompilationOptions) {
abstract class MemoryAllocator(protected val options: CompilationOptions) {
data class VarAllocation(val address: UInt, val dt: DataType, val size: Int)
abstract fun allocate(name: String,
datatype: DataType,
numElements: Int?,
position: Position?,
errors: IErrorReporter): Result<VarAllocation, MemAllocationError>
}
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
data class ZpAllocation(val address: UInt, val dt: DataType, val size: Int)
// the variables allocated into Zeropage.
// name (scoped) ==> pair of address to (Datatype + bytesize)
val allocatedVariables = mutableMapOf<List<String>, ZpAllocation>()
val allocatedVariables = mutableMapOf<String, VarAllocation>()
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
@ -32,6 +42,13 @@ abstract class Zeropage(protected val options: CompilationOptions) {
}
}
fun retainAllowed() {
synchronized(this) {
for(allowed in options.zpAllowed)
free.retainAll { it in allowed }
}
}
fun availableBytes() = if(options.zeropage== ZeropageType.DONTUSE) 0 else free.size
fun hasByteAvailable() = if(options.zeropage== ZeropageType.DONTUSE) false else free.isNotEmpty()
fun hasWordAvailable(): Boolean {
@ -41,21 +58,20 @@ abstract class Zeropage(protected val options: CompilationOptions) {
return free.windowed(2).any { it[0] == it[1] - 1u }
}
fun allocate(name: List<String>,
datatype: DataType,
numElements: Int?,
position: Position?,
errors: IErrorReporter
): Result<Pair<UInt, Int>, ZeropageAllocationError> {
override fun allocate(name: String,
datatype: DataType,
numElements: Int?,
position: Position?,
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
require(name.isEmpty() || name !in allocatedVariables) {"name can't be allocated twice"}
if(options.zeropage== ZeropageType.DONTUSE)
return Err(ZeropageAllocationError("zero page usage has been disabled"))
return Err(MemAllocationError("zero page usage has been disabled"))
val size: Int =
when (datatype) {
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
val memsize = options.compTarget.memorySize(datatype, numElements!!)
if(position!=null)
@ -72,9 +88,9 @@ abstract class Zeropage(protected val options: CompilationOptions) {
else
errors.warn("$name: allocating a large value in zeropage; float $memsize bytes", Position.DUMMY)
memsize
} else return Err(ZeropageAllocationError("floating point option not enabled"))
} else return Err(MemAllocationError("floating point option not enabled"))
}
else -> return Err(ZeropageAllocationError("cannot put datatype $datatype in zeropage"))
else -> throw MemAllocationError("weird dt")
}
synchronized(this) {
@ -82,30 +98,30 @@ abstract class Zeropage(protected val options: CompilationOptions) {
if(size==1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if(oneSeparateByteFree(candidate))
return Ok(Pair(makeAllocation(candidate, 1, datatype, name), 1))
return Ok(VarAllocation(makeAllocation(candidate, 1, datatype, name), datatype,1))
}
return Ok(Pair(makeAllocation(free[0], 1, datatype, name), 1))
return Ok(VarAllocation(makeAllocation(free[0], 1, datatype, name), datatype,1))
}
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if (sequentialFree(candidate, size))
return Ok(Pair(makeAllocation(candidate, size, datatype, name), size))
return Ok(VarAllocation(makeAllocation(candidate, size, datatype, name), datatype, size))
}
}
}
return Err(ZeropageAllocationError("no more free space in ZP to allocate $size sequential bytes"))
return Err(MemAllocationError("no more free space in ZP to allocate $size sequential bytes"))
}
private fun reserve(range: UIntRange) = free.removeAll(range)
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List<String>): UInt {
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: String): UInt {
require(size>=0)
free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) {
in NumericDatatypes -> ZpAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
DataType.STR -> ZpAllocation(address, datatype, size)
in ArrayDatatypes -> ZpAllocation(address, datatype, size)
in NumericDatatypes, DataType.BOOL -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
DataType.STR -> VarAllocation(address, datatype, size)
in ArrayDatatypes -> VarAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt")
}
}
@ -120,3 +136,38 @@ abstract class Zeropage(protected val options: CompilationOptions) {
abstract fun allocateCx16VirtualRegisters()
}
// TODO: this class is not yet used
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
private var nextLocation: UInt = region.first
override fun allocate(
name: String,
datatype: DataType,
numElements: Int?,
position: Position?,
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
val size: Int =
when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
options.compTarget.memorySize(datatype, numElements!!)
}
DataType.FLOAT -> {
if (options.floats) {
options.compTarget.memorySize(DataType.FLOAT)
} else return Err(MemAllocationError("floating point option not enabled"))
}
else -> throw MemAllocationError("weird dt")
}
return if(nextLocation<=region.last && (region.last + 1u - nextLocation) >= size.toUInt()) {
val result = Ok(VarAllocation(nextLocation, datatype, size))
nextLocation += size.toUInt()
result
} else
Err(MemAllocationError("no more free space in Golden RAM to allocate $size sequential bytes"))
}
}

View File

@ -1,11 +1,10 @@
package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are no longer associative because of Shortcircuit/McCarthy evaluation
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val LogicalOperators = setOf("and", "or", "xor", "not", "in")
val BitwiseOperators = setOf("&", "|", "^", "~")
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
val PrefixOperators = setOf("+", "-", "~", "not")
fun invertedComparisonOperator(operator: String) =
when (operator) {

View File

@ -1,5 +1,6 @@
package prog8.code.core
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import java.nio.file.InvalidPathException
import kotlin.io.path.Path
import kotlin.io.path.absolute
@ -7,6 +8,10 @@ import kotlin.io.path.absolute
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
fun toClickableStr(): String {
if(this===DUMMY)
return ""
if(file.startsWith(LIBRARYFILEPREFIX))
return "$file:$line:$startCol:"
return try {
val path = Path(file).absolute().normalize().toString()
"file://$path:$line:$startCol:"

View File

@ -3,6 +3,7 @@ package prog8.code.core
import java.io.File
import java.io.IOException
import java.nio.file.Path
import java.text.Normalizer
import kotlin.io.path.Path
import kotlin.io.path.readText
@ -54,22 +55,25 @@ sealed class SourceCode {
/**
* filename prefix to designate library files that will be retreived from internal resources rather than disk
*/
const val libraryFilePrefix = "library:"
const val stringSourcePrefix = "string:"
const val LIBRARYFILEPREFIX = "library:"
const val STRINGSOURCEPREFIX = "string:"
val curdir: Path = Path(".").toAbsolutePath()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
fun isRegularFilesystemPath(pathString: String) =
!(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix))
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
}
/**
* 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)}"
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
override val name = "<unnamed-text>"
}
@ -92,7 +96,8 @@ sealed class SourceCode {
val normalized = path.normalize()
origin = relative(normalized).toString()
try {
text = normalized.readText()
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) }
@ -110,7 +115,7 @@ sealed class SourceCode {
override val isFromResources = true
override val isFromFilesystem = false
override val origin = "$libraryFilePrefix$normalized"
override val origin = "$LIBRARYFILEPREFIX$normalized"
override val text: String
override val name: String
@ -124,7 +129,8 @@ sealed class SourceCode {
)
}
val stream = object {}.javaClass.getResourceAsStream(normalized)
text = stream!!.reader().use { it.readText() }
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
}
}
@ -139,3 +145,33 @@ sealed class SourceCode {
override val text: String = "<generated code node, no text representation>"
}
}
object SourceLineCache {
private val cache = mutableMapOf<String, List<String>>()
private fun getCachedFile(file: String): List<String> {
val existing = cache[file]
if(existing!=null)
return existing
if (SourceCode.isRegularFilesystemPath(file)) {
val source = SourceCode.File(Path(file))
cache[file] = source.text.split('\n', '\r').map { it.trim() }
return cache.getValue(file)
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
cache[file] = source.text.split('\n', '\r').map { it.trim()}
return cache.getValue(file)
}
return emptyList()
}
fun retrieveLine(position: Position): String? {
if (position.line>0) {
val lines = getCachedFile(position.file)
if(lines.isNotEmpty())
return lines[position.line-1]
}
return null
}
}

View File

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

View File

@ -7,7 +7,6 @@ import prog8.code.target.atari.AtariMachineDefinition
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = AtariMachineDefinition()
override val supportedEncodings = setOf(Encoding.ATASCII)
override val defaultEncoding = Encoding.ATASCII
companion object {
@ -16,13 +15,17 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
else -> throw IllegalArgumentException("invalid datatype")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -11,7 +11,6 @@ import prog8.code.target.cbm.CbmMemorySizer
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = C128MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII
companion object {

View File

@ -11,10 +11,11 @@ import prog8.code.target.cbm.CbmMemorySizer
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = C64MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII
companion object {
const val NAME = "c64"
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
}
}

View File

@ -11,7 +11,6 @@ import prog8.code.target.cx16.CX16MachineDefinition
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = CX16MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
override val defaultEncoding = Encoding.PETSCII
companion object {

View File

@ -10,6 +10,8 @@ import prog8.code.target.cbm.PetsciiEncoding
object Encoder: IStringEncoding {
override val defaultEncoding: Encoding = Encoding.ISO
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
val coded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
@ -23,7 +25,7 @@ object Encoder: IStringEncoding {
success = { it }
)
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
val decoded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)

View File

@ -0,0 +1,19 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.target.pet.PETMachineDefinition
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = PETMachineDefinition()
override val defaultEncoding = Encoding.PETSCII
companion object {
const val NAME = "pet32"
}
}

View File

@ -6,7 +6,6 @@ import prog8.code.target.virtual.VirtualMachineDefinition
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = VirtualMachineDefinition()
override val supportedEncodings = setOf(Encoding.ISO)
override val defaultEncoding = Encoding.ISO
companion object {
@ -15,13 +14,16 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
else -> throw IllegalArgumentException("invalid datatype")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -1,7 +1,6 @@
package prog8.code.target.atari
import prog8.code.core.*
import prog8.code.target.c64.normal6502instructions
import java.nio.file.Path
@ -14,13 +13,15 @@ class AtariMachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = 6
override val PROGRAM_LOAD_ADDRESS = 0x2000u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
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
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.output == OutputType.XEX)
@ -57,9 +58,8 @@ class AtariMachineDefinition: IMachineDefinition {
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
override fun initializeZeropage(compilerOptions: CompilationOptions) {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = AtariZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
override val opcodeNames = normal6502instructions
}

View File

@ -13,6 +13,10 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
init {
if (options.floats) {
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
@ -39,11 +43,12 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
}
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {

View File

@ -1,7 +1,7 @@
package prog8.code.target.c128
import prog8.code.core.*
import prog8.code.target.c64.normal6502instructions
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
@ -15,11 +15,13 @@ class C128MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
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
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
@ -37,7 +39,7 @@ class C128MachineDefinition: IMachineDefinition {
}
println("\nStarting C-128 emulator x128...")
val viceMonlist = viceMonListName(programNameWithPath.toString())
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
@ -47,9 +49,8 @@ class C128MachineDefinition: IMachineDefinition {
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeZeropage(compilerOptions: CompilationOptions) {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C128Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
}
override val opcodeNames = normal6502instructions
}

View File

@ -6,15 +6,22 @@ import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
// reference: "Mapping the C128" zero page chapter.
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte
override val SCRATCH_REG = 0x9cu // temp storage for a register, must be B1+1
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
override val SCRATCH_REG = 0x75u // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
init {
if (options.floats) {
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
@ -24,25 +31,45 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
when (options.zeropage) {
ZeropageType.FULL -> {
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09?
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
free.addAll(0x0au..0xffu)
free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x0au..0x8fu) // BASIC variables
free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
}
ZeropageType.KERNALSAFE,
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> {
free.clear() // TODO c128 usable zero page addresses
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
free.addAll(0x1bu..0x23u)
free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
0x55u, 0x56u, 0x57u, 0x58u,
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u
))
// if(options.zeropage==ZeropageType.BASICSAFE) {
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
// }
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {

View File

@ -1,6 +1,7 @@
package prog8.code.target.c64
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.io.IOException
import java.nio.file.Path
@ -15,11 +16,13 @@ class C64MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
override var ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
override val BSSHIGHRAM_START = 0xc000u
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
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
@ -38,7 +41,7 @@ class C64MachineDefinition: IMachineDefinition {
for(emulator in listOf("x64sc", "x64")) {
println("\nStarting C-64 emulator $emulator...")
val viceMonlist = viceMonListName(programNameWithPath.toString())
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
@ -55,22 +58,9 @@ class C64MachineDefinition: IMachineDefinition {
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeZeropage(compilerOptions: CompilationOptions) {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
}
override val opcodeNames = normal6502instructions
}
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
internal val normal6502instructions = setOf(
"adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")

View File

@ -44,7 +44,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zeropage locations used for floating point operations from the free list
free.removeAll(listOf(
0x03, 0x04, 0x10, 0x11, 0x12,
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
@ -53,11 +53,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
).map{it.toUInt()})
}
if(options.zeropage!= ZeropageType.DONTUSE) {
if(options.zeropage != ZeropageType.DONTUSE) {
// add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
} else {
// don't use the zeropage at all
@ -65,9 +65,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
}
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()
@ -75,6 +75,8 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
allocateCx16VirtualRegisters()
}
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {
@ -83,12 +85,12 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
free.remove((4+reg*2).toUInt())
free.remove((5+reg*2).toUInt())
}

View File

@ -208,7 +208,7 @@ object AtasciiEncoding {
return Ok(mapped)
}
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
}
}

View File

@ -6,13 +6,16 @@ import prog8.code.core.*
internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
else -> throw IllegalArgumentException("invalid datatype")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -27,7 +27,7 @@ object IsoEncoding {
}
}
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
} catch (ce: CharConversionException) {

View File

@ -24,7 +24,7 @@ object PetsciiEncoding {
'\ufffe', // 0x0A -> UNDEFINED
'\ufffe', // 0x0B -> UNDEFINED
'\ufffe', // 0x0C -> UNDEFINED
'\r' , // 0x0D -> CARRIAGE RETURN
'\n' , // 0x0D -> LINE FEED (RETURN)
'\u000e', // 0x0E -> SHIFT OUT
'\ufffe', // 0x0F -> UNDEFINED
'\ufffe', // 0x10 -> UNDEFINED
@ -152,7 +152,7 @@ object PetsciiEncoding {
'\uf113', //  0x8A -> FUNCTION KEY 4 (CUS)
'\uf115', //  0x8B -> FUNCTION KEY 6 (CUS)
'\uf117', //  0x8C -> FUNCTION KEY 8 (CUS)
'\n' , // 0x8D -> LINE FEED
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
'\u000f', //  0x8E -> SHIFT IN
'\ufffe', // 0x8F -> UNDEFINED
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
@ -283,7 +283,7 @@ object PetsciiEncoding {
'\ufffe', // 0x0A -> UNDEFINED
'\ufffe', // 0x0B -> UNDEFINED
'\ufffe', // 0x0C -> UNDEFINED
'\r' , // 0x0D -> CARRIAGE RETURN
'\n' , // 0x0D -> LINE FEED (RETURN)
'\u000e', // 0x0E -> SHIFT OUT
'\ufffe', // 0x0F -> UNDEFINED
'\ufffe', // 0x10 -> UNDEFINED
@ -411,7 +411,7 @@ object PetsciiEncoding {
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
'\n' , // 0x8D -> LINE FEED
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
'\u000f', // 0x8E -> SHIFT IN
'\ufffe', // 0x8F -> UNDEFINED
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
@ -1061,6 +1061,7 @@ object PetsciiEncoding {
'}' -> '├'
'|' -> '│'
'\\' -> '╲'
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
else -> chr
}
@ -1076,7 +1077,10 @@ object PetsciiEncoding {
}
else -> {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}Petscii character for '${chr}' (${chr.code})")
if(chr.isISOControl())
throw CharConversionException("no ${case}Petscii character for char #${chr.code}")
else
throw CharConversionException("no ${case}Petscii character for char #${chr.code} '${chr}'")
}
}
}
@ -1119,7 +1123,10 @@ object PetsciiEncoding {
}
else -> {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}Screencode character for '${chr}' (${chr.code})")
if(chr.isISOControl())
throw CharConversionException("no ${case}Screencode character for char #${chr.code}")
else
throw CharConversionException("no ${case}Screencode character for char #${chr.code} '${chr}'")
}
}
}

View File

@ -1,6 +1,7 @@
package prog8.code.target.cx16
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
@ -14,11 +15,13 @@ class CX16MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x0400u // $0400-$04ff inclusive
override var ESTACK_HI = 0x0500u // $0500-$05ff inclusive
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
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
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
@ -35,11 +38,11 @@ class CX16MachineDefinition: IMachineDefinition {
when(selectedEmulator) {
1 -> {
emulator = "x16emu"
extraArgs = emptyList()
extraArgs = listOf("-debug")
}
2 -> {
emulator = "box16"
extraArgs = listOf("-sym", viceMonListName(programNameWithPath.toString()))
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
}
else -> {
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
@ -48,30 +51,18 @@ 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()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
override fun initializeZeropage(compilerOptions: CompilationOptions) {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
}
// 65c02 opcodes, these cannot be used as variable or label names
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "inx", "iny", "jmp", "jsr",
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
"sec", "sed", "sei",
"sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
"rmb", "smb", "stp", "wai")
}

View File

@ -40,16 +40,15 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
val distictFree = free.distinct()
val distinctFree = free.distinct()
free.clear()
free.addAll(distictFree)
free.addAll(distinctFree)
removeReservedFromFreePool()
allocateCx16VirtualRegisters()
retainAllowed()
}
}
@ -58,12 +57,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
}
}
}

View File

@ -0,0 +1,57 @@
package prog8.code.target.pet
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class PETMachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0401u
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
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The pet target only supports the main emulator (Vice).")
return
}
println("\nStarting PET emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process=processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = PETZeropage(compilerOptions)
// there's no golden ram.
}
}

View File

@ -0,0 +1,59 @@
package prog8.code.target.pet
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
// reference: http://www.zimmers.net/cbmpics/cbm/PETx/petmem.txt
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
init {
if (options.floats) {
throw InternalCompilerException("PET target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
ZeropageType.DONTUSE
))
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x00u..0xffu)
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x00u..0xffu)
free.removeAll(listOf(0x8du, 0x8eu, 0x8fu, 0x97u, 0x98u, 0x99u, 0x9au, 0x9bu, 0x9eu, 0xa7u, 0xa8u, 0xa9u, 0xaau)) // these are updated/used by IRQ
}
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> {
free.addAll(0xb3u..0xbau) // TODO more?
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {
TODO("Not known if PET can put the virtual regs in ZP")
}
}

View File

@ -1,30 +1,34 @@
package prog8.code.target.virtual
import prog8.code.core.CompilationOptions
import prog8.code.core.CpuType
import prog8.code.core.IMachineDefinition
import prog8.code.core.Zeropage
import java.io.File
import prog8.code.core.*
import java.nio.file.Path
import kotlin.io.path.isReadable
import kotlin.io.path.name
import kotlin.io.path.readText
class VirtualMachineDefinition: IMachineDefinition {
override val cpu = CpuType.VIRTUAL
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
override val FLOAT_MEM_SIZE = 8 // 64-bits double
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override var ESTACK_LO = 0u // not actually used
override var ESTACK_HI = 0u // not actually used
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
override lateinit var zeropage: Zeropage // not actually used
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
override fun getFloatAsmBytes(num: Number): String {
// little endian binary representation
val bits = num.toDouble().toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { "\$" + it }
return parts.joinToString(", ")
}
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return listOf("syslib")
@ -35,23 +39,37 @@ class VirtualMachineDefinition: IMachineDefinition {
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name
if(filename.endsWith(".p8virt")) {
if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText())
} else if(File("$filename.p8virt").isFile) {
val source = File("$filename.p8virt").readText()
vm.runProgram(source)
} else {
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
if(withExt.isReadable())
vm.runProgram(withExt.readText())
else
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
}
else
throw IllegalArgumentException("vm can only run .p8virt or .p8ir files")
}
override fun isIOAddress(address: UInt): Boolean = false
override fun initializeZeropage(compilerOptions: CompilationOptions) {}
override val opcodeNames = emptySet<String>()
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = VirtualZeropage(compilerOptions)
}
}
interface IVirtualMachineRunner {
fun runProgram(source: String)
fun runProgram(irSource: String)
}
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
override val SCRATCH_B1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_REG: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W2: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
}

View File

@ -1,4 +1,3 @@
plugins {
id 'java'
id 'application'
@ -25,22 +24,40 @@ compileTestKotlin {
dependencies {
implementation project(':codeCore')
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
srcDir "${project.projectDir}/src"
}
resources {
srcDirs = ["${project.projectDir}/res"]
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
// note: there are no unit tests in this module!
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +1,49 @@
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType
import prog8.code.StConstant
import prog8.code.StMemVar
import prog8.code.SymbolTable
import prog8.code.core.IMachineDefinition
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int {
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
var numberOfOptimizations = 0
var linesByFour = getLinesBy(lines, 4)
var mods = optimizeUselessStackByteWrites(linesByFour)
var mods = optimizeIncDec(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeIncDec(linesByFour)
mods = optimizeStoreLoadSame(linesByFour, machine, symbolTable)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeCmpSequence(linesByFour)
mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeStoreLoadSame(linesByFour, machine, program)
mods = optimizeUselessPushPopStack(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods= optimizeJsrRts(linesByFour)
mods = optimizeUnneededTempvarInAdd(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
@ -52,14 +51,14 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
}
var linesByFourteen = getLinesBy(lines, 14)
mods = optimizeSameAssignments(linesByFourteen, machine, program)
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14)
numberOfOptimizations++
}
mods = optimizeSamePointerIndexing(linesByFourteen, machine, program)
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14)
@ -76,60 +75,51 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false)
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
if(modification.remove)
lines.removeAt(modification.lineIndex)
if(modification.remove) {
if(modification.removeLabel)
lines.removeAt(modification.lineIndex)
else {
val line = lines[modification.lineIndex]
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
lines.removeAt(modification.lineIndex)
else if (haslabel(line)) {
val label = keeplabel(line)
if (label.isNotEmpty())
lines[modification.lineIndex] = label
else
lines.removeAt(modification.lineIndex)
} else lines.removeAt(modification.lineIndex)
}
}
else
lines[modification.lineIndex] = modification.replacement!!
}
}
private fun haslabel(line: String): Boolean {
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
}
private fun keeplabel(line: String): String {
if(':' in line)
return line.substringBefore(':') + ':'
val splits = line.split('\t', ' ', limit=2)
return if(splits.size>1) splits[0] + ':' else ""
}
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
// all lines (that aren't empty or comments) in sliding windows of certain size
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// when statement (on bytes) generates a sequence of:
// lda $ce01,x
// cmp #$20
// beq check_prog8_s72choice_32
// lda $ce01,x
// cmp #$21
// beq check_prog8_s73choice_33
// the repeated lda can be removed
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
lines[1].value.trim().startsWith("cmp ") &&
lines[2].value.trim().startsWith("beq ") &&
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
}
}
return mods
}
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
// this is a lot harder for word values because the instruction sequence varies.
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
lines[1].value.trim()=="dex" &&
lines[2].value.trim()=="inx" &&
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
return mods
}
private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
private fun optimizeSameAssignments(
linesByFourteen: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition,
symbolTable: SymbolTable
): List<Modification> {
// Optimize sequential assignments of the same value to various targets (bytes, words, floats)
// the float one is the one that requires 2*7=14 lines of code to check...
@ -154,8 +144,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val fourthvalue = sixth.substring(4)
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
val address1 = getAddressArg(first, program)
val address2 = getAddressArg(second, program)
val address1 = getAddressArg(first, symbolTable)
val address2 = getAddressArg(second, symbolTable)
if(address1==null || address2==null || (!machine.isIOAddress(address1) && !machine.isIOAddress(address2))) {
mods.add(Modification(lines[4].index, true, null))
mods.add(Modification(lines[5].index, true, null))
@ -168,7 +158,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val secondvalue = third.substring(4)
if(firstvalue==secondvalue) {
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
val address = getAddressArg(first, program)
val address = getAddressArg(first, symbolTable)
if(address==null || !machine.isIOAddress(address))
mods.add(Modification(lines[2].index, true, null))
}
@ -251,7 +241,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val thirdvalue = third.substring(4)
val fourthvalue = fourth.substring(4)
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
val address = getAddressArg(first, program)
val address = getAddressArg(first, symbolTable)
if(address==null || !machine.isIOAddress(address)) {
overlappingMods = true
mods.add(Modification(lines[2].index, true, null))
@ -275,7 +265,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val firstvalue = first.substring(4)
val thirdvalue = third.substring(4)
if(firstvalue==thirdvalue) {
val address = getAddressArg(first, program)
val address = getAddressArg(first, symbolTable)
if(address==null || !machine.isIOAddress(address)) {
overlappingMods = true
mods.add(Modification(lines[2].index, true, null))
@ -295,7 +285,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val secondvalue = second.substring(4)
val thirdvalue = third.substring(4)
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
val address = getAddressArg(first, program)
val address = getAddressArg(first, symbolTable)
if(address==null || !machine.isIOAddress(address)) {
overlappingMods = true
val reg2 = second[2]
@ -308,26 +298,15 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
/*
sta A ; or stz double store, remove this first one
sta A ; or stz
*/
if(!overlappingMods && first.isStoreRegOrZero() && second.isStoreRegOrZero()) {
if(first[2]==second[2]) {
val firstvalue = first.substring(4)
val secondvalue = second.substring(4)
if(firstvalue==secondvalue) {
val address = getAddressArg(first, program)
if(address==null || !machine.isIOAddress(address)) {
overlappingMods = true
mods.add(Modification(lines[0].index, true, null))
}
}
}
}
However, this cannot be done relyably because 'A' could be a constant symbol referring to an I/O address.
We can't see that here and would otherwise delete valid double stores.
*/
}
return mods
}
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
// if Y isn't modified in between we can omit the second LDY:
@ -364,27 +343,57 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
mods.add(Modification(lines[4].index, true, null))
}
}
/*
beq +
lda #1
+
[ optional: label_xxxx_shortcut line here]
beq label_xxxx_shortcut / bne label_xxxx_shortcut
or *_afterif labels.
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
*/
if(first=="beq +" && second=="lda #1" && third=="+") {
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
else if(fourth.startsWith("label_") && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
if((fifth.startsWith("beq label_") || fifth.startsWith("bne label_")) &&
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
}
}
}
return mods
}
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
private fun optimizeStoreLoadSame(
linesByFour: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition,
symbolTable: SymbolTable
): List<Modification> {
val mods = mutableListOf<Modification>()
for (lines in linesByFour) {
val first = lines[1].value.trimStart()
val second = lines[2].value.trimStart()
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("stx ") && second.startsWith("ldx "))
(first.startsWith("ldx ") && second.startsWith("ldx "))
) {
val third = lines[3].value.trimStart()
val attemptRemove =
@ -397,7 +406,7 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
}
else {
// no branch instruction follows, we can remove the load instruction
val address = getAddressArg(lines[2].value, program)
val address = getAddressArg(lines[2].value, symbolTable)
address==null || !machine.isIOAddress(address)
}
@ -423,15 +432,21 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
} 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"))
}
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
(first.startsWith("ldy ") && second.startsWith("sty "))
) {
val firstLoc = first.substring(4).trimStart()
val secondLoc = second.substring(4).trimStart()
if (firstLoc == secondLoc)
mods.add(Modification(lines[2].index, true, null))
}
}
return mods
@ -439,7 +454,8 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
private fun getAddressArg(line: String, program: Program): UInt? {
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
// try to get the constant value address, could return null if it's a symbol instead
val loadArg = line.trimStart().substring(3).trim()
return when {
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
@ -450,22 +466,18 @@ private fun getAddressArg(line: String, program: Program): UInt? {
val identMatch = identifierRegex.find(loadArg)
if(identMatch!=null) {
val identifier = identMatch.value
val decl = program.toplevelModule.lookup(identifier.split(".")) as? VarDecl
if(decl!=null) {
when(decl.type){
VarDeclType.VAR -> null
VarDeclType.CONST,
VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt()
}
when (val symbol = symbolTable.flat[identifier]) {
is StConstant -> symbol.value.toUInt()
is StMemVar -> symbol.address
else -> null
}
else null
} else null
}
else -> loadArg.substring(1).toUIntOrNull()
}
}
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
val mods = mutableListOf<Modification>()
for (lines in linesByFour) {
@ -482,12 +494,18 @@ private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
}
}
return mods
}
private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// jsr Sub + rts -> jmp Sub
// rts + jmp -> remove jmp
// rts + bxx -> remove bxx
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
// and some other optimizations.
val mods = mutableListOf<Modification>()
for (lines in linesByFour) {
val first = lines[0].value
@ -496,6 +514,131 @@ private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
mods += Modification(lines[1].index, true, null)
}
else if (" rts" in first || "\trts" in first) {
if (" jmp" in second || "\tjmp" in second)
mods += Modification(lines[1].index, true, null)
else if (" bra" in second || "\tbra" in second)
mods += Modification(lines[1].index, true, null)
else if (" bcc" in second || "\tbcc" in second)
mods += Modification(lines[1].index, true, null)
else if (" bcs" in second || "\tbcs" in second)
mods += Modification(lines[1].index, true, null)
else if (" beq" in second || "\tbeq" in second)
mods += Modification(lines[1].index, true, null)
else if (" bne" in second || "\tbne" in second)
mods += Modification(lines[1].index, true, null)
else if (" bmi" in second || "\tbmi" in second)
mods += Modification(lines[1].index, true, null)
else if (" bpl" in second || "\tbpl" in second)
mods += Modification(lines[1].index, true, null)
else if (" bvs" in second || "\tbvs" in second)
mods += Modification(lines[1].index, true, null)
else if (" bvc" in second || "\tbvc" in second)
mods += Modification(lines[1].index, true, null)
}
if (!haslabel(second)) {
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
) {
mods.add(Modification(lines[1].index, true, null))
}
else if(" cmp #0" in second || "\tcmp #0" in second) {
// there are many instructions that modify A and set the bits...
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
if(" $instr" in first || "\t$instr" in first) {
mods.add(Modification(lines[1].index, true, null))
}
}
}
}
/*
LDA NUM1
CMP NUM2
BCC LABEL
BEQ LABEL
(or something similar) which branches to LABEL when NUM1 <= NUM2. (In this case NUM1 and NUM2 are unsigned numbers.) However, consider the following sequence:
LDA NUM2
CMP NUM1
BCS LABEL
*/
val tfirst = first.trimStart()
val tsecond = second.trimStart()
val tthird = lines[2].value.trimStart()
val tfourth = lines[3].value.trimStart()
if(tfirst.startsWith("lda") && tsecond.startsWith("cmp") && tthird.startsWith("bcc") && tfourth.startsWith("beq")) {
val label = tthird.substring(4)
if(label==tfourth.substring(4)) {
mods += Modification(lines[0].index, false, " lda ${tsecond.substring(4)}")
mods += Modification(lines[1].index, false, " cmp ${tfirst.substring(4)}")
mods += Modification(lines[2].index, false, " bcs $label")
mods += Modification(lines[3].index, true, null)
}
}
}
return mods
}
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
val mods = mutableListOf<Modification>()
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
if(lines[0].value.trimStart().startsWith("ph$register")) {
if(lines[2].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
if(register!in second
&& !second.startsWith("jsr")
&& !second.startsWith("pl")
&& !second.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
}
else if (lines[3].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
val third = lines[2].value.trimStart().take(6).lowercase()
if(register !in second && register !in third
&& !second.startsWith("jsr") && !third.startsWith("jsr")
&& !second.startsWith("pl") && !third.startsWith("pl")
&& !second.startsWith("ph") && !third.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
}
for (lines in linesByFour) {
optimize('a', lines)
optimize('x', lines)
optimize('y', lines)
}
return mods
}
private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
// this can be performed without the scratch variable: clc / adc something
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
val first = lines[0].value.trimStart()
val second = lines[1].value.trimStart()
val third = lines[2].value.trimStart()
val fourth = lines[3].value.trimStart()
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
if(fourth.substring(4)==first.substring(4)) {
mods.add(Modification(lines[0].index, false, " clc"))
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
return mods
}

View File

@ -1,61 +1,28 @@
package prog8.codegen.cpu6502
import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.expressions.Expression
import prog8.ast.statements.Subroutine
import prog8.code.ast.PtAsmSub
import prog8.code.core.Cx16VirtualRegisters
import prog8.code.core.RegisterOrPair
import prog8.code.core.RegisterOrStatusflag
fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
val order = mutableListOf<Int>()
// order is:
// 1) cx16 virtual word registers,
// 2) paired CPU registers,
// 3) single CPU registers (X last), except A,
// 3) single CPU registers (order Y,X,A),
// 4) CPU Carry status flag
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex()
val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters }
val args = sub.parameters.withIndex()
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters }
val (regsWithoutA, args4) = args3.partition { it.value.second.registerOrPair != RegisterOrPair.A }
val (regA, rest) = args4.partition { it.value.second.registerOrPair != null }
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
val (singleRegs, rest) = args3.partition { it.value.first.registerOrPair != null }
cx16regs.forEach { order += it.index }
pairedRegs.forEach { order += it.index }
regsWithoutA.forEach {
if(it.value.second.registerOrPair != RegisterOrPair.X)
order += it.index
}
regsWithoutA.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
singleRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null})
rest.forEach { order += it.index }
regA.forEach { order += it.index }
require(order.size==sub.parameters.size)
return order
}
fun asmsub6502ArgsHaveRegisterClobberRisk(args: List<Expression>,
paramRegisters: List<RegisterOrStatusflag>): Boolean {
fun isClobberRisk(expr: Expression): Boolean {
when (expr) {
is ArrayIndexedExpression -> {
return paramRegisters.any {
it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
}
}
is BuiltinFunctionCall -> {
if (expr.name == "lsb" || expr.name == "msb")
return isClobberRisk(expr.args[0])
if (expr.name == "mkword")
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
return !expr.isSimple
}
else -> return !expr.isSimple
}
}
return args.size>1 && args.any { isClobberRisk(it) }
}

View File

@ -1,14 +1,8 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import prog8.ast.generatedLabelPrefix
import prog8.code.core.*
import java.io.File
import prog8.code.target.C64Target
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
internal class AssemblyProgram(
@ -20,23 +14,28 @@ internal class AssemblyProgram(
private val prgFile = outputDir.resolve("$name.prg") // CBM prg executable program
private val xexFile = outputDir.resolve("$name.xex") // Atari xex executable program
private val binFile = outputDir.resolve("$name.bin")
private val viceMonListFile = outputDir.resolve(viceMonListName(name))
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
private val listFile = outputDir.resolve("$name.list")
override fun assemble(options: CompilationOptions): Boolean {
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
val assemblerCommand: List<String>
when (compTarget.name) {
in setOf("c64", "c128", "cx16") -> {
in setOf("c64", "c128", "cx16", "pet32") -> {
// CBM machines .prg generation.
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
"-Wall", // "-Wno-strict-bool", "-Werror",
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
)
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet)
command.add("--quiet")
@ -63,12 +62,17 @@ internal class AssemblyProgram(
"atari" -> {
// Atari800XL .xex generation.
// TODO are these options okay?
// TODO are these options okay for atari?
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
"-Wall", // "-Werror", "-Wno-strict-bool"
"--no-monitor"
)
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet)
command.add("--quiet")
@ -104,7 +108,7 @@ internal class AssemblyProgram(
}
private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
val lines = viceMonListFile.toFile().readLines()
viceMonListFile.toFile().outputStream().bufferedWriter().use {
for (line in lines) {
@ -124,24 +128,9 @@ internal class AssemblyProgram(
breakpoints.add("break \$" + match.groupValues[1])
}
val num = breakpoints.size
breakpoints.add(0, "; vice monitor breakpoint list now follows")
breakpoints.add(0, "; breakpoint list now follows")
breakpoints.add(1, "; $num breakpoints have been defined")
breakpoints.add(2, "del")
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
}
}
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
return com.github.michaelbull.result.runCatching {
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").text
}.mapError { NoSuchFileException(File(filename)) }
} else {
val sib = Path(source.origin).resolveSibling(filename)
if (sib.isRegularFile())
Ok(SourceCode.File(sib).text)
else
Ok(SourceCode.File(Path(filename)).text)
}
}

View File

@ -1,918 +0,0 @@
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.code.core.*
import kotlin.math.absoluteValue
internal class ExpressionsAsmGen(private val program: Program,
private val asmgen: AsmGen,
private val allocator: VariableAllocator) {
@Deprecated("avoid calling this as it generates slow evalstack based code")
internal fun translateExpression(expression:Expression) {
if (this.asmgen.options.slowCodegenWarnings) {
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position)
}
translateExpressionInternal(expression)
}
// the rest of the methods are all PRIVATE
private fun translateExpressionInternal(expression: Expression) {
when(expression) {
is PrefixExpression -> translateExpression(expression)
is BinaryExpression -> translateExpression(expression)
is ArrayIndexedExpression -> translateExpression(expression)
is TypecastExpression -> translateExpression(expression)
is AddressOf -> translateExpression(expression)
is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
is NumericLiteral -> translateExpression(expression)
is IdentifierReference -> translateExpression(expression)
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
else -> TODO("missing expression asmgen for $expression")
}
}
private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) {
// only for use in nested expression evaluation
val sub = call.target.targetSubroutine(program)!!
asmgen.saveXbeforeCall(call)
asmgen.translateFunctionCall(call, true)
if(sub.regXasResult()) {
// store the return value in X somewhere that we can access again below
asmgen.out(" stx P8ZP_SCRATCH_REG")
}
asmgen.restoreXafterCall(call)
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for ((_, reg) in returns) {
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
if (reg.registerOrPair != null) {
when (reg.registerOrPair!!) {
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
RegisterOrPair.R0,
RegisterOrPair.R1,
RegisterOrPair.R2,
RegisterOrPair.R3,
RegisterOrPair.R4,
RegisterOrPair.R5,
RegisterOrPair.R6,
RegisterOrPair.R7,
RegisterOrPair.R8,
RegisterOrPair.R9,
RegisterOrPair.R10,
RegisterOrPair.R11,
RegisterOrPair.R12,
RegisterOrPair.R13,
RegisterOrPair.R14,
RegisterOrPair.R15 -> {
asmgen.out(
"""
lda cx16.${reg.registerOrPair.toString().lowercase()}
sta P8ESTACK_LO,x
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
sta P8ESTACK_HI,x
dex
""")
}
}
} else when(reg.statusflag) {
Statusflag.Pc -> {
asmgen.out("""
lda #0
rol a
sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pz -> {
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pv -> {
asmgen.out("""
bvs +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pn -> {
asmgen.out("""
bmi +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
null -> {}
}
}
}
private fun translateExpression(typecast: TypecastExpression) {
translateExpressionInternal(typecast.expression)
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
when(typecast.type) {
DataType.BYTE, DataType.UBYTE -> {}
DataType.WORD, DataType.UWORD -> {}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.WORD -> {
when(typecast.type) {
DataType.BYTE, DataType.UBYTE -> {}
DataType.WORD, DataType.UWORD -> {}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.FLOAT -> {
when(typecast.type) {
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
DataType.FLOAT -> {}
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.STR -> {
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
}
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
else -> throw AssemblyError("weird type")
}
}
private fun translateExpression(expr: AddressOf) {
val name = asmgen.asmVariableName(expr.identifier)
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: NumericLiteral) {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
DataType.UWORD, DataType.WORD -> asmgen.out("""
lda #<${expr.number.toHex()}
sta P8ESTACK_LO,x
lda #>${expr.number.toHex()}
sta P8ESTACK_HI,x
dex
""")
DataType.FLOAT -> {
val floatConst = allocator.getFloatAsmConst(expr.number)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
}
else -> throw AssemblyError("weird type")
}
}
private fun translateExpression(expr: IdentifierReference) {
val varname = asmgen.asmVariableName(expr)
when(expr.inferType(program).getOr(DataType.UNDEFINED)) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
}
in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("stack push weird variable type $expr")
}
}
private fun translateExpression(expr: BinaryExpression) {
// Uses evalstack to evaluate the given expression.
// TODO we're slowly reducing the number of places where this is called and instead replace that by more efficient assignment-form code (using temp var or register for instance).
val leftIDt = expr.left.inferType(program)
val rightIDt = expr.right.inferType(program)
if(!leftIDt.isKnown || !rightIDt.isKnown)
throw AssemblyError("can't infer type of both expression operands")
val leftDt = leftIDt.getOrElse { throw AssemblyError("unknown dt") }
val rightDt = rightIDt.getOrElse { throw AssemblyError("unknown dt") }
// see if we can apply some optimized routines
when(expr.operator) {
"+" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVal = expr.left.constValue(program)?.number?.toInt()
val rightVal = expr.right.constValue(program)?.number?.toInt()
if (leftVal!=null && leftVal in -4..4) {
translateExpressionInternal(expr.right)
if(rightDt in ByteDatatypes) {
val incdec = if(leftVal<0) "dec" else "inc"
repeat(leftVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(leftVal<0) {
repeat(leftVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(leftVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return
}
else if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(expr.left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "dec" else "inc"
repeat(rightVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(rightVal<0) {
repeat(rightVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(rightVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return
}
}
}
"-" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = expr.right.constValue(program)?.number?.toInt()
if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(expr.left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "inc" else "dec"
repeat(rightVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(rightVal>0) {
repeat(rightVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(rightVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return
}
}
}
">>" -> {
val amount = expr.right.constValue(program)?.number?.toInt()
if(amount!=null) {
translateExpressionInternal(expr.left)
when (leftDt) {
DataType.UBYTE -> {
if (amount <= 2)
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x")
repeat(amount) { asmgen.out(" lsr a") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
}
DataType.BYTE -> {
if (amount <= 2)
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
}
DataType.UWORD -> {
if(amount>=16) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
return
}
var left = amount
while (left >= 7) {
asmgen.out(" jsr math.shift_right_uw_7")
left -= 7
}
if (left in 0..2)
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_uw_$left")
}
DataType.WORD -> {
if(amount>=16) {
asmgen.out("""
lda P8ESTACK_HI+1,x
bmi +
lda #0
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
beq ++
+ lda #255
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
+""")
return
}
var left = amount
while (left >= 7) {
asmgen.out(" jsr math.shift_right_w_7")
left -= 7
}
if (left in 0..2)
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_w_$left")
}
else -> throw AssemblyError("weird type")
}
return
}
}
"<<" -> {
val amount = expr.right.constValue(program)?.number?.toInt()
if(amount!=null) {
translateExpressionInternal(expr.left)
if (leftDt in ByteDatatypes) {
if (amount <= 2)
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x")
repeat(amount) { asmgen.out(" asl a") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
} else {
var left = amount
while (left >= 7) {
asmgen.out(" jsr math.shift_left_w_7")
left -= 7
}
if (left in 0..2)
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
else
asmgen.out(" jsr math.shift_left_w_$left")
}
return
}
}
"*" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVar = expr.left as? IdentifierReference
val rightVar = expr.right as? IdentifierReference
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
return translateSquared(leftVar, leftDt)
}
val value = expr.right.constValue(program)
if(value!=null) {
if(rightDt in IntegerDatatypes) {
val amount = value.number.toInt()
if(amount==2) {
// optimize x*2 common case
translateExpressionInternal(expr.left)
if(leftDt in ByteDatatypes) {
asmgen.out(" asl P8ESTACK_LO+1,x")
} else {
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
}
return
}
when(rightDt) {
DataType.UBYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(expr.left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return
}
}
DataType.BYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(expr.left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return
}
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(expr.left)
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
return
}
}
DataType.UWORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(expr.left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return
}
}
DataType.WORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(expr.left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return
}
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(expr.left)
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
return
}
}
else -> {}
}
}
}
}
"/" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = expr.right.constValue(program)?.number?.toInt()
if(rightVal!=null && rightVal==2) {
translateExpressionInternal(expr.left)
when (leftDt) {
DataType.UBYTE -> {
asmgen.out(" lsr P8ESTACK_LO+1,x")
}
DataType.UWORD -> {
asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
}
DataType.BYTE -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_LO+1,x
bpl +
inc P8ESTACK_LO+1,x
lda P8ESTACK_LO+1,x
+ asl a
ror P8ESTACK_LO+1,x""")
}
DataType.WORD -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_HI+1,x
bpl ++
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+ lda P8ESTACK_HI+1,x
+ asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x""")
}
else -> throw AssemblyError("weird dt")
}
return
}
}
}
in ComparisonOperators -> {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.constValue(program)?.number
if(rightVal==0.0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
}
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators) {
translateCompareStrings(expr.left, expr.operator, expr.right)
}
else {
// the general, non-optimized cases TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
translateExpressionInternal(expr.left)
translateExpressionInternal(expr.right)
when (leftDt) {
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
else -> throw AssemblyError("non-numerical datatype")
}
}
}
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
if(expr.isSimple) {
if(operator=="!=") {
when (dt) {
in ByteDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
asmgen.out("""
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
in WordDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
DataType.FLOAT -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
asmgen.out("""
jsr floats.SIGN
sta P8ESTACK_LO,x
dex""")
return
}
else -> {}
}
}
/* operator == is not worth it to special case, the code is mostly larger */
}
translateExpressionInternal(expr)
when(operator) {
"==" -> {
when(dt) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.equal_zero")
else -> throw AssemblyError("wrong dt")
}
}
"!=" -> {
when(dt) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.notequalzero_b")
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.notequalzero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.notequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
"<" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteral.fromBoolean(false, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.less_zero")
else -> throw AssemblyError("wrong dt")
}
}
">" -> {
when(dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.greaterzero_ub")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterzero_sb")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.greaterzero_uw")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.greater_zero")
else -> throw AssemblyError("wrong dt")
}
}
"<=" -> {
when(dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzeros_b")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
">=" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteral.fromBoolean(true, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.greaterequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
else -> throw AssemblyError("invalid comparison operator")
}
}
private fun translateSquared(variable: IdentifierReference, dt: DataType) {
val asmVar = asmgen.asmVariableName(variable)
when(dt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" lda $asmVar")
asmgen.signExtendAYlsb(dt)
asmgen.out(" jsr math.square")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $asmVar | ldy $asmVar+1 | jsr math.square")
}
else -> throw AssemblyError("require integer dt for square")
}
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: PrefixExpression) {
translateExpressionInternal(expr.expression)
val itype = expr.inferType(program)
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
when(expr.operator) {
"+" -> {}
"-" -> {
when(type) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
else -> throw AssemblyError("weird type")
}
}
"~" -> {
when(type) {
in ByteDatatypes ->
asmgen.out("""
lda P8ESTACK_LO+1,x
eor #255
sta P8ESTACK_LO+1,x
""")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
}
}
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
val elementIDt = arrayExpr.inferType(program)
if(!elementIDt.isKnown)
throw AssemblyError("unknown dt")
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!!
if(arrayVarDecl.datatype==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayExpr.inferType(program) isnot DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(arrayExpr.arrayvar)) {
asmgen.out(" lda ($arrayVarName),y")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
}
asmgen.out(" sta P8ESTACK_LO,x | dex")
return
}
val constIndexNum = arrayExpr.indexer.constIndex()
if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
}
in WordDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue) | jsr floats.push_float")
}
else -> throw AssemblyError("weird element type")
}
} else {
when(elementDt) {
in ByteDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
asmgen.out("""
ldy #>$arrayVarName
clc
adc #<$arrayVarName
bcc +
iny
+ jsr floats.push_float""")
}
else -> throw AssemblyError("weird dt")
}
}
}
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
when(operator) {
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
"%" -> {
if(types==DataType.BYTE)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_ub")
}
"+" -> asmgen.out("""
lda P8ESTACK_LO+2,x
clc
adc P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
""")
"-" -> asmgen.out("""
lda P8ESTACK_LO+2,x
sec
sbc P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
""")
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
when(operator) {
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"%" -> {
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_uw")
}
"+" -> asmgen.out(" jsr prog8_lib.add_w")
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
"<<" -> asmgen.out(" jsr math.shift_left_w")
">>" -> {
if(dt==DataType.UWORD)
asmgen.out(" jsr math.shift_right_uw")
else
asmgen.out(" jsr math.shift_right_w")
}
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateBinaryOperatorFloats(operator: String) {
when(operator) {
"*" -> asmgen.out(" jsr floats.mul_f")
"/" -> asmgen.out(" jsr floats.div_f")
"+" -> asmgen.out(" jsr floats.add_f")
"-" -> asmgen.out(" jsr floats.sub_f")
"<" -> asmgen.out(" jsr floats.less_f")
">" -> asmgen.out(" jsr floats.greater_f")
"<=" -> asmgen.out(" jsr floats.lesseq_f")
">=" -> asmgen.out(" jsr floats.greatereq_f")
"==" -> asmgen.out(" jsr floats.equal_f")
"!=" -> asmgen.out(" jsr floats.notequal_f")
"%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateCompareStrings(s1: Expression, operator: String, s2: Expression) {
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
when(operator) {
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
"<=" -> asmgen.out("""
bpl +
lda #1
bne ++
+ lda #0
+ sta P8ESTACK_LO,x""")
">=" -> asmgen.out("""
bmi +
lda #1
bne ++
+ lda #0
+ sta P8ESTACK_LO,x""")
"<" -> asmgen.out("""
bmi +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x""")
">" -> asmgen.out("""
bpl +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x""")
}
asmgen.out(" dex")
}
}

View File

@ -0,0 +1,40 @@
package prog8.codegen.cpu6502
import prog8.code.ast.IPtSubroutine
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtSub
import prog8.code.core.*
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
when(this) {
is PtAsmSub -> {
return returns
}
is PtSub -> {
// for non-asm subroutines, determine the return registers based on the type of the return value
return if(returntype==null)
emptyList()
else {
val register = when (returntype!!) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
listOf(Pair(register, returntype!!))
}
}
}
}
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
return when(returntype) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
null -> null
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
}

View File

@ -1,46 +1,48 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import prog8.ast.Program
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.RangeExpression
import prog8.ast.statements.ForLoop
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
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen, private val zeropage: Zeropage) {
internal class ForLoopsAsmGen(
private val asmgen: AsmGen6502Internal,
private val zeropage: Zeropage
) {
internal fun translate(stmt: ForLoop) {
val iterableDt = stmt.iterable.inferType(program)
if(!iterableDt.isKnown)
throw AssemblyError("unknown dt")
internal fun translate(stmt: PtForLoop) {
val iterableDt = stmt.iterable.type
when(stmt.iterable) {
is RangeExpression -> {
val range = (stmt.iterable as RangeExpression).toConstantIntegerRange()
is PtRange -> {
val range = (stmt.iterable as PtRange).toConstantIntegerRange()
if(range==null) {
translateForOverNonconstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as RangeExpression)
translateForOverNonconstRange(stmt, iterableDt, stmt.iterable as PtRange)
} else {
translateForOverConstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, range)
translateForOverConstRange(stmt, iterableDt, range)
}
}
is IdentifierReference -> {
translateForOverIterableVar(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as IdentifierReference)
is PtIdentifier -> {
translateForOverIterableVar(stmt, iterableDt, stmt.iterable as PtIdentifier)
}
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
}
}
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) {
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
val modifiedLabel = program.makeLabel("for_modified")
val modifiedLabel2 = program.makeLabel("for_modifiedb")
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.loopEndLabels.push(endLabel)
val stepsize=range.step.constValue(program)!!.number.toInt()
val stepsize=range.step.asConstInteger()!!
if(stepsize < -1) {
val limit = range.to.constValue(program)?.number
if(limit==0.0)
val limit = range.to.asConstInteger()
if(limit==0)
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
}
@ -52,11 +54,39 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel)
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
$modifiedLabel cmp #0 ; modified
@ -70,11 +100,39 @@ $modifiedLabel cmp #0 ; modified
// bytes, step >= 2 or <= -2
// loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel)
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
if(stepsize>0) {
asmgen.out("""
lda $varname
@ -102,14 +160,15 @@ $modifiedLabel cmp #0 ; modified
// words, step 1 or -1
stepsize == 1 || stepsize == -1 -> {
val varname = asmgen.asmVariableName(stmt.loopVar)
assignLoopvar(stmt, range)
val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname+1
$modifiedLabel cmp #0 ; modified
@ -136,14 +195,15 @@ $modifiedLabel2 cmp #0 ; modified
stepsize > 0 -> {
// (u)words, step >= 2
val varname = asmgen.asmVariableName(stmt.loopVar)
assignLoopvar(stmt, range)
val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
if (iterableDt == DataType.ARRAY_UW) {
asmgen.out("""
@ -184,42 +244,26 @@ $endLabel""")
else -> {
// (u)words, step <= -2
val varname = asmgen.asmVariableName(stmt.loopVar)
assignLoopvar(stmt, range)
val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
if(iterableDt==DataType.ARRAY_UW) {
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
sta $varname
tax
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
$modifiedLabel cmp #0 ; modified
bcc $endLabel
bne $loopLabel
lda $varname
$modifiedLabel2 cmp #0 ; modified
bcs $loopLabel
$endLabel""")
} else {
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
sta $varname
pha
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
pla
txa
$modifiedLabel2 cmp #0 ; modified
lda $varname+1
$modifiedLabel sbc #0 ; modified
@ -227,7 +271,6 @@ $modifiedLabel sbc #0 ; modified
eor #$80
+ bpl $loopLabel
$endLabel""")
}
}
}
}
@ -237,12 +280,67 @@ $endLabel""")
asmgen.loopEndLabels.pop()
}
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
// pre-check for end already reached.
// 'to' is in AY, do NOT clobber this!
if(iterableDt==DataType.ARRAY_W) {
if(stepsize<0)
asmgen.out("""
sta P8ZP_SCRATCH_W2 ; to
sty P8ZP_SCRATCH_W2+1 ; to
lda $fromVar
cmp P8ZP_SCRATCH_W2
lda $fromVar+1
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #${'$'}80
+ bmi $endLabel
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1""")
else
asmgen.out("""
sta P8ZP_SCRATCH_REG
cmp $fromVar
tya
sbc $fromVar+1
bvc +
eor #${'$'}80
+ bmi $endLabel
lda P8ZP_SCRATCH_REG""")
} else {
if(stepsize<0)
asmgen.out("""
cpy $fromVar+1
beq +
bcc ++
bcs $endLabel
+ cmp $fromVar
bcc +
beq +
bne $endLabel
+""")
else
asmgen.out("""
cpy $fromVar+1
bcc $endLabel
bne +
cmp $fromVar
bcc $endLabel
+""")
}
}
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident)
val decl = ident.targetVarDecl(program)!!
val symbol = asmgen.symbolTable.lookup(ident.name)
val numElements = when(symbol) {
is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!!
else -> 0
}
when(iterableDt) {
DataType.STR -> {
asmgen.out("""
@ -252,8 +350,8 @@ $endLabel""")
sty $loopLabel+2
$loopLabel lda ${65535.toHex()} ; modified
beq $endLabel
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
asmgen.translate(stmt.body)
sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements)
asmgen.out("""
inc $loopLabel+1
bne $loopLabel
@ -261,20 +359,19 @@ $loopLabel lda ${65535.toHex()} ; modified
bne $loopLabel
$endLabel""")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
val length = decl.arraysize!!.constIndex()!!
val indexVar = program.makeLabel("for_index")
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
val indexVar = asmgen.makeLabel("for_index")
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda $iterableName,y
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
asmgen.translate(stmt.body)
if(length<=255) {
sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
cpy #$length
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
@ -285,11 +382,11 @@ $loopLabel sty $indexVar
bne $loopLabel
beq $endLabel""")
}
if(length>=16) {
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
@ -298,9 +395,9 @@ $loopLabel sty $indexVar
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
val length = decl.arraysize!!.constIndex()!! * 2
val indexVar = program.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
val length = numElements * 2
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
@ -308,7 +405,7 @@ $loopLabel sty $indexVar
sta $loopvarName
lda $iterableName+1,y
sta $loopvarName+1""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
if(length<=127) {
asmgen.out("""
ldy $indexVar
@ -328,9 +425,47 @@ $loopLabel sty $indexVar
}
if(length>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
asmgen.out("$indexVar .byte 0")
}
asmgen.out(endLabel)
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda ${iterableName}_lsb,y
sta $loopvarName
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
@ -346,7 +481,7 @@ $loopLabel sty $indexVar
asmgen.loopEndLabels.pop()
}
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
if (range.isEmpty() || range.step==0)
throw AssemblyError("empty range or step 0")
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
@ -359,18 +494,18 @@ $loopLabel sty $indexVar
}
// not one of the easy cases, generate more complex code...
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
// loop over byte range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.loopVar)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
when (range.step) {
0, 1, -1 -> {
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
@ -430,7 +565,7 @@ $loopLabel""")
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
// loop over word range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.loopVar)
val varname = asmgen.asmVariableName(stmt.variable)
when (range.step) {
0, 1, -1 -> {
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
@ -444,7 +579,7 @@ $loopLabel""")
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
cmp #<${range.last}
@ -470,16 +605,16 @@ $loopLabel""")
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
private fun translateForSimpleByteRangeAsc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
if (range.last == 255) {
asmgen.out("""
inc $varname
@ -496,16 +631,16 @@ $endLabel""")
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
when (range.last) {
0 -> {
asmgen.out("""
@ -533,18 +668,18 @@ $endLabel""")
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
private fun translateForSimpleWordRangeAsc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
cmp #<${range.last}
@ -560,18 +695,18 @@ $loopLabel""")
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = program.makeLabel("for_loop")
val endLabel = program.makeLabel("for_end")
private fun translateForSimpleWordRangeDesc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.loopVar)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
cmp #<${range.last}
@ -588,10 +723,9 @@ $loopLabel""")
asmgen.loopEndLabels.pop()
}
private fun assignLoopvar(stmt: ForLoop, range: RangeExpression) =
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =
asmgen.assignExpressionToVariable(
range.from,
asmgen.asmVariableName(stmt.loopVar),
stmt.loopVarDt(program).getOrElse { throw AssemblyError("unknown dt") },
stmt.definingSubroutine)
asmgen.asmVariableName(stmt.variable),
stmt.variable.type)
}

View File

@ -1,16 +1,6 @@
package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.Subroutine
import prog8.ast.statements.SubroutineParameter
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignSource
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
@ -18,71 +8,45 @@ import prog8.codegen.cpu6502.assignment.AsmAssignment
import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) {
saveXbeforeCall(stmt)
translateFunctionCall(stmt, false)
restoreXafterCall(stmt)
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
translateFunctionCall(stmt)
// just ignore any result values from the function call.
}
internal fun saveXbeforeCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if(regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!)
}
}
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
internal fun restoreXafterCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if(regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
internal fun optimizeIntArgsViaRegisters(sub: Subroutine) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
internal fun translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call
// NOTE: does NOT output the code to deal with the result values!
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
val subAsmName = asmgen.asmSymbolName(call.target)
val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol?.astNode as IPtSubroutine
val subAsmName = asmgen.asmSymbolName(call.name)
if(sub.isAsmSubroutine) {
if(sub is PtAsmSub) {
argumentsViaRegisters(sub, call)
if (sub.inline && asmgen.options.optimize) {
// inline the subroutine.
if (sub.inline) {
// inline the subroutine. (regardless of optimization settings!)
// we do this by copying the subroutine's statements at the call site.
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
// (this condition has been enforced by an ast check earlier)
asmgen.out(" \t; inlined routine follows: ${sub.name}")
sub.statements.forEach { asmgen.translate(it as InlineAssembly) }
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
asmgen.out(" \t; inlined routine end: ${sub.name}")
} else {
asmgen.out(" jsr $subAsmName")
}
}
else {
if(sub.inline)
throw AssemblyError("can only reliably inline asmsub routines at this time")
else if(sub is PtSub) {
if(optimizeIntArgsViaRegisters(sub)) {
if(sub.parameters.size==1) {
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
} else {
// 2 byte params, second in Y, first in A
@ -100,85 +64,110 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
asmgen.out(" jsr $subAsmName")
}
else throw AssemblyError("invalid sub type")
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
}
private fun argumentsViaRegisters(sub: Subroutine, call: IFunctionCall) {
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
return when(arg) {
is PtBuiltinFunctionCall -> {
if (arg.name == "lsb" || arg.name == "msb")
return usesOtherRegistersWhileEvaluating(arg.args[0])
if (arg.name == "mkword")
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
return !arg.isSimple()
}
is PtAddressOf -> false
is PtIdentifier -> false
is PtMachineRegister -> false
is PtMemoryByte -> return usesOtherRegistersWhileEvaluating(arg.address)
is PtNumber -> false
is PtBool -> false
else -> true
}
}
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
val registersUsed = mutableListOf<RegisterOrStatusflag>();
fun usedA() = registersUsed.any {it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY}
fun usedX() = registersUsed.any {it.registerOrPair==RegisterOrPair.X || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.XY}
fun usedY() = registersUsed.any {it.registerOrPair==RegisterOrPair.Y || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.XY}
if(sub.parameters.size==1) {
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0])
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0])
} else {
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.asmParameterRegisters)) {
registerArgsViaCpuStackEvaluation(call, sub)
} else {
asmsub6502ArgsEvalOrder(sub).forEach {
val param = sub.parameters[it]
val arg = call.args[it]
argumentViaRegister(sub, IndexedValue(it, param), arg)
val optimalEvalOrder = asmsub6502ArgsEvalOrder(sub)
optimalEvalOrder.forEach {
val param = sub.parameters[it]
val arg = call.args[it]
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
else if(registersUsed.any {it.statusflag!=null}) {
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
}
else {
if(usedX()) asmgen.saveRegisterStack(CpuRegister.X, false)
if(usedY()) asmgen.saveRegisterStack(CpuRegister.Y, false)
if(usedA()) asmgen.saveRegisterStack(CpuRegister.A, false)
val used = argumentViaRegister(sub, IndexedValue(it, param.second), arg)
if(usedA()) asmgen.restoreRegisterStack(CpuRegister.A, false)
if(usedY()) asmgen.restoreRegisterStack(CpuRegister.Y, true)
if(usedX()) asmgen.restoreRegisterStack(CpuRegister.X, true)
used
}
} else {
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
}
}
}
}
private fun registerArgsViaCpuStackEvaluation(call: IFunctionCall, callee: Subroutine) {
// this is called when one or more of the arguments are 'complex' and
// cannot be assigned to a register easily or risk clobbering other registers.
require(callee.isAsmSubroutine)
if(callee.parameters.isEmpty())
return
// use the cpu hardware stack as intermediate storage for the arguments.
val argOrder = asmsub6502ArgsEvalOrder(callee)
argOrder.reversed().forEach {
asmgen.pushCpuStack(callee.parameters[it].type, call.args[it])
}
argOrder.forEach {
val param = callee.parameters[it]
val targetVar = callee.searchParameter(param.name)!!
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine)
}
}
private fun argumentViaVariable(sub: Subroutine, parameter: SubroutineParameter, value: Expression) {
private fun argumentViaVariable(sub: PtSub, parameter: PtSubroutineParameter, value: PtExpression) {
// pass parameter via a regular variable (not via registers)
val valueIDt = value.inferType(program)
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.type))
if(!isArgumentTypeCompatible(value.type, parameter.type))
throw AssemblyError("argument type incompatible")
val varName = asmgen.asmVariableName(sub.scopedName + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type)
}
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression, registerOverride: RegisterOrPair? = null) {
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
// pass argument via a register parameter
val valueIDt = value.inferType(program)
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible")
val paramRegister = if(registerOverride==null) sub.asmParameterRegisters[parameter.index] else RegisterOrStatusflag(registerOverride, null)
val paramRegister: RegisterOrStatusflag = when(sub) {
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null)
is PtSub -> RegisterOrStatusflag(registerOverride!!, null)
}
val statusflag = paramRegister.statusflag
val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type
if(requiredDt!=valueDt) {
if(valueDt largerThan requiredDt)
if(requiredDt!=value.type) {
if(value.type largerThan requiredDt)
throw AssemblyError("can only convert byte values to word param types")
}
if (statusflag!=null) {
if(requiredDt!=valueDt)
throw AssemblyError("for statusflag, byte value is required")
if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte or bool value is required")
if (statusflag == Statusflag.Pc) {
// this param needs to be set last, right before the jsr
// this boolean param needs to be set last, right before the jsr
// for now, this is already enforced on the subroutine definition by the Ast Checker
when(value) {
is NumericLiteral -> {
is PtNumber -> {
val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc")
}
is IdentifierReference -> {
is PtBool -> {
asmgen.out(if(value.value) " sec" else " clc")
}
is PtIdentifier -> {
val sourceName = asmgen.asmVariableName(value)
// note: cannot use X register here to store A because it might be used for other arguments
asmgen.out("""
pha
clc
@ -189,35 +178,34 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
else -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out("""
beq +
sec
bcs ++
+ clc
+""")
asmgen.out(" ror a")
}
}
} else throw AssemblyError("can only use Carry as status flag parameter")
return RegisterOrStatusflag(null, statusflag)
}
else {
// via register or register pair
register!!
if(requiredDt largerThan valueDt) {
if(requiredDt largerThan value.type) {
// we need to sign extend the source, do this via temporary word variable
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
} else {
val scope = value.definingISub()
val target: AsmAssignTarget =
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, sub, register = register)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
else {
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
}
val src = if(valueDt in PassByReferenceDatatypes) {
if(value is IdentifierReference) {
val addr = AddressOf(value, Position.DUMMY)
val src = if(value.type in PassByReferenceDatatypes) {
if(value is PtIdentifier) {
val addr = PtAddressOf(Position.DUMMY)
addr.add(value)
addr.parent = sub as PtNode
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
@ -225,14 +213,17 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
}
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
}
return RegisterOrStatusflag(register, null)
}
}
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType)
return true
if(argType==DataType.BOOL && paramType==DataType.BOOL)
return true
if(argType in ByteDatatypes && paramType in ByteDatatypes)
return true
if(argType in WordDatatypes && paramType in WordDatatypes)
@ -248,3 +239,5 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
return false
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,126 +0,0 @@
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.PostIncrDecr
import prog8.code.core.*
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translate(stmt: PostIncrDecr) {
val incr = stmt.operator=="++"
val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memoryAddress
val targetArrayIdx = stmt.target.arrayindexed
val scope = stmt.definingSubroutine
when {
targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent)
when (stmt.target.inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
in WordDatatypes -> {
if(incr)
asmgen.out(" inc $what | bne + | inc $what+1 |+")
else
asmgen.out("""
lda $what
bne +
dec $what+1
+ dec $what
""")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$what | ldy #>$what")
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
}
else -> throw AssemblyError("need numeric type")
}
}
targetMemory!=null -> {
when (val addressExpr = targetMemory.addressExpression) {
is NumericLiteral -> {
val what = addressExpr.number.toHex()
asmgen.out(if(incr) " inc $what" else " dec $what")
}
is IdentifierReference -> {
val what = asmgen.asmVariableName(addressExpr)
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
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")
}
}
}
targetArrayIdx!=null -> {
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
val elementDt = targetArrayIdx.inferType(program).getOr(DataType.UNDEFINED)
val constIndex = targetArrayIdx.indexer.constIndex()
if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
in WordDatatypes -> {
if(incr)
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
else
asmgen.out("""
lda $asmArrayvarname+$indexValue
bne +
dec $asmArrayvarname+$indexValue+1
+ dec $asmArrayvarname+$indexValue
""")
}
DataType.FLOAT -> {
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
}
else -> throw AssemblyError("need numeric type")
}
}
else
{
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.out(" tax")
when(elementDt) {
in ByteDatatypes -> {
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
}
in WordDatatypes -> {
if(incr)
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
else
asmgen.out("""
lda $asmArrayvarname,x
bne +
dec $asmArrayvarname+1,x
+ dec $asmArrayvarname,x
""")
}
DataType.FLOAT -> {
asmgen.out("""
ldy #>$asmArrayvarname
clc
adc #<$asmArrayvarname
bcc +
iny
+ jsr floats.inc_var_f""")
}
else -> throw AssemblyError("weird array elt dt")
}
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
}
}
}

View File

@ -1,12 +1,10 @@
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.statements.*
import prog8.code.*
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.TargetStorageKind
import prog8.compiler.CallGraph
import java.time.LocalDate
import java.time.LocalDateTime
import kotlin.math.absoluteValue
@ -20,30 +18,27 @@ import kotlin.math.absoluteValue
* - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!)
*/
internal class ProgramAndVarsGen(
val program: Program,
val program: PtProgram,
val options: CompilationOptions,
val errors: IErrorReporter,
private val symboltable: SymbolTable,
private val functioncallAsmGen: FunctionCallAsmGen,
private val asmgen: AsmGen,
private val asmgen: AsmGen6502Internal,
private val allocator: VariableAllocator,
private val zeropage: Zeropage
) {
private val compTarget = options.compTarget
private val callGraph = CallGraph(program, true)
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance<PtAssignment>() }
internal fun generate() {
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"}
header()
val allBlocks = program.allBlocks
if(allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main'")
val allBlocks = program.allBlocks()
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) }
program.allBlocks().forEach { block2asm(it) }
// the global list of all floating point constants for the whole program
asmgen.out("; global float constants")
@ -54,6 +49,7 @@ internal class ProgramAndVarsGen(
}
memorySlabs()
tempVars()
footer()
}
}
@ -71,7 +67,7 @@ internal class ProgramAndVarsGen(
asmgen.out("; assembler syntax is for the 64tasm cross-assembler")
asmgen.out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
asmgen.out("")
asmgen.out(".cpu '$cpu'\n.enc 'none'\n")
asmgen.out(".cpu '$cpu'\n.enc 'none'")
// the global prog8 variables needed
val zp = zeropage
@ -80,8 +76,6 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
asmgen.out(".endweak")
if(options.symbolDefs.isNotEmpty()) {
@ -94,13 +88,13 @@ internal class ProgramAndVarsGen(
when(options.output) {
OutputType.RAW -> {
asmgen.out("; ---- raw assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}\n")
asmgen.out("* = ${options.loadAddress.toHex()}")
}
OutputType.PRG -> {
when(options.launcher) {
CbmPrgLauncherType.BASIC -> {
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.toplevelModule.position)
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.position)
}
asmgen.out("; ---- basic program with sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
@ -108,26 +102,26 @@ internal class ProgramAndVarsGen(
asmgen.out(" .word (+), $year")
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
asmgen.out("+\t.word 0")
asmgen.out("prog8_entrypoint\t; assembly code starts here\n")
asmgen.out("prog8_entrypoint\t; assembly code starts here")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
}
CbmPrgLauncherType.NONE -> {
asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}\n")
asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
}
}
}
OutputType.XEX -> {
asmgen.out("; ---- atari xex program ----")
asmgen.out("* = ${options.loadAddress.toHex()}\n")
asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
}
}
@ -140,96 +134,182 @@ 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 main.start")
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp sys.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr main.start | lda #31 | sta $01")
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
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 main.start")
// TODO c128: how to bank basic+kernal back in?
if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else
asmgen.out(" rts")
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00")
asmgen.out(" jmp sys.cleanup_at_exit")
}
else -> asmgen.jmp("main.start")
else -> asmgen.jmp("p8b_main.p8s_start")
}
}
private fun memorySlabs() {
asmgen.out("; memory slabs")
asmgen.out("prog8_slabs\t.block")
for(slab in symboltable.allMemorySlabs) {
if(slab.align>1u)
asmgen.out("\t.align ${slab.align.toHex()}")
asmgen.out("${slab.name}\t.fill ${slab.size}")
if(symboltable.allMemorySlabs.isNotEmpty()) {
asmgen.out("; memory slabs\n .section slabs_BSS")
asmgen.out("prog8_slabs\t.block")
for (slab in symboltable.allMemorySlabs) {
if (slab.align > 1u)
asmgen.out("\t.align ${slab.align.toHex()}")
asmgen.out("${slab.name}\t.fill ${slab.size}")
}
asmgen.out("\t.bend\n .send slabs_BSS")
}
asmgen.out("\t.bend")
}
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.BOOL -> asmgen.out("$name .byte ?")
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() {
// program end
asmgen.out("prog8_program_end\t; end of program label for progend()")
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(relocateBssVars) {
if(!relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS")
asmgen.out("prog8_program_end\t; end of program label for progend()")
asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS")
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 {
asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
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: Block) {
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("align_word" in block.options())
if(block.options.alignment==PtBlock.BlockAlignment.WORD)
asmgen.out("\t.align 2")
else if("align_page" in block.options())
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE)
asmgen.out("\t.align $100")
}
asmgen.out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
asmgen.outputSourceLine(block)
createBlockVariables(block)
asmsubs2asm(block.statements)
asmsubs2asm(block.children)
asmgen.out("")
asmgen.out("; subroutines in this block")
// First translate regular statements, and then put the subroutines at the end.
// (regular statements = everything except the initialization assignments;
// these will be part of the prog8_init_vars init routine generated below)
val initializers = blockVariableInitializers.getValue(block)
val statements = block.statements.filterNot { it in initializers }
val (subroutine, stmts) = statements.partition { it is Subroutine }
stmts.forEach { asmgen.translate(it) }
subroutine.forEach { asmgen.translate(it) }
val notInitializers = block.children.filterNot { it in initializers }
notInitializers.forEach { asmgen.translate(it) }
if(!options.dontReinitGlobals) {
// generate subroutine to initialize block-level (global) variables
if (initializers.isNotEmpty()) {
asmgen.out("prog8_init_vars\t.block\n")
initializers.forEach { assign -> asmgen.translate(assign) }
asmgen.out(" rts\n .bend")
// generate subroutine to initialize block-level (global) variables
if (initializers.isNotEmpty()) {
asmgen.out("prog8_init_vars\t.block")
initializers.forEach { assign ->
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
asmgen.translate(assign)
// the other variables that should be set to zero are done so as part of the BSS section.
}
asmgen.out(" rts\n .bend")
}
asmgen.out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
asmgen.out(if(block.options.forceOutput) "\n\t.bend" else "\n\t.pend")
}
private fun getVars(scope: StNode): Map<String, StNode> =
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) }
private fun createBlockVariables(block: Block) {
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
private fun createBlockVariables(block: PtBlock) {
val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.BLOCK)
val varsInBlock = getVars(scope)
@ -253,26 +333,16 @@ internal class ProgramAndVarsGen(
nonZpVariables2asm(variables)
}
internal fun translateSubroutine(sub: Subroutine) {
var onlyVariables = false
internal fun translateAsmSubroutine(sub: PtAsmSub) {
if(sub.inline) {
if(options.optimize) {
if(sub.isAsmSubroutine || callGraph.unused(sub))
return
// from an inlined subroutine only the local variables are generated,
// all other code statements are omitted in the subroutine itself
// (they've been inlined at the call site, remember?)
onlyVariables = true
}
return // subroutine gets inlined at call site.
}
asmgen.out("")
val asmStartScope: String
val asmEndScope: String
if(sub.definingBlock.options().contains("force_output")) {
if(sub.definingBlock()!!.options.forceOutput) {
asmStartScope = ".block"
asmEndScope = ".bend"
} else {
@ -280,105 +350,126 @@ internal class ProgramAndVarsGen(
asmEndScope = ".pend"
}
if(sub.isAsmSubroutine) {
if(sub.asmAddress!=null)
return // already done at the memvars section
if(sub.address!=null)
return // already done at the memvars section
// asmsub with most likely just an inline asm in it
asmgen.out("${sub.name}\t$asmStartScope")
sub.statements.forEach { asmgen.translate(it) }
asmgen.out(" $asmEndScope\n")
// asmsub with most likely just an inline asm in it
asmgen.out("${sub.name}\t$asmStartScope")
sub.children.forEach { asmgen.translate(it) }
asmgen.out(" $asmEndScope")
}
internal fun translateSubroutine(sub: PtSub) {
asmgen.out("")
val asmStartScope: String
val asmEndScope: String
if(sub.definingBlock()!!.options.forceOutput) {
asmStartScope = ".block"
asmEndScope = ".bend"
} else {
// regular subroutine
asmgen.out("${sub.name}\t$asmStartScope")
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.SUBROUTINE)
val varsInSubroutine = getVars(scope)
// Zeropage Variables
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
zeropagevars2asm(varnames)
// MemDefs and Consts
val mvs = varsInSubroutine
.filter { it.value.type==StNodeType.MEMVAR }
.map { it.value as StMemVar }
val consts = varsInSubroutine
.filter { it.value.type==StNodeType.CONSTANT }
.map { it.value as StConstant }
memdefsAndConsts2asm(mvs, consts)
asmsubs2asm(sub.statements)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
if(sub.name=="start" && sub.definingBlock.name=="main")
entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
asmgen.out("; simple int arg(s) passed via register(s)")
if(sub.parameters.size==1) {
val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypes)
asmgen.assignRegister(RegisterOrPair.A, target)
else
asmgen.assignRegister(RegisterOrPair.AY, target)
} else {
require(sub.parameters.size==2)
// 2 simple byte args, first in A, second in Y
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2)
}
}
if(!onlyVariables) {
asmgen.out("; statements")
sub.statements.forEach { asmgen.translate(it) }
}
asmgen.out("; variables")
val asmGenInfo = asmgen.subroutineExtra(sub)
for((dt, name, addr) in asmGenInfo.extraVars) {
if(addr!=null)
asmgen.out("$name = $addr")
else when(dt) {
DataType.UBYTE -> asmgen.out("$name .byte 0")
DataType.UWORD -> asmgen.out("$name .word 0")
else -> throw AssemblyError("weird dt")
}
}
if(asmGenInfo.usedRegsaveA) // will probably never occur
asmgen.out("prog8_regsaveA .byte 0")
if(asmGenInfo.usedRegsaveX)
asmgen.out("prog8_regsaveX .byte 0")
if(asmGenInfo.usedRegsaveY)
asmgen.out("prog8_regsaveY .byte 0")
if(asmGenInfo.usedFloatEvalResultVar1)
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
if(asmGenInfo.usedFloatEvalResultVar2)
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
// normal statically allocated variables
val variables = varsInSubroutine
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
.map { it.value as StStaticVariable }
nonZpVariables2asm(variables)
asmgen.out(" $asmEndScope\n")
asmStartScope = ".proc"
asmEndScope = ".pend"
}
asmgen.out("${sub.name}\t$asmStartScope")
val scope = symboltable.lookupOrElse(sub.scopedName) {
throw AssemblyError("lookup ${sub.scopedName}")
}
require(scope.type==StNodeType.SUBROUTINE)
val varsInSubroutine = getVars(scope)
// Zeropage Variables
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
zeropagevars2asm(varnames)
// MemDefs and Consts
val mvs = varsInSubroutine
.filter { it.value.type==StNodeType.MEMVAR }
.map { it.value as StMemVar }
val consts = varsInSubroutine
.filter { it.value.type==StNodeType.CONSTANT }
.map { it.value as StConstant }
memdefsAndConsts2asm(mvs, consts)
asmsubs2asm(sub.children)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
asmgen.out("; simple int arg(s) passed via register(s)")
if(sub.parameters.size==1) {
val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypesWithBoolean)
asmgen.assignRegister(RegisterOrPair.A, target)
else
asmgen.assignRegister(RegisterOrPair.AY, target)
} else {
require(sub.parameters.size==2)
// 2 simple byte args, first in A, second in Y
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name)
asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2)
}
}
asmgen.out("; statements")
sub.children.forEach { asmgen.translate(it) }
asmgen.out("; variables")
asmgen.out(" .section BSS")
val asmGenInfo = asmgen.subroutineExtra(sub)
for((dt, name, addr) in asmGenInfo.extraVars) {
if(addr!=null)
asmgen.out("$name = $addr")
else when(dt) {
DataType.UBYTE -> asmgen.out("$name .byte ?")
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")
}
}
if(asmGenInfo.usedFloatEvalResultVar1)
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2)
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
asmgen.out(" .send BSS")
// normal statically allocated variables
val variables = varsInSubroutine
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
.map { it.value as StStaticVariable }
nonZpVariables2asm(variables)
asmgen.out(" $asmEndScope")
}
private fun entrypointInitialization() {
asmgen.out("; program startup initialization")
asmgen.out(" cld")
if(!options.dontReinitGlobals) {
blockVariableInitializers.forEach {
if (it.value.isNotEmpty())
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
}
asmgen.out(" cld | tsx | stx prog8_lib.orig_stackpointer ; required for sys.exit()")
// set full BSS area to zero
asmgen.out("""
.if prog8_bss_section_size>0
; reset all variables in BSS section to zero
lda #<prog8_bss_section_start
ldy #>prog8_bss_section_start
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldx #<prog8_bss_section_size
ldy #>prog8_bss_section_size
lda #0
jsr prog8_lib.memset
.endif""")
blockVariableInitializers.forEach {
if (it.value.isNotEmpty())
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
}
// string and array variables in zeropage that have initializer value, should be initialized
@ -403,12 +494,12 @@ internal class ProgramAndVarsGen(
asmgen.out("""
lda #<${name}_init_value
ldy #>${name}_init_value
sta cx16.r0L
sty cx16.r0H
sta cx16.r0
sty cx16.r0+1
lda #<${name}
ldy #>${name}
sta cx16.r1L
sty cx16.r1H
sta cx16.r1
sty cx16.r1+1
lda #<$size
ldy #>$size
jsr sys.memcopy""")
@ -426,22 +517,20 @@ internal class ProgramAndVarsGen(
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
}
asmgen.out("""+ tsx
stx prog8_lib.orig_stackpointer ; required for sys.exit()
ldx #255 ; init estack ptr
asmgen.out("""+
clv
clc""")
}
private class ZpStringWithInitial(
val name: List<String>,
val alloc: Zeropage.ZpAllocation,
val name: String,
val alloc: MemoryAllocator.VarAllocation,
val value: Pair<String, Encoding>
)
private class ZpArrayWithInitial(
val name: List<String>,
val alloc: Zeropage.ZpAllocation,
val name: String,
val alloc: MemoryAllocator.VarAllocation,
val value: StArray
)
@ -449,9 +538,10 @@ internal class ProgramAndVarsGen(
val result = mutableListOf<ZpStringWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
for (variable in vars) {
val svar = symboltable.flat.getValue(variable.key) as StStaticVariable
if(svar.onetimeInitializationStringValue!=null)
result.add(ZpStringWithInitial(variable.key, variable.value, svar.onetimeInitializationStringValue!!))
val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationStringValue!=null)
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
}
return result
}
@ -460,36 +550,82 @@ internal class ProgramAndVarsGen(
val result = mutableListOf<ZpArrayWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
for (variable in vars) {
val svar = symboltable.flat.getValue(variable.key) as StStaticVariable
if(svar.onetimeInitializationArrayValue!=null)
result.add(ZpArrayWithInitial(variable.key, variable.value, svar.onetimeInitializationArrayValue!!))
val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationArrayValue!=null)
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
}
return result
}
private fun zeropagevars2asm(varNames: Set<List<String>>) {
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }
private fun zeropagevars2asm(varNames: Set<String>) {
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }.toList().sortedBy { it.second.address }
for ((scopedName, zpvar) in zpVariables) {
if (scopedName.size == 2 && scopedName[0] == "cx16" && scopedName[1][0] == 'r' && scopedName[1][1].isDigit())
if (scopedName.startsWith("cx16.r"))
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
asmgen.out("${scopedName.last()} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
if(variable.dt in SplitWordArrayTypes) {
val lsbAddr = zpvar.address
val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
asmgen.out("${scopedName.substringAfterLast('.')}_msb \t= $msbAddr \t; zp ${zpvar.dt} (msbs)")
} else {
asmgen.out("${scopedName.substringAfterLast('.')} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
}
}
}
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
asmgen.out("")
asmgen.out("; non-zeropage variables")
val (stringvars, othervars) = variables.partition { it.dt==DataType.STR }
stringvars.forEach {
outputStringvar(it.name, it.onetimeInitializationStringValue!!.second, it.onetimeInitializationStringValue!!.first)
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
if(varsNoInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables without initialization value")
asmgen.out(" .section BSS")
varsNoInit.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt }).forEach {
uninitializedVariable2asm(it)
}
asmgen.out(" .send BSS")
}
othervars.sortedBy { it.type }.forEach {
staticVariable2asm(it)
if(varsWithInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables")
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt == DataType.STR }
stringvars.forEach {
outputStringvar(
it.name,
it.onetimeInitializationStringValue!!.second,
it.onetimeInitializationStringValue!!.first
)
}
othervars.sortedBy { it.type }.forEach {
staticVariable2asm(it)
}
}
}
private fun uninitializedVariable2asm(variable: StStaticVariable) {
when (variable.dt) {
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
in SplitWordArrayTypes -> {
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
in ArrayDatatypes -> {
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes")
}
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun staticVariable2asm(variable: StStaticVariable) {
val name = variable.name
val initialValue: Number =
if(variable.onetimeInitializationNumericValue!=null) {
if(variable.dt== DataType.FLOAT)
@ -499,22 +635,23 @@ internal class ProgramAndVarsGen(
} else 0
when (variable.dt) {
DataType.UBYTE -> asmgen.out("$name\t.byte ${initialValue.toHex()}")
DataType.BYTE -> asmgen.out("$name\t.char $initialValue")
DataType.UWORD -> asmgen.out("$name\t.word ${initialValue.toHex()}")
DataType.WORD -> asmgen.out("$name\t.sint $initialValue")
DataType.BOOL -> TODO("bool var to asm")
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue")
DataType.FLOAT -> {
if(initialValue==0) {
asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
} else {
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
asmgen.out("$name\t.byte $floatFill ; float $initialValue")
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
}
}
DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
else -> {
throw AssemblyError("weird dt")
}
@ -523,7 +660,7 @@ internal class ProgramAndVarsGen(
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
when(dt) {
DataType.ARRAY_UB -> {
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.byte ${data.joinToString()}")
@ -563,6 +700,18 @@ internal class ProgramAndVarsGen(
asmgen.out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_UW_SPLIT -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_W_SPLIT -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_F -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map {
@ -579,16 +728,16 @@ internal class ProgramAndVarsGen(
private fun zeroFilledArray(numElts: Int): StArray {
val values = mutableListOf<StArrayElement>()
repeat(numElts) {
values.add(StArrayElement(0.0, null))
values.add(StArrayElement(0.0, null, null))
}
return values
}
private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
memvars.forEach {
memvars.sortedBy { it.address }.forEach {
asmgen.out(" ${it.name} = ${it.address.toHex()}")
}
consts.forEach {
consts.sortedBy { it.name }.forEach {
if(it.dt==DataType.FLOAT)
asmgen.out(" ${it.name} = ${it.value}")
else
@ -596,12 +745,12 @@ internal class ProgramAndVarsGen(
}
}
private fun asmsubs2asm(statements: List<Statement>) {
private fun asmsubs2asm(statements: List<PtNode>) {
statements
.filter { it is Subroutine && it.isAsmSubroutine && it.asmAddress!=null }
.filter { it is PtAsmSub && it.address!=null }
.forEach { asmsub ->
asmsub as Subroutine
asmgen.out(" ${asmsub.name} = ${asmsub.asmAddress!!.toHex()}")
asmsub as PtAsmSub
asmgen.out(" ${asmsub.name} = ${asmsub.address!!.toHex()}")
}
}
@ -616,18 +765,28 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) {
DataType.ARRAY_BOOL ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
if(it.boolean!=null)
if(it.boolean==true) "1" else "0"
else {
val number = it.number!!
if(number==0.0) "0" else "1"
}
}
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_UW -> array.map {
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
}
else if(it.addressOf!=null) {
asmgen.asmSymbolName(it.addressOf!!)
else if(it.addressOfSymbol!=null) {
asmgen.asmSymbolName(it.addressOfSymbol!!)
}
else
throw AssemblyError("weird array elt")
@ -654,11 +813,11 @@ internal class ProgramAndVarsGen(
else
"-$$hexnum"
}
DataType.ARRAY_UW -> array.map {
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0')
}
DataType.ARRAY_W -> array.map {
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0)

View File

@ -16,13 +16,14 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
private val zeropage = options.compTarget.machine.zeropage
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
internal val zeropageVars: Map<List<String>, Zeropage.ZpAllocation> = zeropage.allocatedVariables
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
init {
allocateZeropageVariables()
zeropageVars = zeropage.allocatedVariables
}
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
internal fun getFloatAsmConst(number: Double): String {
val asmName = globalFloatConsts[number]
@ -59,7 +60,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName,
variable.dt,
variable.length,
variable.position,
variable.astNode.position,
errors
)
result.fold(
@ -67,7 +68,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
numVariablesAllocatedInZP++
},
failure = {
errors.err(it.message!!, variable.position)
errors.err(it.message!!, variable.astNode.position)
}
)
}
@ -78,7 +79,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName,
variable.dt,
variable.length,
variable.position,
variable.astNode.position,
errors
)
result.onSuccess { numVariablesAllocatedInZP++ }
@ -88,8 +89,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
// try to allocate any other interger variables into the zeropage until it is full.
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
if(errors.noErrors()) {
for (variable in varsDontCare.sortedBy { it.scopedName.size }) {
if(variable.dt in IntegerDatatypes) {
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
for (variable in sortedList) {
if(variable.dt in IntegerDatatypesWithBoolean) {
if(zeropage.free.isEmpty()) {
break
} else {
@ -97,7 +99,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName,
variable.dt,
variable.length,
variable.position,
variable.astNode.position,
errors
)
result.onSuccess { numVariablesAllocatedInZP++ }
@ -124,6 +126,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
}
}
collect(st)
return vars
return vars.sortedBy { it.dt }
}
}

View File

@ -0,0 +1,253 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.ast.PtBinaryExpression
import prog8.code.ast.PtExpression
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.AsmGen6502Internal
//
// This contains codegen for stack-based evaluation of binary expressions.
// It uses the CPU stack so depth is limited.
// It is called "as a last resort" if the optimized codegen path is unable
// to come up with a special case of the expression.
//
internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal
) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) {
in ByteDatatypesWithBoolean -> {
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
require(expr.operator in ComparisonOperators)
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
}
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign)
}
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) {
"both operands must be words"
}
return assignWordBinExpr(expr)
}
DataType.FLOAT -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
"both operands must be floats"
}
return assignFloatBinExpr(expr, assign)
}
else -> throw AssemblyError("weird expression type in assignment")
}
}
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 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
}
}
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"*" -> {
TODO("byte * at ${expr.position}")
}
"/" -> {
TODO("byte / at ${expr.position}")
}
"<<" -> {
TODO("byte << at ${expr.position}")
}
">>" -> {
TODO("byte >> at ${expr.position}")
}
"%" -> {
TODO("byte % at ${expr.position}")
}
"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)
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"|" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"==" -> {
TODO("byte == at ${expr.position}")
}
"!=" -> {
TODO("byte != at ${expr.position}")
}
"<" -> {
TODO("byte < at ${expr.position}")
}
"<=" -> {
TODO("byte <= at ${expr.position}")
}
">" -> {
TODO("byte > at ${expr.position}")
}
">=" -> {
TODO("byte >= at ${expr.position}")
}
else -> return false
}
}
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
assignFloatOperandsToFACandARG(expr.left, expr.right)
asmgen.out(" jsr floats.FADDT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"-" -> {
assignFloatOperandsToFACandARG(expr.right, expr.left)
asmgen.out(" jsr floats.FSUBT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"*" -> {
assignFloatOperandsToFACandARG(expr.left, expr.right)
asmgen.out(" jsr floats.FMULTT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"/" -> {
assignFloatOperandsToFACandARG(expr.right, expr.left)
asmgen.out(" jsr floats.FDIVT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"==" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_equal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"!=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_notequal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_less_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greater_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_lesseq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greatereq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
else -> TODO("float expression operator ${expr.operator}")
}
}
private fun assignFloatOperandsToFACandARG(left: PtExpression, right: PtExpression) {
when(asmgen.options.compTarget.name) {
C64Target.NAME -> {
// c64 has a quirk: always make sure FAC2 is loaded last (done using CONUPK) otherwise the result will be corrupt on C64
// this requires some more forced copying around of float values in certain cases
if (right.isSimple()) {
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
} else {
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
asmgen.pushFAC1()
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.popFAC2()
}
}
Cx16Target.NAME -> {
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
if (!right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
if (!right.isSimple()) asmgen.popFAC1()
}
else -> TODO("don't know how to evaluate float expression for selected compilation target")
}
}
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
}
}

View File

@ -1,96 +1,88 @@
package prog8.codegen.cpu6502.assignment
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.Subroutine
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.returnsWhatWhere
internal enum class TargetStorageKind {
VARIABLE,
ARRAY,
MEMORY,
REGISTER,
STACK
REGISTER
}
internal enum class SourceStorageKind {
LITERALBOOLEAN,
LITERALNUMBER,
VARIABLE,
ARRAY,
MEMORY,
REGISTER,
STACK, // value is already present on stack
EXPRESSION, // expression in ast-form, still to be evaluated
}
internal class AsmAssignTarget(val kind: TargetStorageKind,
private val asmgen: AsmGen,
private val asmgen: AsmGen6502Internal,
val datatype: DataType,
val scope: Subroutine?,
val scope: IPtSubroutine?,
val position: Position,
private val variableAsmName: String? = null,
val array: ArrayIndexedExpression? = null,
val memory: DirectMemoryWrite? = null,
val array: PtArrayIndexer? = null,
val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null,
val origAstTarget: AssignTarget? = null
val origAstTarget: PtAssignTarget? = null
)
{
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
val constArrayIndexValue by lazy { array?.index?.asConstInteger()?.toUInt() }
val asmVarname: String by lazy {
if (array == null)
variableAsmName!!
else
asmgen.asmVariableName(array.arrayvar)
asmgen.asmVariableName(array.variable)
}
lateinit var origAssign: AsmAssignment
init {
if(register!=null && datatype !in NumericDatatypes)
throw AssemblyError("register must be integer or float type")
if(register!=null && datatype !in NumericDatatypesWithBoolean)
throw AssemblyError("must be numeric type")
}
companion object {
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget {
with(assign.target) {
val idt = inferType(program)
val dt = idt.getOrElse { throw AssemblyError("unknown dt") }
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
with(target) {
when {
identifier != null -> {
val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter
val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen)
if (parameter!=null) {
val sub = parameter.definingSubroutine!!
if (sub.isAsmSubroutine) {
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
val sub = parameter.definingAsmSub()
if (sub!=null) {
val reg = sub.parameters.single { it.second===parameter }.first
if(reg.statusflag!=null)
throw AssemblyError("can't assign value to processor statusflag directly")
else
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this)
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, target.position, register=reg.registerOrPair, origAstTarget = this)
}
}
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, target.position, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
}
arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this)
memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
else -> throw AssemblyError("weird target")
}
}
}
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, asmgen: AsmGen): AsmAssignTarget =
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, pos: Position, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget =
when(registers) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers)
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, register = registers)
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
RegisterOrPair.R0,
RegisterOrPair.R1,
RegisterOrPair.R2,
@ -106,75 +98,98 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.R12,
RegisterOrPair.R13,
RegisterOrPair.R14,
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
}
}
fun isSameAs(left: PtExpression): Boolean =
when(kind) {
TargetStorageKind.VARIABLE -> {
val scopedName: String = if('.' in asmVarname)
asmVarname
else {
val scopeName = (scope as? PtNamedNode)?.scopedName
if (scopeName == null) asmVarname else "$scopeName.$asmVarname"
}
left is PtIdentifier && left.name==scopedName
}
TargetStorageKind.ARRAY -> {
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
}
TargetStorageKind.MEMORY -> {
left isSameAs memory!!
}
TargetStorageKind.REGISTER -> {
false
}
}
}
internal class AsmAssignSource(val kind: SourceStorageKind,
private val program: Program,
private val asmgen: AsmGen,
private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
val datatype: DataType,
private val variableAsmName: String? = null,
val array: ArrayIndexedExpression? = null,
val memory: DirectMemoryRead? = null,
val array: PtArrayIndexer? = null,
val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null,
val number: NumericLiteral? = null,
val expression: Expression? = null
val number: PtNumber? = null,
val boolean: PtBool? = null,
val expression: PtExpression? = null
)
{
val asmVarname: String
get() = if(array==null)
variableAsmName!!
else
asmgen.asmVariableName(array.arrayvar)
asmgen.asmVariableName(array.variable)
companion object {
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
val cv = value.constValue(program)
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
val cv = value as? PtNumber
if(cv!=null)
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
val bv = value as? PtBool
if(bv!=null)
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
return when(value) {
is NumericLiteral -> throw AssemblyError("should have been constant value")
is StringLiteral -> throw AssemblyError("string literal value should not occur anymore for asm generation")
is ArrayLiteral -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is IdentifierReference -> {
val parameter = value.targetVarDecl(program)?.subroutineParameter
if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine)
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
is PtString -> throw AssemblyError("string literal value should not occur anymore for asm generation")
is PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is PtIdentifier -> {
val parameter = asmgen.findSubroutineParameter(value.name, asmgen)
if(parameter?.definingAsmSub() != null)
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
if(dt == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
val regStr = varName.lowercase().substring(5)
val reg = RegisterOrPair.valueOf(regStr.uppercase())
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
} else {
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = varName)
}
}
is DirectMemoryRead -> {
is PtMemoryByte -> {
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
}
is ArrayIndexedExpression -> {
val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
is PtArrayIndexer -> {
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
}
is BuiltinFunctionCall -> {
val returnType = value.inferType(program)
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
is PtBuiltinFunctionCall -> {
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
}
is FunctionCallExpression -> {
val sub = value.target.targetSubroutine(program)!!
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
is PtFunctionCall -> {
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
val sub = symbol.astNode as IPtSubroutine
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
?: throw AssemblyError("can't translate zero return values in assignment")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
}
else -> {
val returnType = value.inferType(program)
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
}
}
}
@ -184,7 +199,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
// allow some signed/unsigned relaxations
fun withAdjustedDt(newType: DataType) =
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
if(target.datatype!=datatype) {
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
@ -199,17 +214,27 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
}
internal class AsmAssignment(val source: AsmAssignSource,
val target: AsmAssignTarget,
val isAugmentable: Boolean,
memsizer: IMemSizer,
val position: Position) {
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
val target: AsmAssignTarget,
val memsizer: IMemSizer,
val position: Position) {
init {
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
"source dt size must be less or equal to target dt size at $position"
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
}
}
}
internal class AsmAssignment(source: AsmAssignSource,
target: AsmAssignTarget,
memsizer: IMemSizer,
position: Position): AsmAssignmentBase(source, target, memsizer, position)
internal class AsmAugmentedAssignment(source: AsmAssignSource,
val operator: String,
target: AsmAssignTarget,
memsizer: IMemSizer,
position: Position): AsmAssignmentBase(source, target, memsizer, position)

View File

@ -0,0 +1,79 @@
package prog8tests.codegencpu6502
import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5
else -> 2
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
}
}
internal object DummyStringEncoder : IStringEncoding {
override val defaultEncoding: Encoding = Encoding.ISO
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
return emptyList()
}
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
return ""
}
}
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"
if(text !in errors)
errors.add(text)
}
override fun warn(msg: String, position: Position) {
val text = "${position.toClickableStr()} $msg"
if(text !in warnings)
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)
}
override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
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, infos.size)
if(!keepMessagesAfterReporting) {
clear()
}
}
fun clear() {
errors.clear()
warnings.clear()
infos.clear()
}
}

View File

@ -0,0 +1,129 @@
package prog8tests.codegencpu6502
import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.codegen.cpu6502.AsmGen6502
import java.nio.file.Files
import kotlin.io.path.Path
class TestCodegen: FunSpec({
fun getTestOptions(): CompilationOptions {
val target = C64Target()
return CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
zpReserved = emptyList(),
zpAllowed = CompilationOptions.AllZeropageAllowed,
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
}
test("augmented assign on arrays") {
//main {
// sub start() {
// ubyte[] particleX = [1,2,3]
// ubyte[] particleDX = [1,2,3]
// particleX[2] += particleDX[2]
//
// word @shared xx = 1
// xx = -xx
// xx += 42
// xx += cx16.r0
// }
//}
val codegen = AsmGen6502(prefixSymbols = false)
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
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))
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
}
it.add(targetIdx)
}
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target)
assign.add(value)
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
sub.add(cxregAssign)
block.add(sub)
program.add(block)
// define the "cx16.r0" virtual register
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)
val options = getTestOptions()
val st = SymbolTableMaker(program, options).make()
val errors = ErrorReporterForTests()
val result = codegen.generate(program, st, options, errors)!!
result.name shouldBe "test"
Files.deleteIfExists(Path("${result.name}.asm"))
}
test("64tass assembler available? - if this fails you need to install 64tass version 1.58 or newer in the path") {
val command = mutableListOf("64tass", "--version")
shouldNotThrowAny {
val proc = ProcessBuilder(command).start()
val output = String(proc.inputStream.readBytes())
val result = proc.waitFor()
result.shouldBe(0)
val (_, version) = output.split('V')
val (major, minor, _) = version.split('.')
val majorNum = major.toInt()
val minorNum = minor.toInt()
withClue("64tass version should be 1.58 or newer") {
majorNum shouldBeGreaterThanOrEqual 1
if (majorNum == 1)
minorNum shouldBeGreaterThanOrEqual 58
}
}
}
})

View File

@ -29,17 +29,17 @@ dependencies {
implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
srcDir "${project.projectDir}/src"
}
resources {
srcDirs = ["${project.projectDir}/res"]
srcDir "${project.projectDir}/res"
}
}
}

View File

@ -3,18 +3,19 @@ package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyGenerator
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
class CodeGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
private val options: CompilationOptions,
private val errors: IErrorReporter
): IAssemblyGenerator {
override fun compileToAssembly(): IAssemblyProgram? {
class ExperiCodeGen: ICodeGeneratorBackend {
override fun generate(
program: PtProgram,
symbolTable: SymbolTable,
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram {
// you could write a code generator directly on the PtProgram AST,
// but you can also use the Intermediate Representation to build a codegen on:
@ -22,9 +23,18 @@ class CodeGen(private val program: PtProgram,
val irProgram = irCodeGen.generate()
// this stub only writes the IR program to disk but doesn't generate anything else.
IRFileWriter(irProgram).writeFile()
IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **")
return null
return EmptyProgram
}
}
}
private object EmptyProgram : IAssemblyProgram {
override val name = "<Empty Program>"
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
println("** nothing assembled **")
return true
}
}

View File

@ -28,18 +28,21 @@ dependencies {
implementation project(':intermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
srcDir "${project.projectDir}/src"
}
resources {
srcDirs = ["${project.projectDir}/res"]
srcDir "${project.projectDir}/res"
}
}
test {

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +1,241 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize() {
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.forEach { chunk ->
class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize(optimizationsEnabled: Boolean, errors: IErrorReporter) {
if(!optimizationsEnabled)
return optimizeOnlyJoinChunks()
peepholeOptimize()
val remover = IRUnusedCodeRemover(irprog, errors)
var totalRemovals = 0
do {
val numRemoved = remover.optimize()
totalRemovals += numRemoved
} while(numRemoved>0 && errors.noErrors())
errors.report()
if(totalRemovals>0) {
irprog.linkChunks() // re-link again.
}
}
private fun optimizeOnlyJoinChunks() {
irprog.foreachSub { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
}
irprog.linkChunks() // re-link
}
private fun peepholeOptimize() {
irprog.foreachSub { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
sub.chunks.withIndex().forEach { (index, chunk1) ->
// we don't optimize Inline Asm chunks here.
if(chunk is IRCodeChunk) {
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
if(chunk1 is IRCodeChunk) {
do {
val indexedInstructions = chunk.lines.withIndex()
.filter { it.value is IRCodeInstruction }
.map { IndexedValue(it.index, (it.value as IRCodeInstruction).ins) }
val changed = removeNops(chunk, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk, indexedInstructions)
|| removeWeirdBranches(chunk, indexedInstructions)
|| removeDoubleSecClc(chunk, indexedInstructions)
|| cleanupPushPop(chunk, indexedInstructions)
// TODO other optimizations:
// more complex optimizations such as unused registers
val indexedInstructions = chunk1.instructions.withIndex()
.map { IndexedValue(it.index, it.value) }
val changed = removeNops(chunk1, indexedInstructions)
|| replaceConcatZeroMsbWithExt(chunk1, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions)
|| removeUselessArithmetic(chunk1, indexedInstructions)
|| removeNeedlessCompares(chunk1, indexedInstructions)
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions)
// TODO other optimizations
} while (changed)
}
}
removeEmptyChunks(sub)
}
// TODO also do register optimization step here at the end?
irprog.linkChunks() // re-link
}
private fun replaceConcatZeroMsbWithExt(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.CONCAT) {
// if the previous instruction loads a zero in the msb, this can be turned into EXT.B instead
val prev = indexedInstructions[idx-1].value
if(prev.opcode==Opcode.LOAD && prev.immediate==0 && prev.reg1==ins.reg2) {
chunk.instructions[idx] = IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = ins.reg1, reg2 = ins.reg3)
chunk.instructions.removeAt(idx-1)
changed = true
}
}
}
return changed
}
private fun removeEmptyChunks(sub: IRSubroutine) {
if(sub.chunks.isEmpty())
return
/*
Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1.
If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label ->
should not have been generated! ERROR.
*/
val relabelChunks = mutableListOf<Pair<Int, String>>()
val removeChunks = mutableListOf<Int>()
val replaceLabels = mutableMapOf<String, String>()
sub.chunks.withIndex().forEach { (index, chunk) ->
if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) {
if(chunk.label==null) {
removeChunks += index
} else {
if (index < sub.chunks.size - 1) {
val nextchunk = sub.chunks[index + 1]
if (nextchunk.label == null) {
// can transplant label to next chunk and remove this empty one.
relabelChunks += Pair(index + 1, chunk.label!!)
removeChunks += index
} else {
// merge both labels into 1 except if this is the label chunk at the start of the subroutine
if(index>0) {
if (chunk.label == nextchunk.label)
removeChunks += index
else {
removeChunks += index
replaceLabels[chunk.label!!] = nextchunk.label!!
replaceLabels.entries.forEach { (key, value) ->
if (value == chunk.label)
replaceLabels[key] = nextchunk.label!!
}
}
}
}
}
}
}
}
relabelChunks.forEach { (index, label) ->
val chunk = IRCodeChunk(label, null)
val subChunk = sub.chunks[index]
chunk.instructions += subChunk.instructions
if(subChunk is IRCodeChunk)
chunk.appendSrcPositions(subChunk.sourceLinesPositions)
sub.chunks[index] = chunk
}
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
sub.chunks.forEach { chunk ->
chunk.instructions.withIndex().forEach { (idx, instr) ->
instr.labelSymbol?.let {
if(instr.opcode in OpcodesThatBranch) {
replaceLabels.forEach { (from, to) ->
if (it == from) {
chunk.instructions[idx] = instr.copy(labelSymbol = to)
}
else {
val actualPrefix = "$from."
if (it.startsWith(actualPrefix))
chunk.instructions[idx] = instr.copy(labelSymbol = "$to.")
}
}
}
}
}
}
}
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun joinChunks(sub: IRSubroutine) {
// Subroutine contains a list of chunks. Some can be joined into one.
if(sub.chunks.isEmpty())
return
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null)
return false
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
// if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
val lastInstruction = previous.instructions.lastOrNull()
if(lastInstruction!=null)
return lastInstruction.opcode !in OpcodesThatJump
return true
}
return false
}
val chunks = mutableListOf<IRCodeChunkBase>()
chunks += sub.chunks[0]
for(ix in 1 until sub.chunks.size) {
val lastChunk = chunks.last()
val candidate = sub.chunks[ix]
when(candidate) {
is IRCodeChunk -> {
if(mayJoinCodeChunks(lastChunk, candidate)) {
lastChunk.instructions += candidate.instructions
lastChunk.next = candidate.next
if(lastChunk is IRCodeChunk)
lastChunk.appendSrcPositions(candidate.sourceLinesPositions)
}
else
chunks += candidate
}
is IRInlineAsmChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
else
chunks += candidate
}
}
is IRInlineBinaryChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
else
chunks += candidate
}
}
}
}
sub.chunks.clear()
sub.chunks += chunks
}
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// push followed by pop to same target, or different target->replace with load
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.PUSH) {
if(idx < chunk.lines.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
if(insAfter!=null && insAfter.ins.opcode == Opcode.POP) {
if(ins.reg1==insAfter.ins.reg1) {
chunk.lines.removeAt(idx)
chunk.lines.removeAt(idx)
if(idx < chunk.instructions.size-1) {
val insAfter = chunk.instructions[idx+1]
if(insAfter.opcode == Opcode.POP) {
if(ins.reg1==insAfter.reg1) {
chunk.instructions.removeAt(idx)
chunk.instructions.removeAt(idx)
} else {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOADR, ins.type, reg1=insAfter.ins.reg1, reg2=ins.reg1)
chunk.lines.removeAt(idx+1)
chunk.instructions[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1)
chunk.instructions.removeAt(idx+1)
}
changed = true
}
@ -49,24 +245,24 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// double sec, clc
// sec+clc or clc+sec
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
if(idx < chunk.lines.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
if(insAfter?.ins?.opcode == ins.opcode) {
chunk.lines.removeAt(idx)
if(idx < chunk.instructions.size-1) {
val insAfter = chunk.instructions[idx+1]
if(insAfter.opcode == ins.opcode) {
chunk.instructions.removeAt(idx)
changed = true
}
else if(ins.opcode== Opcode.SEC && insAfter?.ins?.opcode== Opcode.CLC) {
chunk.lines.removeAt(idx)
else if(ins.opcode== Opcode.SEC && insAfter.opcode== Opcode.CLC) {
chunk.instructions.removeAt(idx)
changed = true
}
else if(ins.opcode== Opcode.CLC && insAfter?.ins?.opcode== Opcode.SEC) {
chunk.lines.removeAt(idx)
else if(ins.opcode== Opcode.CLC && insAfter.opcode== Opcode.SEC) {
chunk.instructions.removeAt(idx)
changed = true
}
}
@ -75,73 +271,147 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
// jump/branch to label immediately below
private fun removeWeirdBranches(chunk: IRCodeChunk, nextChunk: IRCodeChunkBase?, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
val labelSymbol = ins.labelSymbol
// remove jump/branch to label immediately below (= next chunk if it has that label)
if(ins.opcode== Opcode.JUMP && labelSymbol!=null) {
// if jumping to label immediately following this
if(idx < chunk.lines.size-1) {
val label = chunk.lines[idx+1] as? IRCodeLabel
if(labelSymbol.size==1 && label?.name == labelSymbol[0]) {
chunk.lines.removeAt(idx)
if(idx==chunk.instructions.size-1 && ins.branchTarget===nextChunk) {
chunk.instructions.removeAt(idx)
changed = true
}
}
// remove useless RETURN
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) {
val previous = chunk.instructions[idx-1]
if(previous.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx)
changed = true
}
}
// replace subsequent opcodes that jump by just the first
if(idx>0 && (ins.opcode in OpcodesThatJump)) {
val previous = chunk.instructions[idx-1]
if(previous.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx)
changed = true
}
}
// replace call + return --> jump
// This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
// If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
// if(idx>0 && ins.opcode==Opcode.RETURN) {
// val previous = chunk.instructions[idx-1]
// if(previous.opcode==Opcode.CALL) {
// chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
// chunk.instructions.removeAt(idx)
// changed = true
// }
// }
}
return changed
}
private fun removeNeedlessCompares(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// a CMPI with 0, after an instruction like LOAD that already sets the status bits, can be removed.
// but only if the instruction after it is not using the Carry bit because that won't be set by a LOAD instruction etc.
var changed = false
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.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
}
}
}
}
// a SNZ etc. whose target register is not used can be removed altogether
if(ins.opcode in OpcodesThatSetRegFromStatusbits) {
val usages = regUsages(ins.reg1!!)
if(usages.toList().sumOf { it.second } <= 1) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
return changed
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun regUsages(register: Int): Map<IRCodeChunkBase, Int> {
val chunks = mutableMapOf<IRCodeChunkBase, Int>()
irprog.foreachSub { sub ->
sub.chunks.forEach { chunk ->
val used = chunk.usedRegisters()
val numUsages = used.readRegs.getOrDefault(register, 0) + used.writeRegs.getOrDefault(register, 0)
if(numUsages>0) {
chunks[chunk] = numUsages
}
}
}
return chunks
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
when (ins.opcode) {
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
if (ins.value == 1) {
chunk.lines.removeAt(idx)
if (ins.immediate == 1) {
chunk.instructions.removeAt(idx)
changed = true
}
}
Opcode.ADD, Opcode.SUB -> {
if (ins.value == 1) {
chunk.lines[idx] = IRCodeInstruction(
if (ins.immediate == 1) {
chunk.instructions[idx] = IRInstruction(
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
ins.type,
ins.reg1
)
changed = true
} else if (ins.value == 0) {
chunk.lines.removeAt(idx)
} else if (ins.immediate == 0) {
chunk.instructions.removeAt(idx)
changed = true
}
}
Opcode.AND -> {
if (ins.value == 0) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
if (ins.immediate == 0) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
changed = true
} else if (ins.value == 255 && ins.type == VmDataType.BYTE) {
chunk.lines.removeAt(idx)
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
chunk.instructions.removeAt(idx)
changed = true
} else if (ins.value == 65535 && ins.type == VmDataType.WORD) {
chunk.lines.removeAt(idx)
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
chunk.instructions.removeAt(idx)
changed = true
}
}
Opcode.OR -> {
if (ins.value == 0) {
chunk.lines.removeAt(idx)
if (ins.immediate == 0) {
chunk.instructions.removeAt(idx)
changed = true
} else if ((ins.value == 255 && ins.type == VmDataType.BYTE) || (ins.value == 65535 && ins.type == VmDataType.WORD)) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) || (ins.immediate == 65535 && ins.type == IRDataType.WORD)) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
changed = true
}
}
Opcode.XOR -> {
if (ins.value == 0) {
chunk.lines.removeAt(idx)
if (ins.immediate == 0) {
chunk.instructions.removeAt(idx)
changed = true
}
}
@ -151,30 +421,33 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.NOP) {
changed = true
chunk.lines.removeAt(idx)
chunk.instructions.removeAt(idx)
}
}
return changed
}
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
return false
/*
var changed = false
indexedInstructions.forEach { (idx, ins) ->
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
// TODO: detect multiple float ffrom/fto to the same target, only keep first
// TODO: detect multiple sequential rnd with same reg1, only keep one
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
// TODO: detect multiple same ands, ors; only keep first
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them
// ...
}
return changed
*/
}
}

View File

@ -0,0 +1,103 @@
package prog8.codegen.intermediate
import prog8.intermediate.IRProgram
class IRRegisterOptimizer(private val irProg: IRProgram) {
fun optimize() {
// reuseRegisters()
}
/*
TODO: this register re-use renumbering isn't going to work like this,
because subroutines will be clobbering the registers that the subroutine
which is calling them might be using...
private fun reuseRegisters() {
fun addToUsage(usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>>,
regnum: Int,
dt: IRDataType,
chunk: IRCodeChunkBase) {
val key = regnum to dt
val chunks = usage[key] ?: mutableSetOf()
chunks.add(chunk)
usage[key] = chunks
}
val usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>> = mutableMapOf()
irProg.foreachCodeChunk { chunk ->
chunk.usedRegisters().regsTypes.forEach { (regNum, types) ->
types.forEach { dt ->
addToUsage(usage, regNum, dt, chunk)
}
}
}
val registerReplacements = usage.asSequence()
.filter { it.value.size==1 }
.map { it.key to it.value.iterator().next() }
.groupBy({ it.second }, {it.first})
.asSequence()
.associate { (chunk, registers) ->
chunk to registers.withIndex().associate { (index, reg) -> reg to 50000+index }
}
registerReplacements.forEach { replaceRegisters(it.key, it.value) }
}
private fun replaceRegisters(chunk: IRCodeChunkBase, replacements: Map<Pair<Int, IRDataType>, Int>) {
val (rF, rI) = replacements.asSequence().partition { it.key.second==IRDataType.FLOAT }
val replacementsInt = rI.associate { it.key.first to it.value }
val replacementsFloat = rF.associate { it.key.first to it.value }
fun replaceRegs(fcallArgs: FunctionCallArgs?): FunctionCallArgs? {
if(fcallArgs==null)
return null
val args = if(fcallArgs.arguments.isEmpty()) fcallArgs.arguments else {
fcallArgs.arguments.map {
FunctionCallArgs.ArgumentSpec(
it.name,
it.address,
FunctionCallArgs.RegSpec(
it.reg.dt,
if(it.reg.dt==IRDataType.FLOAT)
replacementsFloat.getOrDefault(it.reg.registerNum, it.reg.registerNum)
else
replacementsInt.getOrDefault(it.reg.registerNum, it.reg.registerNum),
it.reg.cpuRegister
)
)
}
}
val rt = fcallArgs.returns
val returns = if(rt==null) null else {
FunctionCallArgs.RegSpec(
rt.dt,
if(rt.dt==IRDataType.FLOAT)
replacementsFloat.getOrDefault(rt.registerNum, rt.registerNum)
else
replacementsInt.getOrDefault(rt.registerNum, rt.registerNum),
rt.cpuRegister
)
}
return FunctionCallArgs(args, returns)
}
fun replaceRegs(instruction: IRInstruction): IRInstruction {
val reg1 = replacementsInt.getOrDefault(instruction.reg1, instruction.reg1)
val reg2 = replacementsInt.getOrDefault(instruction.reg2, instruction.reg2)
val fpReg1 = replacementsFloat.getOrDefault(instruction.fpReg1, instruction.fpReg1)
val fpReg2 = replacementsFloat.getOrDefault(instruction.fpReg2, instruction.fpReg2)
return instruction.copy(reg1 = reg1, reg2 = reg2, fpReg1 = fpReg1, fpReg2 = fpReg2, fcallArgs = replaceRegs(instruction.fcallArgs))
}
val newInstructions = chunk.instructions.map {
replaceRegs(it)
}
chunk.instructions.clear()
chunk.instructions.addAll(newInstructions)
}
*/
}

View File

@ -0,0 +1,266 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.intermediate.*
class IRUnusedCodeRemover(
private val irprog: IRProgram,
private val errors: IErrorReporter
) {
fun optimize(): Int {
var numRemoved = removeUnusedSubroutines() + removeUnusedAsmSubroutines()
// remove empty blocks
irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) {
irprog.blocks.remove(block)
pruneSymboltable(block.label)
numRemoved++
}
}
return numRemoved
}
private fun pruneSymboltable(blockLabel: String) {
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
val prefix = "$blockLabel."
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
blockVars.forEach { stVar ->
irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
chunk.instructions.forEach { ins ->
if(ins.labelSymbol == stVar.name) {
return // symbol occurs in an instruction
}
}
}
irprog.st.allVariables().forEach { variable ->
val initValue = variable.onetimeInitializationArrayValue
if(!initValue.isNullOrEmpty()) {
if(initValue.any {
it.addressOfSymbol?.startsWith(blockLabel)==true
})
return // symbol occurs in an initializer value (address-of this symbol)_
}
}
}
irprog.st.removeTree(blockLabel)
}
private fun removeUnusedSubroutines(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
irprog.foreachCodeChunk { chunk ->
chunk.label?.let { allLabeledChunks[it] = chunk }
}
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!block.options.ignoreUnused) {
errors.info("unused subroutine '${sub.label}'", sub.position)
}
block.children.remove(sub)
irprog.st.removeTree(sub.label)
numRemoved++
}
}
}
return numRemoved
}
private fun removeUnusedAsmSubroutines(): Int {
val allLabeledAsmsubs = irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
.associateBy { it.label }
var numRemoved = removeSimpleUnlinkedAsmsubs(allLabeledAsmsubs)
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!block.options.ignoreUnused) {
errors.info("unused subroutine '${sub.label}'", sub.position)
}
block.children.remove(sub)
irprog.st.removeTree(sub.label)
numRemoved++
}
}
}
return numRemoved
}
private fun removeSimpleUnlinkedAsmsubs(allSubs: Map<String, IRAsmSubroutine>): Int {
val linkedAsmSubs = mutableSetOf<IRAsmSubroutine>()
// TODO: asmsubs in library modules are never removed, we can't really tell here if they're actually being called or not...
// check if asmsub is called from another asmsub
irprog.blocks.asSequence().forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
if (block.options.forceOutput || block.library)
linkedAsmSubs += sub
if (sub.asmChunk.isNotEmpty()) {
allSubs.forEach { (label, asmsub) ->
if (sub.asmChunk.assembly.contains(label))
linkedAsmSubs += asmsub
}
}
val inlineAsm = sub.asmChunk.next as? IRInlineAsmChunk
if(inlineAsm!=null) {
allSubs.forEach { (label, asmsub) ->
if (inlineAsm.assembly.contains(label))
linkedAsmSubs += asmsub
}
}
}
}
// check if asmsub is linked or called from another regular subroutine
irprog.foreachCodeChunk { chunk ->
chunk.instructions.forEach {
it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } }
// note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
}
}
return removeUnlinkedAsmsubs(linkedAsmSubs)
}
private fun removeUnlinkedAsmsubs(linkedAsmSubs: Set<IRAsmSubroutine>): Int {
var numRemoved = 0
irprog.blocks.asSequence().forEach { block ->
block.children.withIndex().reversed().forEach { (index, child) ->
if(child is IRAsmSubroutine && child !in linkedAsmSubs) {
block.children.removeAt(index)
numRemoved++
}
}
}
return numRemoved
}
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
val entrypointSub = irprog.blocks.single { it.label=="main" }
.children.single { it is IRSubroutine && it.label=="main.start" }
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
reachable.add(irprog.globalInits)
// all chunks referenced in array initializer values are also 'reachable':
irprog.st.allVariables()
.filter { !it.uninitialized }
.forEach {
it.onetimeInitializationArrayValue?.let { array ->
array.forEach {elt ->
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
reachable.add(irprog.getChunkWithLabel(elt.addressOfSymbol!!))
}
}
}
fun grow() {
val new = mutableSetOf<IRCodeChunkBase>()
reachable.forEach {
it.next?.let { next -> new += next }
it.instructions.forEach { instr ->
if (instr.branchTarget == null)
instr.labelSymbol?.let { label ->
val chunk = allLabeledChunks[label] ?: allLabeledChunks[label.substringBeforeLast('.')]
if(chunk!=null)
new+=chunk
else
allLabeledChunks[label]?.let { new += it }
}
else
new += instr.branchTarget!!
}
}
reachable += new
}
var previousCount = reachable.size
while(true) {
grow()
if(reachable.size<=previousCount)
break
previousCount = reachable.size
}
return removeUnlinkedChunks(reachable)
}
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
// all chunks referenced in array initializer values are linked as well!:
irprog.st.allVariables()
.filter { !it.uninitialized }
.forEach {
it.onetimeInitializationArrayValue?.let { array ->
array.forEach {elt ->
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
linkedChunks += irprog.getChunkWithLabel(elt.addressOfSymbol!!)
}
}
}
irprog.foreachCodeChunk { chunk ->
chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach {
if(it.branchTarget==null) {
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
} else {
linkedChunks += it.branchTarget!!
}
}
if (chunk.label == "main.start")
linkedChunks += chunk
}
// 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 }
}
}
}
linkedChunks.add(irprog.globalInits)
return removeUnlinkedChunks(linkedChunks)
}
private fun removeUnlinkedChunks(linkedChunks: Set<IRCodeChunkBase>): Int {
var numRemoved = 0
irprog.foreachSub { sub ->
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
if (chunk !in linkedChunks) {
if (chunk === sub.chunks[0]) {
when(chunk) {
is IRCodeChunk -> {
if (chunk.isNotEmpty()) {
// don't remove the first chunk of the sub itself because it has to have the name of the sub as label
chunk.instructions.clear()
numRemoved++
}
}
is IRInlineAsmChunk, is IRInlineBinaryChunk -> {
sub.chunks[index] = IRCodeChunk(chunk.label, chunk.next)
numRemoved++
}
}
} else {
sub.chunks.removeAt(index)
numRemoved++
}
}
}
}
return numRemoved
}
}

View File

@ -0,0 +1,47 @@
package prog8.codegen.intermediate
import prog8.code.ast.*
internal fun makeAllNodenamesScoped(program: PtProgram) {
val renames = mutableListOf<Pair<PtNamedNode, String>>()
fun recurse(node: PtNode) {
node.children.forEach {
if(it is PtNamedNode)
renames.add(it to it.scopedName)
recurse(it)
}
}
recurse(program)
renames.forEach { it.first.name = it.second }
}
internal fun moveAllNestedSubroutinesToBlockScope(program: PtProgram) {
val movedSubs = mutableListOf<Pair<PtBlock, PtSub>>()
val removedSubs = mutableListOf<Pair<PtSub, PtSub>>()
fun moveToBlock(block: PtBlock, parent: PtSub, asmsub: PtAsmSub) {
block.add(asmsub)
parent.children.remove(asmsub)
}
fun moveToBlock(block: PtBlock, parent: PtSub, sub: PtSub) {
sub.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, sub, subsub) }
sub.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub -> moveToBlock(block, sub, asmsubsub) }
movedSubs += Pair(block, sub)
removedSubs += Pair(parent, sub)
}
program.allBlocks().forEach { block ->
block.children.toList().forEach {
if (it is PtSub) {
// Only regular subroutines can have nested subroutines.
it.children.filterIsInstance<PtSub>().forEach { subsub -> moveToBlock(block, it, subsub) }
it.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub -> moveToBlock(block, it, asmsubsub) }
}
}
}
removedSubs.forEach { (parent, sub) -> parent.children.remove(sub) }
movedSubs.forEach { (block, sub) -> block.add(sub) }
}

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