Compare commits

..

498 Commits
v10.1 ... v10.5

Author SHA1 Message Date
25d2b42283 textelite now with sysinit, so it runs on the c128 as well (needs banking) 2024-11-09 16:03:16 +01:00
300d1a871c c128 banks out basic, added banks() and getbanks() 2024-11-09 15:44:08 +01:00
2fcb83a39f version 10.5 2024-11-09 14:04:04 +01:00
3ba1d00a7c add unit test for @dirty variables 2024-11-09 13:31:54 +01:00
64164c1c72 changed @initonce to @dirty and meaning is now: not initialized at all. 2024-11-08 22:05:31 +01:00
3ee6058524 todo 2024-11-08 19:57:38 +01:00
93a0a41e73 Merge branch 'initonce-var-tag'
# Conflicts:
#	examples/test.p8
2024-11-08 19:32:30 +01:00
e7ab7b6d7a neo skeletons added in docs 2024-11-08 19:19:11 +01:00
7d4dc3c063 update 2024-11-08 19:04:51 +01:00
a50400b7d1 initial neo6502 target 2024-11-08 19:04:49 +01:00
f89f1a84d0 @initonce variable tag to skip variable reinitialization 2024-11-08 19:03:48 +01:00
688dce6145 floats: added AYINT2 as a safe wrapper for AYINT. Internal float to word cast now also uses that. 2024-11-08 18:52:48 +01:00
b88f550c5b todo 2024-11-07 00:48:13 +01:00
9864abd393 romsub keyword is now extsub 2024-11-06 22:14:53 +01:00
c702c4a6df internal rename of romsub to extsub 2024-11-06 21:42:16 +01:00
77e376f6bf romsub @bank now also accepts a variable so the bank can be dynamic 2024-11-06 00:02:36 +01:00
491e5dbcfb move the program startup and cleanup machinery to the front of the program to keep it in system ram 2024-11-05 22:12:25 +01:00
a5c7393561 tweaking program startup and cleanup stuff 2024-11-05 21:12:27 +01:00
7fd3e9bb7d also provide a X16-style JSRFAR implementation for the C64. Enable callfar() and callfar2() on the C64 and C128. 2024-11-05 19:26:58 +01:00
459e9f8f3b jsrfar stuff 2024-11-05 01:06:06 +01:00
5b1143bcb3 C64: add support for calling romsub with bank ('jsrfar') 2024-11-04 23:26:21 +01:00
fddd390d31 on the C64, if not using floats, disable basic ROM in startup to gain another 8Kb of RAM
MEMTOP is adjusted to $d000. This gives us 50 Kb of contiguous program RAM space. ($0801-$CFFF)
2024-11-04 22:11:44 +01:00
e514eeba17 added c64.banks() and c64.getbanks() and c64 banking example 2024-11-04 20:14:30 +01:00
c11a52b278 added cx16 banking example 2024-11-03 21:52:04 +01:00
85e87dfe2e consolidate @rombank and @rambank into just @bank 2024-11-03 21:15:11 +01:00
cb47e2c149 documented the romsub bank additions 2024-11-03 20:39:44 +01:00
0fc9aa6b2d cx16: romsubs of the audio routines now have the rom bank tag.
cx16: removed 'audio' module again, no longer needed to have these stubs
2024-11-03 18:35:10 +01:00
155896c4c7 added @rombank and @rambank bank number tags on romsubs
on cx16 and c128 targets the compiler then automatically inserts a CALLFAR instead of a regular JSR to automatically do the bank switching.
2024-11-03 18:19:31 +01:00
178e60bba0 fix ast source gen for romsub 2024-11-03 15:04:53 +01:00
9f84aa5fb2 fix double %option merge problem where it deleted all of the blocks 2024-11-03 13:36:14 +01:00
66fc109ce5 correct program name in help 2024-11-02 22:16:57 +01:00
a231872821 tip for using aliases for the virtual registers r0-r15 2024-11-02 22:09:20 +01:00
7cfb33a448 tweak & fix if expression with word condition 2024-11-02 22:01:57 +01:00
3b798097b9 added memtop to machine definition and asm source code check
added %memtop directive
2024-11-02 00:59:07 +01:00
6fb05bdefc replaced deprecated cx16 ZSOUND example by new ZSMKIT examples 2024-11-01 23:17:23 +01:00
64ea72ed4d tweak plot 2024-11-01 21:56:27 +01:00
89425088ce taking address of a split word array is no longer a fatal error but a warning and the array is turned back into a normal word array. 2024-11-01 20:18:31 +01:00
925b9d845d fix split array possible compiler loop (due to wrong datatype replacement) 2024-11-01 19:18:03 +01:00
ad074076c2 remove last references to gfx2 module 2024-11-01 18:41:36 +01:00
a2194c43a6 fix benchmark 2024-11-01 03:50:13 +01:00
4b23b1dc86 don't always import math automatically anymore 2024-11-01 03:39:52 +01:00
9005c7994a added Linear Interpolation (LERP) functions: math.lerp(), floats.lerp(), floats.lerp_fast() 2024-11-01 02:05:48 +01:00
4a47e15b1c fix IR if expression sometimes lacking a cmpi after calculation of the condition value
VM/IR: add a returni immediate value return instruction to replace certain returnr's
2024-11-01 01:04:16 +01:00
09cbdf410a added diskio.exists(), made f_close_w() idempotent like f_close() already was 2024-10-31 21:25:22 +01:00
df6a43c7f0 gfx_lores now has drawmode_eor() (used by Paint, for example) 2024-10-31 01:28:29 +01:00
4ce130dc8b split up cx16.gfx2 module into gfx_lores and gfx_hires4 modules 2024-10-30 22:21:07 +01:00
94d76aa82c cx16.vaddr(), vaddr_clone(), vaddr_autoincr(), vaddr_autodecr() now all reset vera's ADDRSEL back to 0 even if the configured port was 1 2024-10-30 21:40:58 +01:00
73609636c5 gfx_lores.set_screen_mode() is now gfx_lores.graphics_mode()
adding all missing routines from gfx2 to gfx_lores
2024-10-30 21:39:37 +01:00
66b06d6c40 added gfx2.safe_vertical_line, gfx2.safe_rect, gfx2.safe_fillrect for completeness 2024-10-30 19:03:40 +01:00
eeeb8d81f4 merge now also allows monkeypatching if signature is 100% identical 2024-10-30 01:15:56 +01:00
6f727aff88 fix beanshell compile with jdk11 2024-10-29 23:42:37 +01:00
518e5a30c2 slight parser rule tweak 2024-10-29 23:18:17 +01:00
bbba4b3d60 new block merge semantics and implementation 2024-10-29 22:57:54 +01:00
967adb9a87 Merge branch 'beanshell' 2024-10-29 20:55:09 +01:00
040a6c62de added a beanshell interpreter experiment 2024-10-29 20:52:41 +01:00
483d193ced vm: implemented reading/writing files in diskio 2024-10-29 02:34:53 +01:00
62458216c9 first skeleton of LSP language server 2024-10-28 21:42:20 +01:00
76b05cb5fd fix chained aliasing 2024-10-28 18:35:23 +01:00
570b574b93 added sys.memcmp 2024-10-28 00:41:26 +01:00
a82f211f9a added alias statement 2024-10-28 00:36:10 +01:00
504c80cddf fix parser rule for identifiers (void is a keyword, not an identifier) 2024-10-27 15:57:27 +01:00
4b4af9b527 no longer silently add RTS to asmsubs that don't have one 2024-10-27 13:49:00 +01:00
28b383f888 docs and syntax for @alignxxx and %align 2024-10-27 00:47:52 +02:00
40ce7725a1 cleanup c64 sprite examples 2024-10-26 21:36:11 +02:00
1f2d46628e remove %option align_xxx (block level alignment, as we now have better alternatives) 2024-10-26 21:18:34 +02:00
c9535049c8 %align directive and @align64 2024-10-26 20:58:35 +02:00
9317cf8a35 sorting aligned vars to shrink prg size 2024-10-26 18:33:51 +02:00
1cd754f05d adding @alignword/page on individual variables 2024-10-26 17:00:38 +02:00
97b8cb748d more ifexpression codegen tweaks 2024-10-25 22:52:26 +02:00
84d9040b57 make BIT test also work on signed byte variables. Fixed an address-of optimization error. 2024-10-23 22:34:18 +02:00
fdd18c615c more ifexpresssion codegen tweaks 2024-10-23 21:04:55 +02:00
c14f6cfc2b more optimal if expression code 2024-10-22 23:49:24 +02:00
326eab3dd1 unit test for defer, describe defer and if expression in docs 2024-10-22 22:19:49 +02:00
6da1f7eb4c don't remove essential subroutines even though they seem unused 2024-10-22 21:17:02 +02:00
1e82483152 ast printer correctly prints ifexpression 2024-10-22 21:14:55 +02:00
6e2fd41a8b ast printer correctly prints unroll and continue 2024-10-22 21:14:26 +02:00
9927af1095 about var inits 2024-10-22 01:08:42 +02:00
7585b6ef6f fix issues with calling the defer handler 2024-10-21 19:49:38 +02:00
a6159702da defers are now only registered/called when flow of control actually reached the defer statement
a defer statement sets its corresponding bit in a bitmask that is shifted in the defer handler routine to see what defer blocks to call.
2024-10-21 00:55:51 +02:00
0247fb0d84 some ast2 var tweaks 2024-10-21 00:20:54 +02:00
6de760885f fix defer push/pop typecasting issues 2024-10-19 21:45:49 +02:00
9851d14fb9 added if expression: ubyte a = if b>0 44 else 55
it doesn't generate the best code yet, like regular ifs do.
2024-10-19 15:34:04 +02:00
d5fc69d3e4 fix instruction index error in optimizer 2024-10-19 14:45:53 +02:00
a40d120f2a more defer sanity checks 2024-10-18 22:32:49 +02:00
fcdd9414d9 fix defer interfering with return value, fix prefix expression error when operand is functioncall that doesn't return a value. 2024-10-18 21:43:09 +02:00
272a1001a8 fix bad optimization of floats.pop/push call 2024-10-18 21:04:18 +02:00
2a52241f1c defer is now done *after* calculating a return value 2024-10-18 20:56:27 +02:00
d8f1822c12 fixes 2024-10-18 20:32:46 +02:00
ce7d094adb Zig-like "defer" to clean up stuff when leaving the scope of the current routine. 2024-10-18 01:30:20 +02:00
a0cf1889a3 omit more redundant 0-initializations ("stz's") 2024-10-17 22:51:39 +02:00
38ef394e15 IR codegen: global vars with numeric initialization value are now also put into the VARIABLESWITHINIT section rather than requiring explicit code instructions to initialize them in INITGLOBALS.
Note that something similar, such as putting those variables inline in the program initialized with their value and all, cannot be done for the 6502 codegen: the program needs a mechanism to reset ALL variables when it runs a second time.
2024-10-16 22:15:51 +02:00
abbf7c7cb0 compiler name change: prog8c (was p8compile)
fat jar file also changed:  prog8c-X.Y-all.jar    (was: prog8compiler-X.Y-all.jar)
2024-10-16 18:36:19 +02:00
ca5f7ae32f global (block-level) variables that get initialized with an array index expression now get a constant value as well if possible. This reduces the number of instructions in the init globals code block 2024-10-16 02:14:19 +02:00
cbc4b75e50 IR now contains "bool" as a type instead of already erasing it into "ubyte". (boolean literals still are simply just 1 and 0 values) 2024-10-16 01:03:35 +02:00
65ddcf91d0 remove unused syscalls 2024-10-15 18:24:55 +02:00
5280e1b449 err msgs 2024-10-13 21:33:13 +02:00
b6ffb81909 Merge branch 'next-version' 2024-10-13 21:20:10 +02:00
e9edffa9f0 remove support for array-to-array assignments (other than initialization of variable declaration)
Just use an explicit sys.memcopy(src, dest, sizeof(dest))  or assign array members individually.
2024-10-13 20:02:43 +02:00
0dd1c17ff4 avoid possible crash 2024-10-13 17:51:14 +02:00
aef211e5f3 stricter array literal element type handling (number,bool,address-of).
More consistent implicit address-of handling if array literals contain by-ref identifiers (such as subroutine names)
2024-10-13 17:46:41 +02:00
66829203d8 New [x]*42 syntax to create array literals with repeated values (like "abc"*10 already exists for strings)
Should be used in place of array initializer expressions that contain only a single numeric value to initialize the whole array with. That isn't supported anymore.
2024-10-13 05:16:08 +02:00
7a0eaf3148 Remove array initialization by single value.
New compiler and kotlin version.
2024-10-13 04:31:56 +02:00
fa5479ee5f fix ast printing of arrays with duplicate elements 2024-10-13 04:30:46 +02:00
03412cacba added examples/cx16/balloonflight.p8 2024-10-13 00:51:07 +02:00
01a38a0b11 Merge branch 'monogfx_fill_optimization' 2024-10-12 17:29:38 +02:00
f43c14bd78 doc 2024-10-12 17:29:28 +02:00
fb23452383 optimize monogfx.fill() 2024-10-12 17:18:00 +02:00
ab7dde1450 todo 2024-10-12 13:04:19 +02:00
8d9bc2f5ff fixing all sorts of things about assigning arrays to arrays 2024-10-12 12:33:46 +02:00
7651ccc84e fix a type error 2024-10-11 00:50:05 +02:00
1a6b95b388 house cleaning 2024-10-10 20:46:18 +02:00
78ec1e7512 version 2024-10-09 22:21:04 +02:00
7e38d26c33 added several color fade functions to the palette module (cx16) 2024-10-09 21:48:04 +02:00
ed09dd4e9e improve automatic type conversions for return values, fixes #155 2024-10-09 20:04:05 +02:00
5731b79554 don't allow problematic string and array assignments anymore, improve error messages.
In certain cases you will need to use string.copy() explicitly to overwrite strings with new strings.
2024-10-09 00:51:05 +02:00
eaa22a9d13 added callfar2() builtin function that allows to set A,X,Y and Carry arguments. 2024-10-08 21:36:04 +02:00
b2bdfe8482 fix ir rndseed() 2024-10-08 20:40:00 +02:00
fea531be9a add sys.sizeof_bool, _ubyte, _uword constants 2024-10-07 20:45:13 +02:00
7c69d38588 scan all asmsubs to see if another subroutine is referenced. Fixes #153 2024-10-07 20:39:49 +02:00
a088ee56b0 function inlining can no longer get into an infinite loop. Fixes #154 2024-10-07 19:58:04 +02:00
ae669af904 add sys.sizeof_byte, _word, _float constants
because Antlr doesn't allow the grammar to contain a sizeof(typename) rule to override the sizeof(identifier) rule
2024-10-07 19:17:37 +02:00
d1ddf05e38 check that block address leaves room for program startup logic 2024-10-03 22:30:06 +02:00
51279a98b3 attempt to fix forloop range datatype issues 2024-10-03 21:12:31 +02:00
bf33a4f82d small refactor to prepare for better range dt adjustment 2024-10-02 23:28:33 +02:00
fff0d741c3 improved parsing of "not in" operator, and [] array signature (allow space) 2024-10-02 19:06:20 +02:00
e83d0ee820 fix crash in msb() when assigning to word again. Fix wrong register in lsb() and msb() in certain situations. 2024-10-02 02:40:16 +02:00
09f3eecf56 changed cx16/rotating-stars example to starszoom instead. 2024-10-02 01:36:54 +02:00
2bd4326ff6 added cx16/rotating-stars example 2024-10-01 23:43:50 +02:00
c13168b60c various improvements:
fix verafx.available().
added gfx_lores.plot().
faster gfx_lores.clear_screen().
added a new Sublime Text 4 syntax highlighting file.
2024-10-01 22:18:03 +02:00
ea3871d0c4 comment about builtin function call ast node type 2024-10-01 02:14:31 +02:00
70a2b11271 New example program: draw a fractal tree (#152)
* demo of prog8 recursion to draw a fractal tree

* feat: comments

* fix: comment formatting

* feat: make minimum branch size a tunable parameter
2024-10-01 02:00:04 +02:00
3cf39e072e fix C64 floating point sign issue 2024-09-30 21:56:34 +02:00
413b86cc4a more helpful error messages 2024-09-30 00:27:29 +02:00
a6107fcfdf taking the address of a romsub is now the constant value of said romsub's declared address 2024-09-29 23:53:39 +02:00
a064ade1e0 better codegen for call() function 2024-09-29 23:18:51 +02:00
df35aa7942 added (experimental) compression library 2024-09-29 18:59:53 +02:00
cd49c5f88d cx16: set rom bank to 0 at startup (for faster kernal API calls)
cx16: callfar() with constant address generates shorter asm
2024-09-28 20:58:29 +02:00
1541ad2160 fix variable init order mistake in program startup
reset multiply bit at end of verafx.muls
2024-09-28 19:17:45 +02:00
c78b7b1a24 added verafx.mult16() 2024-09-28 01:00:28 +02:00
9c7a645e18 remove non-functional verafx.mult(). note: muls() is still there and just fine!
added documentation/source code comments to the cpu word*word multiplication routine not producing the correct upper 16 bits.
2024-09-28 00:43:05 +02:00
4acf38031a feedback 2024-09-27 20:59:31 +02:00
4cd7271e30 added prog8 vs other languages chapter to the manual 2024-09-27 20:19:28 +02:00
3f630ab1b0 RAW output now also properly initializes variables 2024-09-27 18:46:03 +02:00
04cb684fd4 tweak program start initialization and fix cleanup at exit for atari and pet compiler targets 2024-09-27 02:14:54 +02:00
4c843571ea fix syntax error check for missing return statement 2024-09-26 01:52:33 +02:00
1326498802 update skeleton scripts 2024-09-26 00:12:29 +02:00
b7ebd8c4a6 update cx16/audioroutines example to use the new audio module 2024-09-26 00:08:25 +02:00
24e0a69480 feat: module with front-ends that jsrfar into audio ROM routines (#151) 2024-09-26 00:00:42 +02:00
4bcb2bdede added benchmark program 2024-09-25 23:32:45 +02:00
d27f3eb8a4 remove wrong print_f mention 2024-09-24 23:58:39 +02:00
d3e4481112 fix asm optimizer bug where it erroneously discarded rts with a label 2024-09-22 21:41:41 +02:00
1d1d6b3d98 tweak c64 balloonflight example etc. 2024-09-22 13:20:12 +02:00
90b8a22a71 correct amount 2024-09-20 22:35:17 +02:00
8dbfb8ab76 move community note to start page 2024-09-20 20:12:33 +02:00
e29ff1c848 fix name redefinition check for multi-declarations 2024-09-20 19:55:32 +02:00
585f6ffc9b version 10.4.1 2024-09-20 18:43:08 +02:00
46b94c17d6 comment 2024-09-20 17:33:00 +02:00
7af8007447 Merge remote-tracking branch 'origin/master' 2024-09-20 17:28:55 +02:00
16a2b2f566 Add 24-bit integer-to-float routine and floating-point jiffy clock reader (#150)
* feat: add routine to convert 24-bit integer from A/X/Y to float in FAC1

* fix: remove duplicate definition

* fix: shift to appropriate exponent
2024-09-20 17:28:37 +02:00
ea2a90c3c5 nah 2024-09-19 23:09:59 +02:00
5cda750e5e improve error message for undefined symbol in when choices 2024-09-18 23:00:03 +02:00
4e143d45c8 fix warning 2024-09-18 22:04:25 +02:00
4c50980d81 new skeletons dump 2024-09-18 18:45:43 +02:00
2954f5f04d Add clear_screen and set_screen_mode to gfx_lores. Fix boolean draw vs color param in some monogfx routines. Elaborate some docs. 2024-09-17 22:19:47 +02:00
cac4c1eb1e improve callgraph unused subroutine check for routines called from inline asm 2024-09-16 21:49:30 +02:00
0b1f30d98c no more span overdraws in graphics disc routines 2024-09-15 15:38:33 +02:00
c7b1e8d772 fixed a variable scopedname issue where it took the fully scoped name instead of just the local name
this made 64tass not strip out that code if it was unused
2024-09-14 23:17:26 +02:00
a4f7512d44 oops, was fixed 2024-09-14 22:33:20 +02:00
0d3ad80659 retain type of consts better to avoid precision loss
this also fixed a difference in const calculation where the result could differ if you were using optimzations or not.
2024-09-14 21:06:21 +02:00
aba1a73e28 actually use any @zp etc tags on subroutine parameters 2024-09-14 17:42:13 +02:00
dca31b2ca3 added gfx_lores module for cx16 for optimized graphics routines for lores 256c screen mode
currently contains a new line() routine
2024-09-14 15:30:39 +02:00
0cb378ca31 added emudbg.cpu_cycles() and emudbg.reset_cpu_cycles() 2024-09-13 23:03:14 +02:00
cf551d2cc7 tweak containment check even more 2024-09-13 00:02:26 +02:00
ac0c8a68f6 IR: Improve codegen for for loops downto 0/1 2024-09-12 23:00:32 +02:00
5986dcdd2f add new containment check codegen for IR 2024-09-12 22:04:20 +02:00
6be6eb2227 tweak diskio to not always include unused subroutine internal_f_tell() in resulting program 2024-09-11 19:51:53 +02:00
d34015eec5 fix gfx2.fill() vera CTRL corruption 2024-09-11 19:10:45 +02:00
255c5bfaca improve containment check for few values 2024-09-11 03:24:30 +02:00
01c6754928 get rid of problematic common-subexpression optimization 2024-09-11 01:10:42 +02:00
8eaf884f69 improve codegen for for loops downto 0,1 when start value is not const 2024-09-10 23:54:44 +02:00
699a2bb7ab improved codegen for for loops downto 0 2024-09-10 21:33:57 +02:00
4a2dcd20d1 fix the "x<2" optimization made a few commits ago to only work on unsigned 2024-09-09 23:06:36 +02:00
4e98fb75d6 support assigning multiple return flags from asmsub in 6502 codegen 2024-09-09 22:56:40 +02:00
64e66e732f cx16/circles example now uses gfx2 2024-09-08 22:52:46 +02:00
7aec627f6b add optimization if x==0 or x==1 -> if x<2 2024-09-08 22:39:48 +02:00
59a2fec176 fix IR containment check 2024-09-08 21:49:13 +02:00
edc5a5a94f improve data driven unit tests to use kotest withData() 2024-09-08 16:55:08 +02:00
c5b7edad82 added memsizer unit tests 2024-09-08 15:24:47 +02:00
124ffac4e4 readme 2024-09-08 11:50:18 +02:00
6d2a36fb2b testcase improvement 2024-09-06 22:51:26 +02:00
28b43b3e1d added cx16.EXTAPI_kbd_leds definition (new in kernal R48) 2024-09-06 20:47:49 +02:00
f7feaf158d added cx16.mouse_present() routine to check for presence of mouse 2024-09-06 18:21:13 +02:00
2396f707c6 fix bug in codegen for certain array lookups using word typed index value (i.e. via a pointer variable) 2024-09-06 18:11:41 +02:00
d4d8e1b1ba comment about implementation in life example 2024-09-06 16:39:44 +02:00
44fec2c729 some additional last minute optimization to life example 2024-09-06 15:56:41 +02:00
a80a6913e3 some additional last minute tweaks to life example 2024-09-06 15:42:49 +02:00
0eac04c220 added cx16/life.p8 example (Conway's game of life) 2024-09-06 15:21:29 +02:00
29dd758302 Fix compiler crash in for loops with just 1 iteration 2024-09-05 21:26:46 +02:00
5c45adc7f0 graphics module on x16 now uses kernal (R48 or newer) support for drawing circles and ovals 2024-09-05 20:27:12 +02:00
ad22cf08cd todo 2024-09-03 17:46:32 +02:00
2c2ae64194 replace java Stack by kotlin ArrayDeque 2024-09-02 00:15:28 +02:00
97c2dadd16 doc update 2024-09-01 20:55:43 +02:00
b36e1e3baf change sprite.hide() : now disables sprite instead of moving it offscreen.
added sprite.show() to re-enable it (with z-order 3, as all sprites have by default in this module)
added sprite.zdepth() to set a custom z-depth.
2024-09-01 20:55:43 +02:00
2da35fec17 remove requirement to end subroutine with an EOL, so oneliners are now possible
main { sub start() { cx16.r0++ cx16.r1++ } }
2024-09-01 20:55:43 +02:00
bdeac74cfc removed the -nostrictbool compiler option
boolean types and bytes are no longer implicitly interchangeable using this option
2024-09-01 20:53:39 +02:00
6516d7cb15 regenerate skeletons and set version 10.4 2024-08-25 16:56:33 +02:00
31cf76042d scope 2024-08-25 15:28:45 +02:00
c4c4dcf2b3 optimizing gfx2.fill() 4 color mode 2024-08-25 15:05:24 +02:00
03145630f8 optimizing gfx2.fill() 2024-08-25 14:26:49 +02:00
e2fcac322f optimizing gfx2.fill() 2024-08-25 13:51:01 +02:00
beaff4d650 moved non X16 specific variables and vector definitions from cx16 to cbm namespace.
This makes the naming consistent with the other cbm-like targets (c64, pet, c128). Only the x16 specific ones remain in the cx16 namespace, such as cx16.KEYHDL

Probably the most impactful is the move of cx16.CINV to cbm.CINV
2024-08-24 20:06:50 +02:00
79af96ddde new kotlin version 2024-08-24 16:34:26 +02:00
e439720c9d optimized string compares 2024-08-24 14:53:18 +02:00
48d0185ea4 increase flood fill stack size a bit 2024-08-23 20:55:30 +02:00
e2592b4e0b fix possible gfx2 color problem in 2bpp mode 2024-08-23 19:48:23 +02:00
2967866e3d avoid self-modifying code to be compatible with IRQ handlers 2024-08-23 17:46:23 +02:00
b566ea5c3f added string.rfind() 2024-08-22 23:22:31 +02:00
8f6eaeac2c half width katakana conversion 2024-08-21 18:51:34 +02:00
b4facaeb3c add "kata" string encoding (Katakana) 2024-08-20 21:40:43 +02:00
d12b7ccc6b fix syslib importing for raw outputs. fixes #144 2024-08-19 13:33:02 +02:00
453e8bd0a0 update kotlin and antlr libs 2024-08-19 12:47:18 +02:00
9204d390ae correct fetch() signature. fixes #148 2024-08-18 20:04:23 +02:00
b70ce0015c fix missing opportunities to use TSB instruction 2024-07-24 22:51:49 +02:00
d113827753 todo 2024-07-24 19:50:30 +02:00
c67f877857 Codegen: use BIT instruction for memory location bit 7 and 6 tests (use N and V flags) 2024-07-24 19:26:54 +02:00
0ec719e429 cx16: added a polling pcm streaming example 2024-07-23 21:37:11 +02:00
17f7b11148 tweaks cx16 sample streaming example, also added a new one 2024-07-23 02:10:05 +02:00
966b017670 tweaks 2024-07-22 18:20:01 +02:00
4c98070b3c optimize shifts by 1 by inlining it better 2024-07-21 22:08:41 +02:00
3681d6ee1c optimize division by powers of 2 better (into bit shifts) 2024-07-21 21:34:38 +02:00
0af17cdc33 todo's for division optimizations 2024-07-21 20:32:03 +02:00
2aae1f5e30 stricter checks for negative array indexing 2024-07-20 22:37:03 +02:00
d18f2a7bfd improved codegen for some pointer+index expressions 2024-07-18 23:41:34 +02:00
9046fe8d3a ringbuffer and pointer optimization todo 2024-07-16 22:59:31 +02:00
78c7ee247a generate 65c02 TSB/TRB instructions in certain cases 2024-07-16 00:36:00 +02:00
d5adb85e5b IR: add SEC,CLC,SEI,CLI instructions for the sys function calls. 2024-07-14 21:01:19 +02:00
69f953fd9b diskio.f_readline() now also returns I/O status as secondary return value in A 2024-07-06 22:25:01 +02:00
484677b4b1 Get rid of any() and all() builtin functions.
Replaced by regular subroutines in the anyall module.
2024-07-06 18:49:03 +02:00
b10a8e728f update vim syntax too 2024-07-06 17:13:42 +02:00
25f25a8767 Get rid of sort() and reverse() builtin functions.
Sort() had too many gotchas and reverse() is kinda redundant you can loop in decreasing order through an array too.
2024-07-06 17:07:58 +02:00
0c053e4a2c IR: don't confuse symbol names starting with 'r', with register names
Added start of buffer.p8 (experimental)
2024-07-04 01:39:25 +02:00
3f6521cc9b todo 2024-07-03 20:33:59 +02:00
a074491d5b fix doc build 2024-07-03 00:08:49 +02:00
a291164953 fix leaving Vera CTRL at 1 instead of 0, could lead to kernal text output errors etc
Fixes #143
2024-07-02 23:36:36 +02:00
43c55b58d2 fix register overwriting for certain subroutine call parameter combinations. Fixes #136 2024-07-02 23:26:34 +02:00
e7298f8162 fix invalid code gen for if v1==0 or v2==0 2024-07-01 23:38:25 +02:00
ddf990296b fix subroutine inlining symbol scope error 2024-06-29 18:53:54 +02:00
ead8aa7800 asm optimization: bxx+jmp -> opposite bxx 2024-06-29 17:22:57 +02:00
7a9dd1ac9b optimize trivial 65c02 stack instructions 2024-06-29 15:42:40 +02:00
1c97c22eff optimize simple word and byte addition/subtraction better 2024-06-29 14:33:42 +02:00
bbf621a8c4 doc 2024-06-29 13:39:08 +02:00
8efa89165c sprites.get_data_ptr() signature changed: now properly returns the 2 values 2024-06-27 22:22:26 +02:00
4f8aaf9244 some tweaks and todos 2024-06-26 21:22:40 +02:00
a97edef380 update gradle wrapper to gradle 8.8 2024-06-25 22:43:04 +02:00
eefae24aa3 update gradle wrapper to gradle 8.8 2024-06-25 22:42:16 +02:00
54bffc91ae properly generate PUSH and POP instructions for push() and pop() calls in IR.
Also switch to a fork of shadowJar to avoid Gradle deprecation errors.
2024-06-25 22:39:20 +02:00
63f5ef9e14 fix typo for bool array storage size 2024-06-20 22:55:47 +02:00
034f27a8dd added queens example, update kotest lib 2024-06-19 23:57:43 +02:00
c2f6311367 todo 2024-06-17 22:41:52 +02:00
6f00a48772 fix: atan2(anything, 0) should return ±π/2 (#141)
* fix: atan2(anything, 0) should return pi/2

* fix: if y<0, x=0 maps to 3π/2, not π/2

* fix: standard seems to be atan2(0,0) == 0
2024-06-07 23:19:45 +02:00
b3dba67405 added cx16.rom_version() routine 2024-06-07 23:15:26 +02:00
c9a4235669 update to kotlin 2.0, fix several code style issues 2024-06-04 01:00:46 +02:00
ae0d52274c Merge branch 'refs/heads/fixwindowseolstests' 2024-06-04 00:13:55 +02:00
8973763866 Fix line endings conversion errors on windows builds 2024-06-04 00:12:12 +02:00
3d799ae7fe todo 2024-06-01 15:03:01 +02:00
8b10115390 release 10.3.1 2024-05-31 23:51:35 +02:00
d2e010c439 added cx16.scnsiz (extapi call), describe profiler.py script 2024-05-31 21:48:29 +02:00
15867ab423 update cx16.mouse_get() and mouse_pos() to also return scroll wheel in X 2024-05-29 23:19:53 +02:00
22c9e99fa3 explain integer math sin/cos routines even better 2024-05-29 23:12:00 +02:00
ee262f6aad explain integer math sin/cos routines even better 2024-05-29 20:26:42 +02:00
af64af2397 explain integer math sin/cos routines better 2024-05-29 19:48:27 +02:00
1feead2260 tweaks 2024-05-29 02:30:06 +02:00
d3dcd24b4d add profiler script 2024-05-29 00:56:31 +02:00
fd1e6796ef correct branch instruction, fixes #137 2024-05-24 20:54:40 +02:00
3ea0f0cbaa remove 16 bit f_tell variant. 2024-05-22 21:47:02 +02:00
f3e3311598 added diskio.f_tell() and f_tell32() on the cx16 target 2024-05-21 23:14:25 +02:00
0dc50a93a4 added @nozp variable flag 2024-05-21 21:53:58 +02:00
fda8e61be4 give better error when using @split wrong 2024-05-20 21:51:07 +02:00
ac1d4b4a7a mouse_pos() now returns the coordinates as unsigned words 2024-05-20 21:38:02 +02:00
c719e274d5 java version tweaks 2024-05-18 20:25:44 +02:00
e4990f8ec5 Revert "update to Java 17 LTS"
This reverts commit 3ef5bdfeda.
2024-05-18 18:59:32 +02:00
62afd3342e void syntax check, fixes #135 2024-05-18 17:15:31 +02:00
6e8a89e6f1 optimize const word repeat setup 2024-05-18 16:30:27 +02:00
aa2437cfb8 fix invalid repeat loop when iterations is already in register Y 2024-05-18 15:09:56 +02:00
4a710ecdfc cleanups 2024-05-17 18:48:04 +02:00
3ef5bdfeda update to Java 17 LTS 2024-05-17 18:27:21 +02:00
7915dda35f update libraries 2024-05-12 03:02:54 +02:00
9120e16683 todo 2024-05-02 21:02:50 +02:00
a1ebc7090d fix sieve example 2024-04-18 22:22:29 +02:00
054b4636e0 version 10.3 2024-04-18 21:50:48 +02:00
e3e7b060b7 vumeter tweaks 2024-04-18 01:31:59 +02:00
5ac9c75521 docs of new floats routines and added them to VM target too 2024-04-17 20:03:36 +02:00
07710e0995 Feature/reciprocal tangent functions (#133)
* feat: additional trig functions

* fix: 64tass won't assemble a proc named 'sec'

* fix: indentation
2024-04-17 19:54:47 +02:00
d6a67f5f2b vumeter colors 2024-04-17 00:22:19 +02:00
2675623aea fix optimization ast parent linkage problem 2024-04-16 23:27:22 +02:00
94263c43d0 added cx16/vumeter example 2024-04-16 22:48:36 +02:00
d8ec03874f move the pi-related constants from system specific floats module into the shared one. Clarify some stuff. 2024-04-15 19:15:44 +02:00
a7247f5b8b fix boolean expression optimization bug 2024-04-12 21:56:25 +02:00
4d37581694 fix the symbol lookup error lsb(a) when a is in a multi vardecl. 2024-04-11 00:51:08 +02:00
5d7ddebcad fix bool to uword cast in 6502 codegen 2024-04-11 00:34:53 +02:00
53df0eb707 cleanups 2024-04-10 22:04:03 +02:00
8babad9c7c sphinx config 2024-04-10 20:04:09 +02:00
8db7aa07bd added (autogenerated) symbol skeleton files to the docs 2024-04-10 19:58:15 +02:00
42f4b06ac8 added options -bytes2float and -float2bytes to be able to do float conversions from the command line 2024-04-09 23:59:54 +02:00
f4b50368ba fix grammar: if_xx with else part 2024-04-09 22:35:30 +02:00
db80417bd7 fix a problem with const fold optimization in if expressions, and IR compilation of that 2024-04-09 22:09:29 +02:00
7a6f2ecc8c add symboldumps to doc makefile 2024-04-09 19:53:36 +02:00
f5d556a7f9 added missing options to doc 2024-04-09 19:30:04 +02:00
2aae46d632 added -dumpsymbols option to print a dump of all the variables and subroutine signatures 2024-04-09 19:19:13 +02:00
19ebc6d6b3 better error message for ambiguous multi-var initialization in vardecl 2024-04-08 22:36:00 +02:00
f88c29e083 convert github doc links into permalinks 2024-04-08 22:12:28 +02:00
6ed9899dc7 smarter desugaring of ubyte x,y 2024-04-07 23:36:46 +02:00
9de7698a5c verafx.mult() and muls() now return both words of the 32 bits result. 2024-04-07 22:41:21 +02:00
112d2d6058 cx16 sprites module: the palette_offset parameter now takes values 0-15 (instead of 0-255) to be more consistent with docs and vera behavior 2024-04-07 21:49:03 +02:00
ddb8346711 added txt.cls() as a shorter alternative to clear_screen().
cx16: added new character encodings, and routines in textio to enable the character sets for them.
cx16: added txt.chrout_lit() and txt.print_lit() to always print the literal characters and never as control codes
2024-04-07 19:32:44 +02:00
8dd3faf395 clarification 2024-04-06 14:31:39 +02:00
35f3e8708b doc and tweak subexpression extraction a tiny bit 2024-04-06 14:01:06 +02:00
cfe3fcc9e7 fix symbol table issue 2024-04-06 12:53:33 +02:00
66a6659a6e cbm.STOP2() and cbm.GETIN2() convenience routines 2024-04-06 02:16:21 +02:00
88ae3daa42 Merge branch 'refs/heads/master' into multi-assign
# Conflicts:
#	examples/test.p8
2024-04-06 00:14:41 +02:00
08b8fe01ab added missing cmp #0 after func()==0
cx16: diskio.fastmode() now returns success boolean
2024-04-06 00:04:54 +02:00
731132d4b3 check number of result values in return statements 2024-04-05 02:13:31 +02:00
98acff802f better checking for number of return values
assignment optimization if return register already is the same as the assignment target
2024-04-04 23:47:33 +02:00
5f11f485a2 fix compiler error 2024-04-04 02:00:55 +02:00
34f3169dda tweak library routines for multiple return values.
cbm:
MEMTOP changed (now also returns nr of banks in A)
STOP2 removed (just use STOP)
RDTIM_safe() added                  TEST IRQ ENABLE
RDTIM16 changed (internally)        TEST IRQ ENABLE

cx16:
screen_mode changed (now also returns width and height in X,Y)
kbdbuf_peek2 removed (just use kbdbuf_peek)
joystick_get changed (presence now returned as bool in Y)
joystick_get2 removed (just use joystick_get)
mouse_pos changed (now properly returns x and y position in R0 and R1)
set_led_brightness changed into set_led_state, with only a boolean on/off argument. There is no variable brightness.

sys.set_leds_brightness() removed. Use cx16.set_led_brightness().
2024-04-04 01:39:19 +02:00
a3ef8f814b Merge branch 'master' into multi-assign
# Conflicts:
#	examples/test.p8
2024-04-03 01:13:27 +02:00
385dd6fc23 todos 2024-04-03 01:12:45 +02:00
9af4168ae2 cx16: added diskio.fastmode() to select the fast serial disk mode for the SD card 2024-04-02 22:17:51 +02:00
a5e0e31b74 clarify order of multi-assign 2024-04-02 01:47:46 +02:00
b385dc8c26 add cx16 extapi ROM call, call numbers and shims. (new in Rom R47) 2024-04-02 01:45:10 +02:00
92c012b55a fix IR peephole optimization 2024-04-02 00:28:28 +02:00
641f6c05d8 allow 'void' as dummy assign target in multi-assignment statements 2024-03-31 23:43:26 +02:00
788f6b44a6 antlr grammar now understands underscores in identifier names 2024-03-31 00:31:10 +01:00
63a4525f06 remove hacks from floats.parse now that kernal R47 is out 2024-03-30 22:29:13 +01:00
3e34a3ef72 allow multi-assign to skip any status register result 2024-03-29 23:10:08 +01:00
0c5e8ca199 Merge branch 'master' into multi-assign 2024-03-29 11:51:42 +01:00
ff23fb0086 take ignore_unused option into account for warnings about removing unused blocks themselves as well 2024-03-29 00:16:18 +01:00
56f41d5e34 docs about multi-assign 2024-03-28 23:24:14 +01:00
4700a239b9 Merge branch 'master' into multi-assign
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2024-03-28 01:06:43 +01:00
bd5abfb969 add IR peephole optimization to remove redundant store 2024-03-28 01:06:05 +01:00
b93fa75377 consolidate cbm textio routines 2024-03-28 00:39:58 +01:00
681ce9c60c fix void warning 2024-03-27 23:05:41 +01:00
dd0f0fe415 conv.str_ub and partners are now much shorter routines than before 2024-03-27 22:34:44 +01:00
119040fc50 also add diskio.status_code() in other comp targets 2024-03-27 20:05:39 +01:00
551e5688da Add diskio.status_code() function (#130) 2024-03-27 19:42:47 +01:00
56c1035581 Merge branch 'master' into multi-assign
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2024-03-26 22:09:16 +01:00
ba1e907c79 fix divmod; out args are written to and should be potential constants 2024-03-26 22:04:44 +01:00
2a3a27c56d bmx library: set bpp header field correctly on save 2024-03-26 22:01:10 +01:00
647af34f5b fix: tweak divmod() doc (#131)
* fix: adjust naming on divmod parameters to match standard mathematical terminology; clarify description

* fix: wording

* fix: wording
2024-03-26 22:00:55 +01:00
993be6394e unit tests multi-assigns 2024-03-25 23:20:03 +01:00
9a27505315 6502 codegen for multi-assigns 2024-03-25 22:17:31 +01:00
2e37f5dee3 IR: support for multi-returnvalue function calls (asmsubs)
note: the VM can't execute these though as it has no CPU hardware registers
2024-03-23 00:30:17 +01:00
03e486c082 multi assign 2024-03-22 21:51:25 +01:00
edc83305a4 allow multiple targets in AssignTarget 2024-03-22 21:51:08 +01:00
66e7c51064 IR: fix some things related to asmsubs 2024-03-22 21:49:01 +01:00
60244aaf16 64tass version... 2024-03-21 21:40:18 +01:00
443391c700 another way to hash? 2024-03-21 21:30:34 +01:00
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
435 changed files with 40692 additions and 17561 deletions

View File

@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: build and install recent 64tass
run: |
@ -18,19 +18,24 @@ jobs:
git clone --depth=1 https://github.com/irmen/64tass
cd 64tass
make -j4
sudo make install
sudo make install
- name: Set up JDK 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 11
distribution: adopt
distribution: temurin
- name: Build and test with Gradle
run: ./gradlew build shadowJar --no-daemon
run: |
./gradlew build shadowJar --no-daemon
sha256sum -b compiler/build/libs/*-all.jar > compiler/build/libs/hash.txt
- name: Create compiler shadowJar artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: prog8-compiler-jar-zipped
path: compiler/build/libs/*-all.jar
path: |
compiler/build/libs/*-all.jar
compiler/build/libs/hash.txt

8
.gitignore vendored
View File

@ -1,8 +1,12 @@
.idea/workspace.xml
.idea/discord.xml
.idea/developer-tools.xml
.idea/usage.statistics.xml
.idea/shelf/
build/
dist/
output/
out/
.*cache/
*.directory
*.prg
@ -11,7 +15,6 @@ output/
*.vm.txt
*.vice-mon-list
docs/build
out/
parser/**/*.interp
parser/**/*.tokens
parser/**/*.java
@ -22,6 +25,7 @@ compiler/src/prog8/buildversion/*
.eggs/
/MANIFEST
.tox/
.kotlin/
__pycache__/
parser.out
parsetab.py
@ -30,8 +34,6 @@ parsetab.py
compiler/lib/
.gradle
**/BuildVersion.kt
/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" />

4
.idea/kotlinc.xml generated
View File

@ -4,6 +4,6 @@
<option name="jvmTarget" value="11" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.20" />
<option name="version" value="2.0.21" />
</component>
</project>
</project>

View File

@ -1,23 +1,23 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22" />
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.21/kotlin-stdlib-jdk8-2.0.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.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.9.22/kotlin-stdlib-jdk7-1.9.22.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.21/kotlin-stdlib-jdk7-2.0.21.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.21/kotlin-stdlib-jdk8-2.0.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21-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!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.21/kotlin-stdlib-jdk7-2.0.21-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.22/kotlin-stdlib-jdk8-1.9.22-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.21/kotlin-stdlib-jdk8-2.0.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21-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!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.21/kotlin-stdlib-jdk7-2.0.21-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -1,13 +1,13 @@
<component name="libraryTable">
<library name="antlr.antlr4" type="repository">
<properties maven-id="org.antlr:antlr4:4.13.1">
<properties maven-id="org.antlr:antlr4:4.13.2">
<exclude>
<dependency maven-id="com.ibm.icu:icu4j" />
</exclude>
</properties>
<CLASSES>
<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/antlr4/4.13.2/antlr4-4.13.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.2/antlr4-runtime-4.13.2.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.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!/" />

13
.idea/libraries/eclipse_lsp4j.xml generated Normal file
View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="eclipse.lsp4j" type="repository">
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.23.1/org.eclipse.lsp4j-0.23.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.23.1/org.eclipse.lsp4j.jsonrpc-0.23.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.27.0/error_prone_annotations-2.27.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

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.2" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.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.3/jnr-constants-0.10.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.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,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,18 @@
<component name="libraryTable">
<library name="io.kotest.assertions.core.jvm" type="repository">
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.8.0" />
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
<CLASSES>
<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/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.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!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -0,0 +1,23 @@
<component name="libraryTable">
<library name="io.kotest.framework.datatest" type="repository">
<properties maven-id="io.kotest:kotest-framework-datatest:5.9.1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest/5.9.1/kotest-framework-datatest-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest-jvm/5.9.1/kotest-framework-datatest-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.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/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -1,30 +1,30 @@
<component name="libraryTable">
<library name="io.kotest.runner.junit5.jvm" type="repository">
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.8.0" />
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.9.1" />
<CLASSES>
<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/kotest/kotest-runner-junit5-jvm/5.9.1/kotest-runner-junit5-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.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$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.9.1/kotest-framework-engine-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.172/classgraph-4.8.172.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.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$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.8.0/kotlinx-coroutines-debug-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/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$/io/kotest/kotest-framework-discovery-jvm/5.9.1/kotest-framework-discovery-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.9.1/kotest-extensions-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.9.1/kotest-framework-concurrency-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.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!/" />
@ -32,11 +32,8 @@
<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!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

@ -1,15 +1,20 @@
<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.18" />
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.18/kotlin-result-jvm-1.1.18.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$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0.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.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!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-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!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-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!/" />
</SOURCES>
</library>
</component>

View File

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

View File

@ -1,18 +0,0 @@
<component name="libraryTable">
<library name="takes" type="repository">
<properties maven-id="org.takes:takes:1.24.4" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.24.4/takes-1.24.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.54.0/cactoos-0.54.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.4.0-b180830.0359/jaxb-api-2.4.0-b180830.0359.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/4.0.0/jaxb-core-4.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/jakarta/xml/bind/jakarta.xml.bind-api/4.0.0/jakarta.xml.bind-api-4.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/jakarta/activation/jakarta.activation-api/2.1.0/jakarta.activation-api-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/angus/angus-activation/1.0.0/angus-activation-1.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/4.0.0/jaxb-impl-4.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

2
.idea/misc.xml generated
View File

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

5
.idea/modules.xml generated
View File

@ -2,6 +2,8 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/beanshell/beanshell.iml" filepath="$PROJECT_DIR$/beanshell/beanshell.iml" />
<module fileurl="file://$PROJECT_DIR$/benchmark-program/benchmark-program.iml" filepath="$PROJECT_DIR$/benchmark-program/benchmark-program.iml" />
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
@ -10,12 +12,11 @@
<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$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
</modules>
</component>

View File

@ -9,17 +9,19 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
# 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

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/prog8c"
test:
gradle build
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"

View File

@ -29,6 +29,10 @@ How to get it/build it
- 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).
Note that if you are not using *gradle* to build it, you might have to perform some manual
tasks once to make it compile fully. These are explained in the linked instructions.
- 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
---------
@ -49,26 +53,27 @@ 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.
- modularity, symbol scoping, subroutines
- all advantages of a higher level language over having to write assembly code manually
- programs run very fast because it's compiled to native machine code
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- modularity, symbol scoping, subroutines. No need for forward declarations.
- 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)
- floating point math is supported on certain targets
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
- automatic static variable allocations, automatic string and array variables and string sharing
- subroutines with input parameters and result values
- high-level program optimizations
- small program boilerplate/compilersupport overhead
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
- conditional branches
- conditional branches that map 1:1 to cpu status flags
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
- ``in`` expression for concise and efficient multi-value/containment check
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
- ``defer`` statement to help write concise and robust subroutine cleanup logic
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
- various powerful built-in libraries to do I/O, number conversions, graphics and more
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
- encode strings and characters into petscii or screencodes or even other encodings
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
*Rapid edit-compile-run-debug cycle:*
@ -116,14 +121,13 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
%import textio
%zeropage basicsafe
main {
ubyte[256] sieve
bool[256] sieve
ubyte candidate_prime = 2 ; is increased in the loop
sub start() {
sys.memset(sieve, 256, false) ; clear the sieve
sys.memset(sieve, 256, 0) ; clear the sieve
txt.print("prime numbers up to 255:\n\n")
ubyte amount=0
repeat {
@ -159,9 +163,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
}
}
when compiled an ran on a C-64 you'll get:
![c64 screen](docs/source/_static/primes_example.png)

View File

@ -10,8 +10,15 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
<orderEntry type="library" name="github.hypfvieh.dbus.java" level="project" />
<orderEntry type="library" name="slf4j.simple" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/lib/bsh-3.0.0-SNAPSHOT.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>

View File

@ -0,0 +1,91 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
id("application")
}
val serverMainClassName = "prog8lsp.MainKt"
val applicationName = "prog8-beanshell"
val javaVersion: String by project
application {
mainClass.set(serverMainClassName)
description = "Code completions, diagnostics and more for Prog8"
// applicationDefaultJvmArgs = listOf("-DkotlinLanguageServer.version=$version")
applicationDistribution.into("bin") {
filePermissions {
user {
read=true
execute=true
write=true
}
other.execute = true
group.execute = true
}
}
}
repositories {
mavenCentral()
}
dependencies {
implementation(files("lib/bsh-3.0.0-SNAPSHOT.jar"))
}
configurations.forEach { config ->
config.resolutionStrategy {
preferProjectModules()
}
}
sourceSets.main {
java.srcDir("src")
resources.srcDir("resources")
}
sourceSets.test {
java.srcDir("src")
resources.srcDir("resources")
}
tasks.startScripts {
applicationName = "prog8-beanshell"
}
tasks.register<Exec>("fixFilePermissions") {
// When running on macOS or Linux the start script
// needs executable permissions to run.
onlyIf { !System.getProperty("os.name").lowercase().contains("windows") }
commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell")
}
tasks.withType<Test>() {
testLogging {
events("failed")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
}
tasks.installDist {
finalizedBy("fixFilePermissions")
}
tasks.build {
finalizedBy("installDist")
}
java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions.jvmTarget = JvmTarget.JVM_11
// jvmToolchain {
// languageVersion = JavaLanguageVersion.of(javaVersion.toInt())
// }
}

Binary file not shown.

View File

@ -0,0 +1,48 @@
package prog8beanshell
import java.io.FilterReader
import java.io.Reader
class CommandLineReader(val input: Reader): FilterReader(input) {
private val normal = 0
private val lastCharNL = 1
private val sentSemi = 2
private var state = lastCharNL
override fun read(): Int {
if (state == sentSemi) {
this.state = lastCharNL
return 10
} else {
var b = input.read()
while(b==13) b = input.read()
if (b == 10) {
if (this.state == lastCharNL) {
b = 59
this.state = sentSemi
} else {
this.state = lastCharNL
}
} else {
this.state = normal
}
return b
}
}
override fun read(buff: CharArray, off: Int, len: Int): Int {
val b = read()
if (b == -1) {
return -1
} else {
buff[off] = b.toChar()
return 1
}
}
}

View File

@ -0,0 +1,23 @@
package prog8beanshell
import bsh.FileReader
import bsh.Interpreter
class BeanshellInterpreter {
fun run(symbols: Map<String, Any>) {
val interpreter = Interpreter(CommandLineReader(FileReader(System.`in`)), System.out, System.err, true)
interpreter.setExitOnEOF(false)
symbols.forEach { (name, value) -> interpreter.set(name, value) }
interpreter.run()
}
}
fun main(args: Array<String>) {
val i = BeanshellInterpreter()
i.run(mapOf(
"env" to System.getenv(),
"args" to args
))
}

View File

@ -0,0 +1,12 @@
.PHONY: all clean emu
all: benchmark.prg
clean:
rm -f *.prg *.PRG *.asm *.vice-* *.BIN *.PAL *.zip *.7z
emu: benchmark.prg
x16emu -run -prg $< -warp
benchmark.prg: benchmark.p8 b_3d.p8 b_adpcm.p8 b_circles.p8 b_life.p8 b_mandelbrot.p8 b_maze.p8 b_queens.p8 b_textelite.p8
prog8c $< -target cx16

109
benchmark-program/b_3d.p8 Normal file
View File

@ -0,0 +1,109 @@
%import textio
%import math
rotate3d {
const ubyte WIDTH = 40
const ubyte HEIGHT = 30
sub benchmark(uword max_time) -> uword {
uword anglex
uword angley
uword anglez
uword frames
txt.nl()
cbm.SETTIM(0,0,0)
while cbm.RDTIM16()<max_time {
matrix_math.rotate_vertices(msb(anglex), msb(angley), msb(anglez))
draw_edges() ; doesn't really draw anything in the benchmark, but does do the screen calculations
anglex+=500
angley+=215
anglez+=453
frames++
}
return frames
}
sub draw_edges() {
; plot the points of the 3d cube
; first the points on the back, then the points on the front (painter algorithm)
ubyte @zp i
word @zp rz
word @zp persp
byte @shared sx
byte @shared sy
for i in 0 to len(matrix_math.xcoor)-1 {
rz = matrix_math.rotatedz[i]
if rz >= 10 {
persp = 600 + rz/64
sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2
sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2
;; txt.setcc(sx as ubyte, sy as ubyte, 46, 7)
}
}
for i in 0 to len(matrix_math.xcoor)-1 {
rz = matrix_math.rotatedz[i]
if rz < 10 {
persp = 600 + rz/64
sx = matrix_math.rotatedx[i] / persp as byte + WIDTH/2
sy = matrix_math.rotatedy[i] / persp as byte + HEIGHT/2
;; txt.setcc(sx as ubyte, sy as ubyte, 81, 7)
}
}
txt.chrout('.')
}
}
matrix_math {
; vertices
word[] @split xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] @split ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] @split zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
; storage for rotated coordinates
word[len(xcoor)] @split rotatedx
word[len(ycoor)] @split rotatedy
word[len(zcoor)] @split rotatedz
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
; rotate around origin (0,0,0)
; set up the 3d rotation matrix values
word wcosa = math.cos8(ax)
word wsina = math.sin8(ax)
word wcosb = math.cos8(ay)
word wsinb = math.sin8(ay)
word wcosc = math.cos8(az)
word wsinc = math.sin8(az)
word wcosa_sinb = wcosa*wsinb / 128
word wsina_sinb = wsina*wsinb / 128
word Axx = wcosa*wcosb / 128
word Axy = (wcosa_sinb*wsinc - wsina*wcosc) / 128
word Axz = (wcosa_sinb*wcosc + wsina*wsinc) / 128
word Ayx = wsina*wcosb / 128
word Ayy = (wsina_sinb*wsinc + wcosa*wcosc) / 128
word Ayz = (wsina_sinb*wcosc - wcosa*wsinc) / 128
word Azx = -wsinb
word Azy = wcosb*wsinc / 128
word Azz = wcosb*wcosc / 128
ubyte @zp i
for i in 0 to len(xcoor)-1 {
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]
rotatedz[i] = Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i]
}
}
}

View File

@ -0,0 +1,89 @@
adpcm {
sub decode_benchmark(uword max_time) -> uword {
uword num_blocks
txt.nl()
cbm.SETTIM(0,0,0)
while cbm.RDTIM16()<max_time {
adpcm.init(0,0)
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
repeat 252/2 {
unroll 2 {
ubyte @zp nibble = @(nibbles_ptr)
adpcm.decode_nibble(nibble & 15) ; first word (note: upper nibble needs to be zero!)
adpcm.decode_nibble(nibble>>4) ; second word (note: upper nibble is zero, after the shifts.)
nibbles_ptr++
}
}
num_blocks++
txt.chrout('.')
}
return num_blocks
}
; IMA ADPCM decoder. Supports mono and stereo streams.
ubyte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
uword[] @split t_step = [
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767]
uword @requirezp predict ; decoded 16 bit pcm sample for first channel.
ubyte @requirezp index
uword @requirezp pstep
sub init(uword startPredict, ubyte startIndex) {
; initialize first decoding channel.
predict = startPredict
index = startIndex
pstep = t_step[index]
}
sub decode_nibble(ubyte @zp nibble) {
; Decoder for a single nibble for the first channel. (value of 'nibble' needs to be strictly 0-15 !)
; This is the hotspot of the decoder algorithm!
; Note that the generated assembly from this is pretty efficient,
; rewriting it by hand in asm seems to improve it only ~10%.
cx16.r0s = 0 ; difference
if nibble & %0100 !=0
cx16.r0s += pstep
pstep >>= 1
if nibble & %0010 !=0
cx16.r0s += pstep
pstep >>= 1
if nibble & %0001 !=0
cx16.r0s += pstep
pstep >>= 1
cx16.r0s += pstep
if nibble & %1000 !=0
predict -= cx16.r0
else
predict += cx16.r0
; NOTE: the original C/Python code uses a 32 bits prediction value and clips it to a 16 bit word
; but for speed reasons we only work with 16 bit words here all the time (with possible clipping error)
; if predicted > 32767:
; predicted = 32767
; elif predicted < -32767:
; predicted = - 32767
index += t_index[nibble]
if_neg
index = 0
else if index >= len(t_step)-1
index = len(t_step)-1
pstep = t_step[index]
}
}

View File

@ -0,0 +1,111 @@
%import gfx_lores
%import math
circles {
const ubyte MAX_NUM_CIRCLES = 80
const ubyte GROWTH_RATE = 4
uword[MAX_NUM_CIRCLES] @split circle_x
uword[MAX_NUM_CIRCLES] @split circle_y
ubyte[MAX_NUM_CIRCLES] circle_radius
ubyte color
uword total_num_circles
sub draw(bool use_kernal, uword max_time) -> uword {
if use_kernal
void cx16.set_screen_mode(128)
else
gfx_lores.graphics_mode()
math.rndseed(12345,6789)
cbm.SETTIM(0,0,0)
total_num_circles = 0
color = 16
while cbm.RDTIM16()<max_time {
if use_kernal {
cx16.GRAPH_set_colors(0,0,0)
cx16.GRAPH_clear()
}
else
gfx_lores.clear_screen(0)
total_num_circles += draw_circles(use_kernal, max_time)
}
if use_kernal
void cx16.set_screen_mode(3)
else {
gfx_lores.text_mode()
}
return total_num_circles
}
sub draw_circles(bool use_kernal, uword max_time) -> uword {
uword @zp x
uword @zp y
ubyte @zp radius
ubyte num_circles
while num_circles<MAX_NUM_CIRCLES and cbm.RDTIM16()<max_time {
x = math.rndw() % 320
y = math.rndw() % 240
radius = GROWTH_RATE
if not_colliding() {
while not_edge() and not_colliding() {
radius += GROWTH_RATE
}
radius -= GROWTH_RATE
if radius>0 {
color++
if color==0
color=16
if use_kernal {
cx16.GRAPH_set_colors(color, 255-color, 0)
cx16.GRAPH_draw_oval(x-radius, y-radius, radius*2, radius*2, true)
}
else
gfx_lores.disc(x, y as ubyte, radius, color)
circle_x[num_circles] = x
circle_y[num_circles] = y
circle_radius[num_circles] = radius
num_circles++
}
}
}
return num_circles
sub not_colliding() -> bool {
if num_circles==0
return true
ubyte @zp c
for c in 0 to num_circles-1 {
if distance(c) < (radius as uword) + circle_radius[c]
return false
}
return true
}
sub distance(ubyte cix) -> uword {
word dx = x as word - circle_x[cix]
word dy = y as word - circle_y[cix]
uword sqx = dx*dx as uword
uword sqy = dy*dy as uword
return sqrt(sqx + sqy)
}
sub not_edge() -> bool {
if x as word - radius < 0
return false
if x + radius >= 320
return false
if y as word - radius < 0
return false
if y + radius >= 240
return false
return true
}
}
}

123
benchmark-program/b_life.p8 Normal file
View File

@ -0,0 +1,123 @@
; conway's game of life.
%import math
%import textio
life {
const ubyte WIDTH = 40
const ubyte HEIGHT = 30
const uword STRIDE = $0002+WIDTH
uword world1 = memory("world1", (WIDTH+2)*(HEIGHT+2), 0)
uword world2 = memory("world2", (WIDTH+2)*(HEIGHT+2), 0)
uword @requirezp active_world = world1
sub benchmark(uword max_time) -> uword {
txt.clear_screen()
sys.memset(world1, (WIDTH+2)*(HEIGHT+2), 0)
sys.memset(world2, (WIDTH+2)*(HEIGHT+2), 0)
set_start_gen()
uword gen
cbm.SETTIM(0,0,0)
while cbm.RDTIM16()<max_time {
next_gen()
gen++
}
return gen
}
sub set_start_gen() {
; some way to set a custom start generation:
; str start_gen = " " +
; " " +
; " " +
; " ** " +
; " * * " +
; " * " +
; " * * " +
; " ****** " +
; " " +
; " " +
; " " +
; " " +
; " " +
; " " +
; " " +
; " "
;
; for y in 0 to 15 {
; for x in 0 to 15 {
; if start_gen[y*16 + x]=='*'
; active_world[offset + x] = 1
; }
; offset += STRIDE
; }
; randomize whole world
math.rndseed(12345,9999)
uword offset = STRIDE+1
ubyte x
ubyte y
for y in 0 to HEIGHT-1 {
for x in 0 to WIDTH-1 {
active_world[offset+x] = math.rnd() & 1
}
offset += STRIDE
}
}
sub next_gen() {
const ubyte DXOFFSET = 0
const ubyte DYOFFSET = 0
ubyte[2] cell_chars = [sc:' ', sc:'●']
uword @requirezp new_world = world1
if active_world == world1
new_world = world2
; To avoid re-calculating word index lookups into the new- and active world arrays,
; we calculate the required pointer values upfront.
; Inside the loop we can use ptr+x just fine (results in efficient LDA (ptr),Y instruction because x is a byte type),
; and for each row we simply add the stride to the pointer.
; It's more readable to use active_world[offset] etc, but offset is a word value, and this produces
; inefficient assembly code because we can't use a register indexed mode in this case. Costly inside a loop.
uword @requirezp new_world_ptr = new_world + STRIDE+1-DXOFFSET
uword @requirezp active_world_ptr = active_world + STRIDE+1-DXOFFSET
ubyte x
ubyte y
for y in DYOFFSET to HEIGHT+DYOFFSET-1 {
cx16.vaddr_autoincr(1, $b000 + 256*y, 0, 2) ; allows us to use simple Vera data byte assigns later instead of setchr() calls
for x in DXOFFSET to WIDTH+DXOFFSET-1 {
; count the living neighbors
ubyte cell = @(active_world_ptr + x)
uword @requirezp ptr = active_world_ptr + x - STRIDE - 1
ubyte neighbors = @(ptr) + @(ptr+1) + @(ptr+2) +
@(ptr+STRIDE) + cell + @(ptr+STRIDE+2) +
@(ptr+STRIDE*2) + @(ptr+STRIDE*2+1) + @(ptr+STRIDE*2+2)
; apply game of life rules
if neighbors==3
cell=1
else if neighbors!=4
cell=0
@(new_world_ptr + x) = cell
; draw new cell
; txt.setchr(x,y,cell_chars[cell])
cx16.VERA_DATA0 = cell_chars[cell]
}
active_world_ptr += STRIDE
new_world_ptr += STRIDE
}
active_world = new_world
}
}

View File

@ -0,0 +1,54 @@
%import textio
%import floats
mandelbrot {
const ubyte width = 39
const ubyte height = 29
const ubyte max_iter = 15
sub calc(uword max_time) -> uword {
uword num_pixels
ubyte pixelx
ubyte pixely
txt.home()
cbm.SETTIM(0,0,0)
while cbm.RDTIM16() < max_time {
for pixely in 0 to height-1 {
float yy = (pixely as float)/0.40/height - 1.3
for pixelx in 0 to width-1 {
float xx = (pixelx as float)/0.32/width - 2.2
float xsquared = 0.0
float ysquared = 0.0
float x = 0.0
float y = 0.0
ubyte iter = 0
while iter<max_iter and xsquared+ysquared<4.0 {
y = x*y*2.0 + yy
x = xsquared - ysquared + xx
xsquared = x*x
ysquared = y*y
iter++
}
txt.color2(1, max_iter-iter)
txt.spc()
num_pixels++
if cbm.RDTIM16()>=max_time
goto finished
}
txt.nl()
}
txt.clear_screen()
}
finished:
txt.color2(1, 6)
return num_pixels
}
}

343
benchmark-program/b_maze.p8 Normal file
View File

@ -0,0 +1,343 @@
%import textio
%import math
; Even though prog8 only has support for extremely limited recursion,
; you can write recursive algorithms with a bit of extra work by building your own explicit stack structure.
; This program shows a depth-first maze generation algorithm (1 possible path from start to finish),
; and a depth-first maze solver algorithm, both using a stack to store the path taken.
; Note: this program can be compiled for multiple target systems.
maze {
uword score
sub bench(uword max_time) -> uword {
txt.nl()
score=0
math.rndseed(2345,44332)
cbm.SETTIM(0,0,0)
while cbm.RDTIM16()<max_time {
maze.initialize()
maze.drawStartFinish()
if maze.generate(max_time) {
maze.openpassages()
maze.drawStartFinish()
if maze.solve(max_time) {
maze.drawStartFinish()
} else break
} else break
}
return score
}
const uword screenwidth = 40
const uword screenheight = 30
const ubyte numCellsHoriz = (screenwidth-1) / 2
const ubyte numCellsVert = (screenheight-1) / 2
; maze start and finish cells
const ubyte startCx = 0
const ubyte startCy = 0
const ubyte finishCx = numCellsHoriz-1
const ubyte finishCy = numCellsVert-1
; cell properties
const ubyte STONE = 128
const ubyte WALKED = 64
const ubyte BACKTRACKED = 32
const ubyte UP = 1
const ubyte RIGHT = 2
const ubyte DOWN = 4
const ubyte LEFT = 8
const ubyte WALLCOLOR = 12
const ubyte EMPTYCOLOR = 0
; unfortunately on larger screens (cx16), the number of cells exceeds 256 and doesn't fit in a regular array anymore.
uword cells = memory("cells", numCellsHoriz*numCellsVert, 0)
ubyte[256] cx_stack
ubyte[256] cy_stack
ubyte stackptr
ubyte[4] directionflags = [LEFT,RIGHT,UP,DOWN]
sub generate(uword max_time) -> bool {
ubyte cx = startCx
ubyte cy = startCy
stackptr = 0
@(celladdr(cx,cy)) &= ~STONE
drawCell(cx, cy)
uword cells_to_carve = numCellsHoriz * numCellsVert - 1
while cbm.RDTIM16()<max_time {
carve_restart_after_repath:
ubyte direction = choose_uncarved_direction()
if direction==0 {
;backtrack
stackptr--
if stackptr==255 {
; stack empty.
; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit)
if cells_to_carve!=0 {
if repath()
goto carve_restart_after_repath
}
return true
}
cx = cx_stack[stackptr]
cy = cy_stack[stackptr]
} else {
cx_stack[stackptr] = cx
cy_stack[stackptr] = cy
stackptr++
if stackptr==0 {
; stack overflow, we can't track our path any longer.
; repath if we are not done yet. (this is a workaround for the prog8 256 array lenght limit)
if cells_to_carve!=0 {
if repath()
goto carve_restart_after_repath
}
return true
}
@(celladdr(cx,cy)) |= direction
when direction {
UP -> {
cy--
@(celladdr(cx,cy)) |= DOWN
}
RIGHT -> {
cx++
@(celladdr(cx,cy)) |= LEFT
score++
}
DOWN -> {
cy++
@(celladdr(cx,cy)) |= UP
}
LEFT -> {
cx--
@(celladdr(cx,cy)) |= RIGHT
}
}
@(celladdr(cx,cy)) &= ~STONE
cells_to_carve--
drawCell(cx, cy)
}
}
return false
sub repath() -> bool {
; repath: try to find a new start cell with possible directions.
; we limit our number of searches so that the algorith doesn't get stuck
; for too long on bad rng... just accept a few unused cells in that case.
repeat 255 {
do {
cx = math.rnd() % numCellsHoriz
cy = math.rnd() % numCellsVert
} until @(celladdr(cx, cy)) & STONE ==0
if available_uncarved()!=0
return true
}
return false
}
sub available_uncarved() -> ubyte {
ubyte candidates = 0
if cx>0 and @(celladdr(cx-1, cy)) & STONE !=0
candidates |= LEFT
if cx<numCellsHoriz-1 and @(celladdr(cx+1, cy)) & STONE !=0
candidates |= RIGHT
if cy>0 and @(celladdr(cx, cy-1)) & STONE !=0
candidates |= UP
if cy<numCellsVert-1 and @(celladdr(cx, cy+1)) & STONE !=0
candidates |= DOWN
return candidates
}
sub choose_uncarved_direction() -> ubyte {
ubyte candidates = available_uncarved()
if candidates==0
return 0
repeat {
ubyte choice = candidates & directionflags[math.rnd() & 3]
if choice!=0
return choice
}
}
}
sub openpassages() {
; open just a few extra passages, so that multiple routes are possible in theory.
ubyte numpassages
ubyte cx
ubyte cy
do {
do {
cx = math.rnd() % (numCellsHoriz-2) + 1
cy = math.rnd() % (numCellsVert-2) + 1
} until @(celladdr(cx, cy)) & STONE ==0
ubyte direction = directionflags[math.rnd() & 3]
if @(celladdr(cx, cy)) & direction == 0 {
when direction {
LEFT -> {
if @(celladdr(cx-1,cy)) & STONE == 0 {
@(celladdr(cx,cy)) |= LEFT
drawCell(cx,cy)
numpassages++
}
}
RIGHT -> {
if @(celladdr(cx+1,cy)) & STONE == 0 {
@(celladdr(cx,cy)) |= RIGHT
drawCell(cx,cy)
numpassages++
}
}
UP -> {
if @(celladdr(cx,cy-1)) & STONE == 0 {
@(celladdr(cx,cy)) |= UP
drawCell(cx,cy)
numpassages++
}
}
DOWN -> {
if @(celladdr(cx,cy+1)) & STONE == 0 {
@(celladdr(cx,cy)) |= DOWN
drawCell(cx,cy)
numpassages++
}
}
}
}
} until numpassages==10
}
sub solve(uword max_time) -> bool {
ubyte cx = startCx
ubyte cy = startCy
const uword max_path_length = 1024
; the path through the maze can be longer than 256 so doesn't fit in a regular array.... :(
uword pathstack = memory("pathstack", max_path_length, 0)
uword pathstackptr = 0
@(celladdr(cx,cy)) |= WALKED
; txt.setcc(cx*2+1, cy*2+1, 81, 1)
while cbm.RDTIM16()<max_time {
solve_loop:
if cx==finishCx and cy==finishCy {
;txt.home()
txt.print("found! path length: ")
txt.print_uw(pathstackptr)
txt.nl()
return true
}
ubyte cell = @(celladdr(cx,cy))
if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0 {
@(pathstack + pathstackptr) = UP
;txt.setcc(cx*2+1, cy*2, 81, 3)
cy--
}
else if cell & DOWN !=0 and @(celladdr(cx,cy+1)) & (WALKED|BACKTRACKED) ==0 {
@(pathstack + pathstackptr) = DOWN
;txt.setcc(cx*2+1, cy*2+2, 81, 3)
cy++
}
else if cell & LEFT !=0 and @(celladdr(cx-1,cy)) & (WALKED|BACKTRACKED) ==0 {
@(pathstack + pathstackptr) = LEFT
;txt.setcc(cx*2, cy*2+1, 81, 3)
cx--
}
else if cell & RIGHT !=0 and @(celladdr(cx+1,cy)) & (WALKED|BACKTRACKED) ==0 {
@(pathstack + pathstackptr) = RIGHT
;txt.setcc(cx*2+2, cy*2+1, 81, 3)
cx++
}
else {
; dead end, pop stack
pathstackptr--
if pathstackptr==65535 {
txt.print("no solution?!\n")
return true
}
@(celladdr(cx,cy)) |= BACKTRACKED
;txt.setcc(cx*2+1, cy*2+1, 81, 2)
when @(pathstack + pathstackptr) {
UP -> {
;txt.setcc(cx*2+1, cy*2+2, 81, 9)
cy++
}
DOWN -> {
;txt.setcc(cx*2+1, cy*2, 81, 9)
cy--
}
LEFT -> {
;txt.setcc(cx*2+2, cy*2+1, 81, 9)
cx++
}
RIGHT -> {
;txt.setcc(cx*2, cy*2+1, 81, 9)
cx--
score++
}
}
goto solve_loop
}
pathstackptr++
if pathstackptr==max_path_length {
txt.print("stack overflow, path too long\n")
return true
}
@(celladdr(cx,cy)) |= WALKED
;txt.setcc(cx*2+1, cy*2+1, 81, 1)
}
return false
}
sub celladdr(ubyte cx, ubyte cy) -> uword {
return cells+(numCellsHoriz as uword)*cy+cx
}
sub drawCell(ubyte cx, ubyte cy) {
return
; ubyte x = cx * 2 + 1
; ubyte y = cy * 2 + 1
; ubyte doors = @(celladdr(cx,cy))
; if doors & UP !=0
; txt.setcc(x, y-1, ' ', EMPTYCOLOR)
; if doors & RIGHT !=0
; txt.setcc(x+1, y, ' ', EMPTYCOLOR)
; if doors & DOWN !=0
; txt.setcc(x, y+1, ' ', EMPTYCOLOR)
; if doors & LEFT !=0
; txt.setcc(x-1, y, ' ', EMPTYCOLOR)
; if doors & STONE !=0
; txt.setcc(x, y, 160, WALLCOLOR)
; else
; txt.setcc(x, y, 32, EMPTYCOLOR)
;
; if doors & WALKED !=0
; txt.setcc(x, y, 81, 1)
; if doors & BACKTRACKED !=0
; txt.setcc(x, y, 81, 2)
}
sub initialize() {
sys.memset(cells, numCellsHoriz*numCellsVert, STONE)
; txt.fill_screen(160, WALLCOLOR)
drawStartFinish()
}
sub drawStartFinish() {
; txt.setcc(startCx*2+1,startCy*2+1,sc:'s',5)
; txt.setcc(finishCx*2+1, finishCy*2+1, sc:'f', 13)
}
}

View File

@ -0,0 +1,63 @@
%import textio
; Recursive N-Queens solver.
; The problem is: find all possible ways to place 8 Queen chess pieces on a chess board, so that none of them attacks any other.
; (this program prints all solutions without taking mirroring and flipping the chess board into account)
; Note: this program can be compiled for multiple target systems.
queens {
const ubyte NUMQUEENS=8
ubyte[NUMQUEENS] board
sub could_place(ubyte row, ubyte col) -> bool {
if row==0
return true
ubyte i
for i in 0 to row-1 {
if board[i]==col or board[i]-i==col-row or board[i]+i==col+row
return false
}
return true
}
uword solution_count
uword maximum_duration
sub place_queen(ubyte row) -> bool {
if row == NUMQUEENS {
solution_count++
txt.chrout('.')
return cbm.RDTIM16()<maximum_duration
}
bool continue_running=true
ubyte col
for col in 0 to NUMQUEENS-1 {
if could_place(row, col) {
board[row] = col
; we need to save the local variables row and col.
sys.push(row)
sys.push(col)
continue_running = place_queen(row + 1)
; restore the local variables after the recursive call.
col = sys.pop()
row = sys.pop()
board[row] = 0
if not continue_running
break
}
}
return continue_running
}
sub bench(uword max_time) -> uword {
solution_count = 0
maximum_duration = max_time
txt.nl()
cbm.SETTIM(0,0,0)
while cbm.RDTIM16() < maximum_duration {
void place_queen(0)
}
return solution_count
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
; This benchmark program is meant to check for regressions in the
; Prog8 compiler's code-generator (performance wise).
;
; As the X16 computer is a more or less fixed system, it's not very useful
; to benchmark the computer itself with.
%import textio
%import b_adpcm
%import b_circles
%import b_3d
%import b_life
%import b_mandelbrot
%import b_queens
%import b_textelite
%import b_maze
%zeropage basicsafe
%option no_sysinit
main {
str[20] benchmark_names
uword[20] benchmark_score
sub start() {
ubyte benchmark_number
void cx16.set_screen_mode(3)
txt.color2(1, 6)
txt.clear_screen()
txt.print("\n\n\n prog8 compiler benchmark tests.\n")
sys.wait(60)
benchmark_number = 0
announce_benchmark("maze solver")
benchmark_score[benchmark_number] = maze.bench(300)
benchmark_number++
announce_benchmark("n-queens")
benchmark_score[benchmark_number] = queens.bench(300)
benchmark_number++
announce_benchmark("mandelbrot (floating point)")
benchmark_score[benchmark_number] = mandelbrot.calc(400)
benchmark_number++
announce_benchmark("game of life")
benchmark_score[benchmark_number] = life.benchmark(300)
benchmark_number++
announce_benchmark("3d model rotation")
benchmark_score[benchmark_number] = rotate3d.benchmark(300)
benchmark_number++
announce_benchmark("adpcm audio decoding")
benchmark_score[benchmark_number] = adpcm.decode_benchmark(300)
benchmark_number++
announce_benchmark("circles with gfx_lores")
benchmark_score[benchmark_number] = circles.draw(false, 300)
benchmark_number++
; announce_benchmark("circles with kernal")
; benchmark_score[benchmark_number] = circles.draw(true, 300)
; benchmark_number++
announce_benchmark("text-elite")
benchmark_score[benchmark_number] = textelite.bench(120)
benchmark_number++
benchmark_names[benchmark_number] = 0
benchmark_score[benchmark_number] = 0
void cx16.set_screen_mode(3)
txt.uppercase()
txt.color2(1, 6)
uword final_score
benchmark_number = 0
txt.print("\nscore benchmark\n\n")
do {
txt.spc()
txt.print_uw(benchmark_score[benchmark_number])
txt.column(6)
txt.print(benchmark_names[benchmark_number])
final_score += benchmark_score[benchmark_number]
txt.nl()
benchmark_number++
} until benchmark_names[benchmark_number]==0
txt.print("\n\nfinal score : ")
txt.print_uw(final_score)
txt.nl()
sub announce_benchmark(str name) {
benchmark_names[benchmark_number] = name
void cx16.set_screen_mode(3)
txt.uppercase()
txt.color2(1, 6)
txt.clear_screen()
txt.plot(4, 6)
txt.print(benchmark_names[benchmark_number])
txt.nl()
sys.wait(60)
}
}
}

View File

@ -6,9 +6,8 @@ plugins {
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
@ -26,7 +25,7 @@ compileTestKotlin {
dependencies {
// should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
}
sourceSets {

View File

@ -1,5 +1,6 @@
package prog8.code
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtNode
import prog8.code.ast.PtProgram
import prog8.code.core.*
@ -82,8 +83,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
override fun lookup(scopedName: String) = flat[scopedName]
fun getLength(name: String): Int? {
val node = flat[name]
return when(node) {
return when(val node = flat[name]) {
is StMemVar -> node.length
is StMemorySlab -> node.size.toInt()
is StStaticVariable -> node.length
@ -98,7 +98,7 @@ enum class StNodeType {
// MODULE, // not used with current scoping rules
BLOCK,
SUBROUTINE,
ROMSUB,
EXTSUB,
LABEL,
STATICVAR,
MEMVAR,
@ -179,31 +179,48 @@ open class StNode(val name: String,
class StStaticVariable(name: String,
val dt: DataType,
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
val onetimeInitializationStringValue: StString?,
val onetimeInitializationArrayValue: StArray?,
val initializationStringValue: StString?,
val initializationArrayValue: StArray?,
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
val align: Int,
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null
var initializationNumericValue: Double? = null
private set
fun setOnetimeInitNumeric(number: Double) {
// In certain cases the init value of an existing var should be updated,
// so we can't ask this as a constructor parameter.
// This has to do with the way Prog8 does the (re)initialization of such variables: via code assignment statements.
// Certain codegens might want to put them back into the variable directly.
// For strings and arrays this doesn't occur - these are always already specced at creation time.
initializationNumericValue = number
}
val uninitialized: Boolean
get() = initializationArrayValue==null && initializationStringValue==null && initializationNumericValue==null
init {
if(length!=null) {
require(onetimeInitializationNumericValue == null)
if(onetimeInitializationArrayValue!=null)
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
require(initializationNumericValue == null)
if(initializationArrayValue!=null)
require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length)
}
if(onetimeInitializationNumericValue!=null) {
require(dt in NumericDatatypes)
if(initializationNumericValue!=null) {
require(dt in NumericDatatypes || dt==DataType.BOOL)
}
if(onetimeInitializationArrayValue!=null) {
if(initializationArrayValue!=null) {
require(dt in ArrayDatatypes)
require(length==onetimeInitializationArrayValue.size)
require(length==initializationArrayValue.size)
}
if(onetimeInitializationStringValue!=null) {
if(initializationStringValue!=null) {
require(dt == DataType.STR)
require(length == onetimeInitializationStringValue.first.length+1)
require(length == initializationStringValue.first.length+1)
}
if(align > 0) {
require(dt == DataType.STR || dt in ArrayDatatypes)
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
}
}
}
@ -221,8 +238,9 @@ class StMemVar(name: String,
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" }
requireNotNull(length)
}
}
@ -239,18 +257,24 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
StNode(name, StNodeType.SUBROUTINE, astNode)
class StRomSub(name: String,
val address: UInt?, // null in case of asmsub, specified in case of romsub
val parameters: List<StRomSubParameter>,
val returns: List<StRomSubParameter>,
class StExtSub(name: String,
val address: PtAsmSub.Address?, // null in case of asmsub, specified in case of extsub.
val parameters: List<StExtSubParameter>,
val returns: List<StExtSubParameter>,
astNode: PtNode) :
StNode(name, StNodeType.ROMSUB, astNode)
StNode(name, StNodeType.EXTSUB, astNode)
class StSubroutineParameter(val name: String, val type: DataType)
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOfSymbol: String?)
class StExtSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?) {
init {
if(number!=null) require(addressOfSymbol==null && boolean==null)
if(addressOfSymbol!=null) require(number==null && boolean==null)
if(boolean!=null) require(addressOfSymbol==null && number==null)
}
}
typealias StString = Pair<String, Encoding>
typealias StArray = List<StArrayElement>

View File

@ -3,7 +3,7 @@ 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 {
@ -13,8 +13,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
}
val scopestack = Stack<StNode>()
scopestack.push(st)
val scopestack = ArrayDeque<StNode>()
scopestack.add(st)
program.children.forEach {
addToSt(it, scopestack)
}
@ -35,12 +35,12 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
return st
}
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
private fun addToSt(node: PtNode, scope: ArrayDeque<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)
val parameters = node.parameters.map { StExtSubParameter(it.first, it.second.type) }
val returns = node.returns.map { StExtSubParameter(it.first, it.second) }
StExtSub(node.name, node.address, parameters, returns, node)
}
is PtBlock -> {
StNode(node.name, StNodeType.BLOCK, node)
@ -65,23 +65,26 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
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
initialNumeric = null
numElements = value.value.length + 1 // include the terminating 0-byte
}
is PtArray -> {
initialArray = makeInitialArray(value)
initialString = null
initialNumeric = null
numElements = initialArray.size
require(node.arraySize?.toInt()==numElements)
}
else -> {
require(value is PtNumber)
initialString = null
initialArray = null
val number = value.number
initialNumeric = number
numElements = node.arraySize?.toInt()
}
}
@ -94,7 +97,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
// 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)
val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align.toInt(), node)
if(initialNumeric!=null)
stVar.setOnetimeInitNumeric(initialNumeric)
stVar
}
is PtBuiltinFunctionCall -> {
if(node.name=="memory") {
@ -104,7 +110,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
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))
scope.first().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
}
null
}
@ -112,14 +118,14 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
}
if(stNode!=null) {
scope.peek().add(stNode)
scope.push(stNode)
scope.last().add(stNode)
scope.add(stNode)
}
node.children.forEach {
addToSt(it, scope)
}
if(stNode!=null)
scope.pop()
scope.removeLast()
}
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
@ -128,10 +134,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
is PtAddressOf -> {
if(it.isFromArrayElement)
TODO("address-of array element $it in initial array value")
StArrayElement(null, it.identifier.name)
StArrayElement(null, it.identifier.name, null)
}
is PtIdentifier -> StArrayElement(null, it.name)
is PtNumber -> StArrayElement(it.number, null)
is PtNumber -> StArrayElement(it.number, null, null)
is PtBool -> StArrayElement(null, null, it.value)
else -> throw AssemblyError("invalid array element $it")
}
}

View File

@ -67,6 +67,7 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): 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?
@ -79,18 +80,11 @@ class PtBlock(name: String,
val options: Options,
position: Position
) : PtNamedNode(name, position), IPtStatementContainer {
enum class BlockAlignment {
NONE,
WORD,
PAGE
}
class Options(val address: UInt? = null,
val forceOutput: Boolean = false,
val noSymbolPrefixing: Boolean = false,
val veraFxMuls: Boolean = false,
val ignoreUnused: Boolean = false,
val alignment: BlockAlignment = BlockAlignment.NONE)
val ignoreUnused: Boolean = false)
}
@ -108,6 +102,9 @@ class PtLabel(name: String, position: Position) : PtNamedNode(name, position)
class PtBreakpoint(position: Position): PtNode(position)
class PtAlign(val align: UInt, position: Position): PtNode(position)
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position)

View File

@ -1,7 +1,7 @@
package prog8.code.ast
import prog8.code.core.*
import java.util.*
import java.util.Objects
import kotlin.math.abs
import kotlin.math.truncate
@ -9,8 +9,6 @@ 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) {
@ -44,11 +42,23 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
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 PtContainmentCheck -> {
if(other !is PtContainmentCheck || other.type != type || !(other.needle isSameAs needle))
false
else {
if(haystackHeapVar!=null)
other.haystackHeapVar!=null && other.haystackHeapVar!! isSameAs haystackHeapVar!!
else if(haystackValues!=null)
other.haystackValues!=null && other.haystackValues!! isSameAs haystackValues!!
else
false
}
}
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
is PtIrRegister -> other is PtIrRegister && 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
@ -71,11 +81,11 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
}
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt()
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
fun isSimple(): Boolean {
return when(this) {
is PtAddressOf -> true
is PtAddressOf -> this.arrayIndexExpr==null || this.arrayIndexExpr?.isSimple()==true
is PtArray -> true
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
is PtBinaryExpression -> false
@ -88,13 +98,15 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtContainmentCheck -> false
is PtFunctionCall -> false
is PtIdentifier -> true
is PtMachineRegister -> true
is PtIrRegister -> 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()
is PtIfExpression -> condition.isSimple() && truevalue.isSimple() && falsevalue.isSimple()
}
}
@ -116,6 +128,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
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))
@ -138,23 +151,20 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
val variable: PtIdentifier
get() {
require((children[0] as? PtIdentifier)?.type in ArrayDatatypes+DataType.STR) // TODO remove
return children[0] as PtIdentifier
}
get() = children[0] as PtIdentifier
val index: PtExpression
get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
init {
require(elementType in NumericDatatypes)
require(elementType in NumericDatatypesWithBoolean)
}
}
class PtArray(type: DataType, position: Position): PtExpression(type, position) {
// children are always one of 3 types: PtBool, PtNumber or PtAddressOf.
override fun hashCode(): Int = Objects.hash(children, type)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtArray)
@ -183,19 +193,42 @@ class PtBuiltinFunctionCall(val name: String,
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
val left: PtExpression
get() = children[0] as PtExpression
val right: PtExpression
get() = children[1] as PtExpression
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) {
val element: PtExpression
class PtIfExpression(type: DataType, position: Position): PtExpression(type, position) {
val condition: PtExpression
get() = children[0] as PtExpression
val iterable: PtIdentifier
get() = children[1] as PtIdentifier
val truevalue: PtExpression
get() = children[1] as PtExpression
val falsevalue: PtExpression
get() = children[2] as PtExpression
}
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
val needle: PtExpression
get() = children[0] as PtExpression
val haystackHeapVar: PtIdentifier?
get() = children[1] as? PtIdentifier
val haystackValues: PtArray?
get() = children[1] as? PtArray
companion object {
val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
}
}
@ -203,13 +236,15 @@ class PtFunctionCall(val name: String,
val void: Boolean,
type: DataType,
position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
}
val args: List<PtExpression>
get() = children.map { it as PtExpression }
init {
if(void) require(type==DataType.UNDEFINED) {
"void fcall should have undefined datatype"
}
// note: non-void calls can have UNDEFINED type: is if they return more than 1 value
}
}
@ -228,6 +263,21 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
}
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, 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) {
companion object {
@ -237,7 +287,7 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
throw IllegalArgumentException("use PtBool instead")
if(type!=DataType.FLOAT) {
val trunc = truncate(number)
if (trunc != number)
@ -248,12 +298,12 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
override fun hashCode(): Int = Objects.hash(type, number)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtNumber)
return false
return if(other==null || other !is PtNumber)
false
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
return number==other.number
number==other.number
else
return type==other.type && number==other.number
type==other.type && number==other.number
}
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
@ -267,8 +317,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
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" }
}
}
@ -284,6 +333,7 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
fun toConstantIntegerRange(): IntProgression? {
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
return when {
fromVal == toVal -> fromVal .. toVal
fromVal <= toVal -> when {
stepVal <= 0 -> IntRange.EMPTY
stepVal == 1 -> fromVal..toVal
@ -327,8 +377,8 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
// 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)
class PtIrRegister(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

@ -10,13 +10,29 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
fun txt(node: PtNode): String {
return when(node) {
is PtAssignTarget -> "<target>"
is PtAlign -> "%align ${node.align}"
is PtAssignTarget -> if(node.void) "<void>" else "<target>"
is PtAssignment -> "<assign>"
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
is PtBreakpoint -> "%breakpoint"
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> "&"
is PtArray -> "array len=${node.children.size} ${type(node.type)}"
is PtAddressOf -> {
if(node.isFromArrayElement)
"& array-element"
else
"&"
}
is PtArray -> {
val valuelist = node.children.map {
when (it) {
is PtBool -> it.toString()
is PtNumber -> it.number.toString()
is PtIdentifier -> it.name
else -> "?"
}
}.joinToString(", ")
"array len=${node.children.size} ${type(node.type)} [ $valuelist ]"
}
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
is PtBuiltinFunctionCall -> {
@ -29,12 +45,13 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
str + node.name + "()"
}
is PtIdentifier -> "${node.name} ${type(node.type)}"
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
is PtIrRegister -> "IRREG#${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()}\""
@ -57,34 +74,40 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"???"
}
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 params = node.parameters.joinToString(", ") {
val register = it.first.registerOrPair
val statusflag = it.first.statusflag
"${it.second.type} ${it.second.name} @${register ?: statusflag}"
}
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(", ")
"-> ${node.returns.joinToString(", ") {
val register = it.first.registerOrPair
val statusflag = it.first.statusflag
"${it.second} @${register ?: statusflag}"
}
}"
}
val str = if (node.inline) "inline " else ""
if(node.address==null) {
if(node.address == null) {
str + "asmsub ${node.name}($params) $clobbers $returns"
} else {
str + "romsub ${node.address.toHex()} = ${node.name}($params) $clobbers $returns"
val bank = if(node.address.constbank!=null) "@bank ${node.address.constbank}"
else if(node.address.varbank!=null) "@bank ${node.address.varbank?.name}"
else ""
str + "extsub $bank ${node.address.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"
"\nblock '${node.name}' $addr"
}
is PtConstant -> {
val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString()
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}:"
@ -98,7 +121,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
}
}
is PtSub -> {
val params = node.parameters.map { "${it.type} ${it.name}" }.joinToString(", ")
val params = node.parameters.joinToString(", ") { "${it.type} ${it.name}" }
var str = "sub ${node.name}($params) "
if(node.returntype!=null)
str += "-> ${node.returntype.name.lowercase()}"
@ -106,13 +129,20 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
}
is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val align = when(node.align) {
0u -> ""
2u -> "@alignword"
64u -> "@align64"
256u -> "@alignpage"
else -> throw IllegalArgumentException("invalid alignment size")
}
val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
"${eltType.name.lowercase()}[${node.arraySize}] $split $align ${node.name}"
}
else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] $split ${node.name}"
"${eltType.name.lowercase()}[] $split $align ${node.name}"
}
else
"${node.type.name.lowercase()} ${node.name}"
@ -121,9 +151,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else
str
}
is PtNodeGroup -> "<group>"
is PtNodeGroup -> if(node.children.isNotEmpty()) "<group>" else ""
is PtNop -> "nop"
is PtPostIncrDecr -> "<post> ${node.operator}"
is PtProgram -> "PROGRAM ${node.name}"
is PtRepeatLoop -> "repeat"
is PtReturn -> "return"
@ -135,6 +164,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else
"->"
}
is PtDefer -> "<defer>"
is PtIfExpression -> "<ifexpr>"
}
}

View File

@ -5,17 +5,21 @@ import prog8.code.core.*
sealed interface IPtSubroutine {
val name: String
val scopedName: String
}
class PtAsmSub(
name: String,
val address: UInt?,
val address: Address?,
val clobbers: Set<CpuRegister>,
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
val inline: Boolean,
position: Position
) : PtNamedNode(name, position), IPtSubroutine
) : PtNamedNode(name, position), IPtSubroutine {
class Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt)
}
class PtSub(
@ -26,10 +30,10 @@ class PtSub(
) : 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 }
}
}
@ -41,9 +45,18 @@ class PtSubroutineParameter(name: String, val type: DataType, position: Position
sealed interface IPtAssignment {
val children: MutableList<PtNode>
val target: PtAssignTarget
get() = children[0] as PtAssignTarget
get() {
if(children.size==2)
return children[0] as PtAssignTarget
else if(children.size<2)
throw AssemblyError("incomplete node")
else
throw AssemblyError("no singular target")
}
val value: PtExpression
get() = children[1] as PtExpression
get() = children.last() as PtExpression
val multiTarget: Boolean
get() = children.size>2
}
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
@ -51,7 +64,7 @@ class PtAssignment(position: Position) : PtNode(position), IPtAssignment
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
class PtAssignTarget(position: Position) : PtNode(position) {
class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) {
val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier
val array: PtArrayIndexer?
@ -69,7 +82,7 @@ class PtAssignTarget(position: Position) : PtNode(position) {
}
}
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this)
infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this)
}
@ -98,6 +111,8 @@ class PtIfElse(position: Position) : PtNode(position) {
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup
get() = children[2] as PtNodeGroup
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
}
@ -110,12 +125,6 @@ class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are
}
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
val target: PtAssignTarget
get() = children.single() as PtAssignTarget
}
class PtRepeatLoop(position: Position) : PtNode(position) {
val count: PtExpression
get() = children[0] as PtExpression
@ -125,7 +134,8 @@ class PtRepeatLoop(position: Position) : PtNode(position) {
class PtReturn(position: Position) : PtNode(position) {
val hasValue = children.any()
val hasValue: Boolean
get() = children.any()
val value: PtExpression?
get() {
return if(children.any())
@ -142,7 +152,15 @@ sealed interface IPtVariable {
}
class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
class PtVariable(
name: String,
override val type: DataType,
val zeropage: ZeropageWish,
val align: UInt,
val value: PtExpression?,
val arraySize: UInt?,
position: Position
) : PtNamedNode(name, position), IPtVariable {
init {
value?.let {it.parent=this}
}
@ -152,7 +170,11 @@ class PtVariable(name: String, override val type: DataType, val zeropage: Zeropa
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable
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)
}
}
class PtWhen(position: Position) : PtNode(position) {
@ -169,3 +191,6 @@ class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
val statements: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtDefer(position: Position): PtNode(position), IPtStatementContainer

View File

@ -0,0 +1,16 @@
package prog8.code.ast
import prog8.code.SymbolTable
import prog8.code.core.*
fun verifyFinalAstBeforeAsmGen(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
/*
walkAst(program) { node, _ ->
if(node is PtVariable) {
if(node.value!=null) {
// require(node.type in ArrayDatatypes || node.type==DataType.STR) { "final check before asmgen: only string and array variables can still have an init value ${node.name} ${node.type} ${node.position}"}
}
}
}
*/
}

View File

@ -76,30 +76,28 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"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", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
"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_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", NumericDatatypesNoBool)), null),
"prog8_ifelse_bittest_set" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
"prog8_ifelse_bittest_notset" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
"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", NumericDatatypesNoBool)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
"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.UBYTE),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"divmod" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"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),
@ -129,7 +127,12 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"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),
"callfar2" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("argA", arrayOf(DataType.UBYTE)), FParam("argX", arrayOf(DataType.UBYTE)), FParam("argY", arrayOf(DataType.UBYTE)), FParam("argC", arrayOf(DataType.BOOL))), 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")
val InplaceModifyingBuiltinFunctions = setOf(
"setlsb", "setmsb",
"rol", "ror", "rol2", "ror2",
"divmod", "divmod__ubyte", "divmod__uword"
)

View File

@ -14,17 +14,21 @@ class CompilationOptions(val output: OutputType,
val compTarget: ICompilationTarget,
// these are set later, based on command line arguments or options in the source code:
var loadAddress: UInt,
var memtopAddress: UInt,
var warnSymbolShadowing: Boolean = false,
var optimize: Boolean = false,
var asmQuiet: Boolean = false,
var asmListfile: Boolean = false,
var includeSourcelines: Boolean = false,
var dumpVariables: Boolean = false,
var dumpSymbols: Boolean = false,
var experimentalCodegen: Boolean = false,
var varsHighBank: Int? = null,
var varsGolden: Boolean = false,
var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false,
var addMissingRts: Boolean = false, // deprecated, will likely go way in future version
var breakpointCpuInstruction: String? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()

View File

@ -1,6 +1,11 @@
package prog8.code.core
import kotlin.math.abs
import kotlin.math.pow
val powersOfTwoFloat = (0..16).map { (2.0).pow(it) }.toTypedArray()
val negativePowersOfTwoFloat = powersOfTwoFloat.map { -it }.toTypedArray()
val powersOfTwoInt = (0..16).map { 2.0.pow(it).toInt() }.toTypedArray()
fun Number.toHex(): String {
// 0..15 -> "0".."15"
@ -58,6 +63,7 @@ fun String.unescape(): String {
'\\' -> '\\'
'n' -> '\n'
'r' -> '\r'
't' -> '\t'
'"' -> '"'
'\'' -> '\''
'u' -> {

View File

@ -24,8 +24,8 @@ enum class DataType {
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, LONG, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT, BOOL)
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)
@ -41,9 +41,9 @@ enum class DataType {
infix fun largerThan(other: DataType) =
when {
this == other -> false
this in ByteDatatypes -> false
this in WordDatatypes -> other in ByteDatatypes
this == LONG -> other in ByteDatatypes+WordDatatypes
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
}
@ -51,7 +51,7 @@ enum class DataType {
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
@ -78,7 +78,7 @@ enum class RegisterOrPair {
R8, R9, R10, R11, R12, R13, R14, R15;
companion object {
val names by lazy { values().map { it.toString()} }
val names by lazy { entries.map { it.toString()} }
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
return when(cpu) {
CpuRegister.A -> A
@ -104,7 +104,7 @@ enum class Statusflag {
Pn; // don't use
companion object {
val names by lazy { values().map { it.toString()} }
val names by lazy { entries.map { it.toString()} }
}
}
@ -124,12 +124,13 @@ enum class BranchCondition {
}
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 IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
val IntegerDatatypes = IntegerDatatypesNoBool + DataType.BOOL
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
val NumericDatatypes = NumericDatatypesNoBool + DataType.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)
@ -141,7 +142,7 @@ val IterableDatatypes = arrayOf(
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,

View File

@ -11,4 +11,6 @@ interface IErrorReporter {
if(numErrors>0)
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
}
fun noErrorForLine(position: Position): Boolean
}

View File

@ -14,6 +14,7 @@ interface IMachineDefinition {
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
val PROGRAM_LOAD_ADDRESS : UInt
val PROGRAM_TOP_ADDRESS: UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
val BSSGOLDENRAM_START: UInt
@ -26,7 +27,9 @@ interface IMachineDefinition {
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun convertFloatToBytes(num: Double): List<UByte>
fun convertBytesToFloat(bytes: List<UByte>): Double
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean
}

View File

@ -5,7 +5,11 @@ enum class Encoding(val prefix: String) {
PETSCII("petscii"), // c64/c128/cx16
SCREENCODES("sc"), // c64/c128/cx16
ATASCII("atascii"), // atari
ISO("iso") // cx16
ISO("iso"), // cx16 (iso-8859-15)
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
CP437("cp437"), // cx16 (ibm pc, codepage 437)
KATAKANA("kata") // cx16 (katakana)
}
interface IStringEncoding {

View File

@ -71,7 +71,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
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)
@ -119,7 +119,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) {
in NumericDatatypes -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
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")
@ -151,7 +151,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
val size: Int =
when (datatype) {
in IntegerDatatypes -> options.compTarget.memorySize(datatype)
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
options.compTarget.memorySize(datatype, numElements!!)
}

View File

@ -1,8 +1,8 @@
package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor")
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 LogicalOperators = setOf("and", "or", "xor", "not", "in")
val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not")

View File

@ -1,13 +1,18 @@
package prog8.code.optimize
import prog8.code.StExtSub
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, errors: IErrorReporter) {
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
if (!options.optimize)
return
while(errors.noErrors() && optimizeCommonSubExpressions(program, errors)>0) {
while (errors.noErrors() &&
(optimizeBitTest(program, options)
+ optimizeAssignTargets(program, st, errors)) > 0
) {
// keep rolling
}
}
@ -22,109 +27,154 @@ private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
}
private var tempVarCounter = 0
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: IErrorReporter): Int {
var changes = 0
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtAssignment) {
val value = node.value
val functionName = when(value) {
is PtBuiltinFunctionCall -> value.name
is PtFunctionCall -> value.name
else -> null
}
if(functionName!=null) {
val stNode = st.lookup(functionName)
if (stNode is StExtSub) {
require(node.children.size==stNode.returns.size+1) {
"number of targets must match return values"
}
node.children.zip(stNode.returns).withIndex().forEach { (index, xx) ->
val target = xx.first as PtAssignTarget
val returnedRegister = xx.second.register.registerOrPair
if(returnedRegister!=null && !target.void && target.identifier!=null) {
if(isSame(target.identifier!!, xx.second.type, returnedRegister)) {
// output register is already identical to target register, so it can become void
val voidTarget = PtAssignTarget(true, target.position)
node.children[index] = voidTarget
voidTarget.parent = node
changes++
}
}
}
}
if(node.children.dropLast(1).all { (it as PtAssignTarget).void }) {
// all targets are now void, the whole assignment can be discarded and replaced by just a (void) call to the subroutine
val index = node.parent.children.indexOf(node)
val voidCall = PtFunctionCall(functionName, true, DataType.UNDEFINED, value.position)
value.children.forEach { voidCall.add(it) }
node.parent.children[index] = voidCall
voidCall.parent = node.parent
changes++
}
}
}
true
}
return changes
}
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
private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): Int {
if(options.compTarget.machine.cpu == CpuType.VIRTUAL)
return 0 // the special bittest optimization is not yet valid for the IR
fun makeBittestCall(condition: PtBinaryExpression, and: PtBinaryExpression, variable: PtIdentifier, bitmask: Int): PtBuiltinFunctionCall {
require(bitmask==128 || bitmask==64)
val setOrNot = if(condition.operator=="!=") "set" else "notset"
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.BOOL, condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(DataType.UBYTE, 7.0, and.right.position))
else
!expr.isSimple()
return result
bittestCall.add(PtNumber(DataType.UBYTE, 6.0, and.right.position))
return bittestCall
}
// 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
fun isAndByteCondition(condition: PtBinaryExpression?): Triple<PtBinaryExpression, PtIdentifier, Int>? {
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
if (condition.right.asConstInteger() == 0) {
val and = condition.left as? PtBinaryExpression
if (and != null && and.operator == "&" && and.type == DataType.UBYTE) {
val bitmask = and.right.asConstInteger()
if(bitmask==128 || bitmask==64) {
val variable = and.left as? PtIdentifier
if (variable != null && variable.type in ByteDatatypes) {
return Triple(and, variable, bitmask)
}
val typecast = and.left as? PtTypeCast
if (typecast != null && typecast.type == DataType.UBYTE) {
val castedVariable = typecast.value as? PtIdentifier
if(castedVariable!=null && castedVariable.type in ByteDatatypes)
return Triple(and, castedVariable, bitmask)
}
}
}
}
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 null
}
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
var changes = 0
var recurse = true
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtIfElse) {
val condition = node.condition as? PtBinaryExpression
val check = isAndByteCondition(condition)
if(check!=null) {
val (and, variable, bitmask) = check
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
val ifElse = PtIfElse(node.position)
ifElse.add(bittestCall)
ifElse.add(node.ifScope)
if (node.hasElse())
ifElse.add(node.elseScope)
val index = node.parent.children.indexOf(node)
node.parent.children[index] = ifElse
ifElse.parent = node.parent
changes++
recurse = false
}
}
child=parent
parent=parent.parent
if (node is PtIfExpression) {
val condition = node.condition as? PtBinaryExpression
val check = isAndByteCondition(condition)
if(check!=null) {
val (and, variable, bitmask) = check
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
node.children[0] = bittestCall
bittestCall.parent = node
changes++
recurse = false
}
}
recurse
}
return changes
}
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
if(returnedRegister in Cx16VirtualRegisters) {
val regname = returnedRegister.name.lowercase()
val identifierRegName = identifier.name.substringAfterLast('.')
/*
cx16.r? UWORD
cx16.r?s WORD
cx16.r?L UBYTE
cx16.r?H UBYTE
cx16.r?sL BYTE
cx16.r?sH BYTE
*/
if(identifier.type in ByteDatatypes && type in ByteDatatypes) {
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
return identifierRegName.substring(2) in arrayOf("", "L", "sL") // note: not the -H (msb) variants!
}
}
else if(identifier.type in WordDatatypes && type in WordDatatypes) {
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
return identifierRegName.substring(2) in arrayOf("", "s")
}
}
}
return false // there are no identifiers directly corresponding to cpu registers
}

View File

@ -15,7 +15,7 @@ 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 -> throw IllegalArgumentException("invalid datatype")
@ -23,5 +23,9 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
}
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

@ -19,3 +19,25 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Cb
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
}
}
val CompilationTargets = listOf(
C64Target.NAME,
C128Target.NAME,
Cx16Target.NAME,
PETTarget.NAME,
AtariTarget.NAME,
Neo6502Target.NAME,
VMTarget.NAME
)
fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
C64Target.NAME -> C64Target()
C128Target.NAME -> C128Target()
Cx16Target.NAME -> Cx16Target()
PETTarget.NAME -> PETTarget()
AtariTarget.NAME -> AtariTarget()
VMTarget.NAME -> VMTarget()
Neo6502Target.NAME -> Neo6502Target()
else -> throw IllegalArgumentException("invalid compilation target")
}

View File

@ -4,9 +4,7 @@ import com.github.michaelbull.result.fold
import prog8.code.core.Encoding
import prog8.code.core.IStringEncoding
import prog8.code.core.InternalCompilerException
import prog8.code.target.cbm.AtasciiEncoding
import prog8.code.target.cbm.IsoEncoding
import prog8.code.target.cbm.PetsciiEncoding
import prog8.code.target.encodings.*
object Encoder: IStringEncoding {
@ -18,6 +16,10 @@ object Encoder: IStringEncoding {
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
Encoding.ISO -> IsoEncoding.encode(str)
Encoding.ATASCII -> AtasciiEncoding.encode(str)
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
Encoding.CP437 -> Cp437Encoding.encode(str)
Encoding.KATAKANA -> KatakanaEncoding.encode(str)
else -> throw InternalCompilerException("unsupported encoding $encoding")
}
return coded.fold(
@ -31,6 +33,10 @@ object Encoder: IStringEncoding {
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
Encoding.ISO -> IsoEncoding.decode(bytes)
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
Encoding.CP437 -> Cp437Encoding.decode(bytes)
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
else -> throw InternalCompilerException("unsupported encoding $encoding")
}
return decoded.fold(

View File

@ -0,0 +1,30 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.target.neo6502.Neo6502MachineDefinition
class Neo6502Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = Neo6502MachineDefinition()
override val defaultEncoding = Encoding.ISO
companion object {
const val NAME = "neo"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt== DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -14,7 +14,7 @@ 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 -> throw IllegalArgumentException("invalid datatype")
@ -22,5 +22,8 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
}
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

@ -12,6 +12,7 @@ class AtariMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6
override val PROGRAM_LOAD_ADDRESS = 0x2000u
override val PROGRAM_TOP_ADDRESS = 0xffffu // TODO what's memtop
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
@ -22,13 +23,8 @@ class AtariMachineDefinition: IMachineDefinition {
override lateinit var golden: GoldenRam
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)
listOf("syslib")
else
emptyList()
}
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
val emulatorName: String

View File

@ -14,6 +14,7 @@ class C128MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val PROGRAM_TOP_ADDRESS = 0xfeffu
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
@ -25,11 +26,15 @@ class C128MachineDefinition: IMachineDefinition {
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 convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {

View File

@ -15,6 +15,8 @@ class C64MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_TOP_ADDRESS = 0xcfe0u // $9fff if floats are used
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
override val BSSHIGHRAM_START = 0xc000u
override val BSSHIGHRAM_END = 0xcfffu
@ -26,11 +28,15 @@ class C64MachineDefinition: IMachineDefinition {
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 convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {

View File

@ -6,7 +6,7 @@ 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 -> throw IllegalArgumentException("invalid datatype")
@ -14,5 +14,8 @@ internal object CbmMemorySizer: IMemSizer {
}
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

@ -14,6 +14,7 @@ class CX16MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_TOP_ADDRESS = 0x9effu
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
@ -24,11 +25,16 @@ class CX16MachineDefinition: IMachineDefinition {
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 convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {

View File

@ -1,4 +1,4 @@
package prog8.code.target.cbm
package prog8.code.target.encodings
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result

View File

@ -0,0 +1,69 @@
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import java.io.CharConversionException
import java.nio.charset.Charset
object Cp437Encoding {
val charset: Charset = Charset.forName("IBM437")
fun encode(str: String): Result<List<UByte>, CharConversionException> {
return try {
val mapped = str.map { chr ->
when (chr) {
'\u0000' -> 0u
'\u00a0' -> 255u
'☺' -> 1u
'☻' -> 2u
'♥' -> 3u
'♦' -> 4u
'♣' -> 5u
'♠' -> 6u
'•' -> 7u
'◘' -> 8u
'○' -> 9u
'◙' -> 10u
'♂' -> 11u
'♀' -> 12u
'♪' -> 13u
'♫' -> 14u
'☼' -> 15u
'►' -> 16u
'◄' -> 17u
'↕' -> 18u
'‼' -> 19u
'¶' -> 20u
'§' -> 21u
'▬' -> 22u
'↨' -> 23u
'↑' -> 24u
'↓' -> 25u
'→' -> 26u
'←' -> 27u
'∟' -> 28u
'↔' -> 29u
'▲' -> 30u
'▼' -> 31u
in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte()
}
else -> charset.encode(chr.toString())[0].toUByte()
}
}
Ok(mapped)
} catch (ce: CharConversionException) {
Err(ce)
}
}
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
} catch (ce: CharConversionException) {
Err(ce)
}
}
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.cbm
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
@ -6,8 +6,8 @@ import com.github.michaelbull.result.Result
import java.io.CharConversionException
import java.nio.charset.Charset
object IsoEncoding {
val charset: Charset = Charset.forName("ISO-8859-15")
open class IsoEncodingBase(charsetName: String) {
val charset: Charset = Charset.forName(charsetName)
fun encode(str: String): Result<List<UByte>, CharConversionException> {
return try {
@ -35,3 +35,8 @@ object IsoEncoding {
}
}
}
object IsoEncoding: IsoEncodingBase("ISO-8859-15")
object IsoCyrillicEncoding: IsoEncodingBase("ISO-8859-5")
object IsoEasternEncoding: IsoEncodingBase("ISO-8859-16")

View File

@ -0,0 +1,122 @@
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import java.io.CharConversionException
import java.nio.charset.Charset
object JapaneseCharacterConverter {
// adapted from https://github.com/raminduw/Japanese-Character-Converter
private val ZENKAKU_KATAKANA = charArrayOf(
'ァ', 'ア', 'ィ', 'イ', 'ゥ',
'ウ', 'ェ', 'エ', 'ォ', 'オ', 'カ', 'ガ', 'キ', 'ギ', 'ク', 'グ', 'ケ', 'ゲ',
'コ', 'ゴ', 'サ', 'ザ', 'シ', 'ジ', 'ス', 'ズ', 'セ', 'ゼ', 'ソ', 'ゾ', 'タ',
'ダ', 'チ', 'ヂ', 'ッ', 'ツ', 'ヅ', 'テ', 'デ', 'ト', 'ド', 'ナ', 'ニ', 'ヌ',
'ネ', '', 'ハ', 'バ', 'パ', 'ヒ', 'ビ', 'ピ', 'フ', 'ブ', 'プ', 'ヘ', 'ベ',
'ペ', 'ホ', 'ボ', 'ポ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ャ', 'ヤ', 'ュ', 'ユ',
'ョ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ヮ', 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン',
'ヴ', 'ヵ', 'ヶ'
)
private val HANKAKU_HIRAGANA = charArrayOf(
'ぁ', 'あ', 'ぃ', 'い', 'ぅ', 'う', 'ぇ', 'え',
'ぉ', 'お', 'か', 'が', 'き', 'ぎ', 'く', 'ぐ',
'け', 'げ', 'こ', 'ご', 'さ', 'ざ', 'し', 'じ',
'す', 'ず', 'せ', 'ぜ', 'そ', 'ぞ', 'た', 'だ',
'ち', 'ぢ', 'っ', 'つ', 'づ', 'て', 'で', 'と',
'ど', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ば',
'ぱ', 'ひ', 'び', 'ぴ', 'ふ', 'ぶ', 'ぷ', 'へ',
'べ', 'ぺ', 'ほ', 'ぼ', 'ぽ', 'ま', 'み', 'む',
'め', 'も', 'ゃ', 'や', 'ゅ', 'ゆ', 'ょ', 'よ',
'ら', 'り', 'る', 'れ', 'ろ', 'ゎ', 'わ', 'ゐ',
'ゑ', 'を', 'ん', 'ゔ', 'ゕ', 'ゖ'
)
private val HANKAKU_KATAKANA = arrayOf(
"", "", "", "", "",
"", "", "", "", "", "", "ガ", "", "ギ", "", "グ", "",
"ゲ", "", "ゴ", "", "ザ", "", "ジ", "", "ズ", "", "ゼ", "ソ",
"ゾ", "", "ダ", "", "ヂ", "", "", "ヅ", "", "デ", "", "ド",
"", "", "", "", "", "", "バ", "パ", "", "ビ", "ピ", "",
"ブ", "プ", "", "ベ", "ペ", "", "ボ", "ポ", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "ヴ", "", ""
)
private val ZENKAKU_KATAKANA_FIRST_CHAR_CODE = ZENKAKU_KATAKANA.first().code
private val HANKAKU_HIRAGANA_FIRST_CHAR_CODE = HANKAKU_HIRAGANA.first().code
private fun zenkakuKatakanaToHankakuKatakana(c: Char): String = if (c in ZENKAKU_KATAKANA) HANKAKU_KATAKANA[c.code - ZENKAKU_KATAKANA_FIRST_CHAR_CODE] else c.toString()
private fun hankakuKatakanaToZenkakuKatakana(c: Char): Char = if (c in HANKAKU_HIRAGANA) ZENKAKU_KATAKANA[c.code - HANKAKU_HIRAGANA_FIRST_CHAR_CODE] else c
fun zenkakuKatakanaToHankakuKatakana(s: String): String = buildString {
for (element in s) {
val converted = hankakuKatakanaToZenkakuKatakana(element)
val convertedChar = zenkakuKatakanaToHankakuKatakana(converted)
append(convertedChar)
}
}
}
object KatakanaEncoding {
val charset: Charset = Charset.forName("JIS_X0201")
fun encode(str: String): Result<List<UByte>, CharConversionException> {
return try {
val mapped = str.map { chr ->
when (chr) {
'\u0000' -> 0u
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
'♥' -> 0xe3u
'♦' -> 0xe4u
'♣' -> 0xe5u
'♠' -> 0xe6u
'大' -> 0xeau
'中' -> 0xebu
'小' -> 0xecu
'百' -> 0xedu
'千' -> 0xeeu
'万' -> 0xefu
'♪' -> 0xf0u
'土' -> 0xf1u
'金' -> 0xf2u
'木' -> 0xf3u
'水' -> 0xf4u
'火' -> 0xf5u
'月' -> 0xf6u
'日' -> 0xf7u
'時' -> 0xf8u
'分' -> 0xf9u
'秒' -> 0xfau
'年' -> 0xfbu
'円' -> 0xfcu
'人' -> 0xfdu
'生' -> 0xfeu
'〒' -> 0xffu
in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte()
}
else -> charset.encode(chr.toString())[0].toUByte()
}
}
Ok(mapped)
} catch (ce: CharConversionException) {
Err(ce)
}
}
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
} catch (ce: CharConversionException) {
Err(ce)
}
}
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.cbm
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok

View File

@ -0,0 +1,49 @@
package prog8.code.target.neo6502
import prog8.code.core.*
import java.nio.file.Path
class Neo6502MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU65c02
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6
override val PROGRAM_LOAD_ADDRESS = 0x0800u
override val PROGRAM_TOP_ADDRESS = 0xfbffu
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("atari float asm bytes from number")
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The neo target only supports the main emulator (neo).")
return
}
val cmdline = listOf("neo", "${programNameWithPath}.bin@800", "cold")
println("\nStarting Neo6502 emulator...")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address in 0xff00u..0xff0fu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = Neo6502Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}

View File

@ -0,0 +1,48 @@
package prog8.code.target.neo6502
import prog8.code.core.*
class Neo6502Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xfau // temp storage for a single byte
override val SCRATCH_REG = 0xfbu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xfcu // temp storage 1 for a word $fc+$fd
override val SCRATCH_W2 = 0xfeu // temp storage 2 for a word $fe+$ff
init {
if (options.floats) {
throw InternalCompilerException("Neo6502 target doesn't support floating point routines")
}
when (options.zeropage) {
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> {
free.addAll(0x22u..0xffu)
}
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
allocateCx16VirtualRegisters()
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) {
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

@ -14,6 +14,7 @@ class PETMachineDefinition: IMachineDefinition {
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 PROGRAM_TOP_ADDRESS = 0x7fffu
override val BSSHIGHRAM_START = 0u
override val BSSHIGHRAM_END = 0u
@ -25,11 +26,15 @@ class PETMachineDefinition: IMachineDefinition {
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 convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {

View File

@ -14,6 +14,7 @@ class VirtualMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 8 // 64-bits double
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val PROGRAM_TOP_ADDRESS = 0xffffu // not actually used
override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used
@ -30,8 +31,24 @@ class VirtualMachineDefinition: IMachineDefinition {
return parts.joinToString(", ")
}
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return listOf("syslib")
override fun convertFloatToBytes(num: Double): List<UByte> {
val bits = num.toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
return parts
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==8) { "need 8 bytes" }
val b0 = bytes[0].toLong() shl (8*7)
val b1 = bytes[1].toLong() shl (8*6)
val b2 = bytes[2].toLong() shl (8*5)
val b3 = bytes[3].toLong() shl (8*4)
val b4 = bytes[4].toLong() shl (8*3)
val b5 = bytes[5].toLong() shl (8*2)
val b6 = bytes[6].toLong() shl (8*1)
val b7 = bytes[7].toLong() shl (8*0)
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {

View File

@ -1,15 +1,12 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
@ -28,9 +25,12 @@ dependencies {
implementation project(':codeCore')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
sourceSets {

View File

@ -14,5 +14,6 @@
<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" />
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
</component>
</module>

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++
}
mods = optimizeTSBtoRegularOr(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
var linesByFourteen = getLinesBy(lines, 14)
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
if(mods.isNotEmpty()) {
@ -58,15 +65,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++
}
mods = optimizeSamePointerIndexing(linesByFourteen)
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14)
numberOfOptimizations++
}
// TODO more assembly peephole optimizations
return numberOfOptimizations
}
@ -75,23 +80,48 @@ 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 optimizeSameAssignments(
linesByFourteen: List<List<IndexedValue<String>>>,
linesByFourteen: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition,
symbolTable: SymbolTable
): List<Modification> {
@ -281,7 +311,7 @@ private fun optimizeSameAssignments(
return mods
}
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): 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:
@ -318,33 +348,59 @@ 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>>>,
linesByFour: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition,
symbolTable: SymbolTable
): List<Modification> {
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
val mods = mutableListOf<Modification>()
for (lines in linesByFour) {
val first = lines[1].value.trimStart()
val second = lines[2].value.trimStart()
val third = lines[3].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 =
if(third.isBranch()) {
// a branch instruction follows, we can only remove the load instruction if
@ -385,6 +441,18 @@ private fun optimizeStoreLoadSame(
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tya"))
}
// 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
}
@ -414,7 +482,7 @@ private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
}
}
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) {
@ -431,50 +499,152 @@ 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 optimizeJsrRtsAndOtherCombinations(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
val second = lines[1].value
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
mods += Modification(lines[1].index, true, null)
val third = lines[2].value
if(!haslabel(second)) {
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
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 ((" 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))
}
}
}
}
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)
/*
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)
}
}
fun sameLabel(branchInstr: String, jumpInstr: String, labelInstr: String): Boolean {
if('(' in jumpInstr) return false // indirect jump cannot be replaced
val label = labelInstr.trimEnd().substringBefore(':').substringBefore(' ').substringBefore('\t')
val branchLabel = branchInstr.trimStart().substring(3).trim()
return label==branchLabel
}
// beq Label + jmp Addr + Label -> bne Addr
if((" jmp" in second || "\tjmp " in second) && haslabel(third)) {
if((" beq " in first || "\tbeq " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bne")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bne " in first || "\tbne " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "beq")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bcc " in first || "\tbcc " in first) && sameLabel(first, second, third)){
val branch = second.replace("jmp", "bcs")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bcs " in first || "\tbcs " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bcc")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bpl " in first || "\tbpl " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bmi")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bmi " in first || "\tbmi " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bpl")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bvc " in first || "\tbvc " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bvs")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bvs " in first || "\tbvs " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bvc")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
}
}
return mods
}
private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
val mods = mutableListOf<Modification>()
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
@ -507,11 +677,57 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
optimize('a', lines)
optimize('x', lines)
optimize('y', lines)
val first = lines[1].value.trimStart()
val second = lines[2].value.trimStart()
val third = lines[3].value.trimStart()
// phy + ldy + pla -> tya + ldy
// phx + ldx + pla -> txa + ldx
// pha + lda + pla -> nop
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
mods.add(Modification(lines[3].index, true, null))
mods.add(Modification(lines[1].index, false, " tya"))
}
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") {
mods.add(Modification(lines[3].index, true, null))
mods.add(Modification(lines[1].index, false, " txa"))
}
else if(first=="pha" && second.startsWith("lda ") && third=="pla") {
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 optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
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()
if(first.startsWith("lda") && second.startsWith("tsb") && third.startsWith("lda")) {
val operand1 = first.substring(3)
val operand2 = second.substring(3)
val operand3 = third.substring(3)
if(operand1!=operand2 && operand2==operand3) {
mods.add(Modification(lines[0].index, false, " lda $operand2"))
mods.add(Modification(lines[1].index, false, " ora $operand1"))
mods.add(Modification(lines[2].index, false, " sta $operand2"))
}
}
}
return mods
}
private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
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>()
@ -523,8 +739,8 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<Str
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[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))
}

View File

@ -1,7 +1,9 @@
package prog8.codegen.cpu6502
import prog8.code.core.*
import prog8.code.target.AtariTarget
import prog8.code.target.C64Target
import prog8.code.target.Neo6502Target
import java.nio.file.Path
@ -16,6 +18,7 @@ internal class AssemblyProgram(
private val binFile = outputDir.resolve("$name.bin")
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
private val listFile = outputDir.resolve("$name.list")
private val targetWithoutBreakpointsForEmulator = setOf(AtariTarget.NAME, Neo6502Target.NAME)
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
@ -28,7 +31,7 @@ internal class AssemblyProgram(
// 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", "-Werror",
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile"
)
if(options.warnSymbolShadowing)
@ -39,8 +42,9 @@ internal class AssemblyProgram(
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
command.add("--list=$listFile")
if(options.asmListfile) {
command.addAll(listOf("--list=$listFile", "--no-monitor"))
}
val outFile = when (options.output) {
OutputType.PRG -> {
@ -95,12 +99,47 @@ internal class AssemblyProgram(
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command
}
"neo" -> {
// Neo6502 raw program generation.
if(options.output!=OutputType.RAW || options.loadAddress!=0x0800u || options.launcher!=CbmPrgLauncherType.NONE) {
throw AssemblyError("invalid program compilation options. Neo6502 requires %output raw, %launcher none, %address $0800")
}
// TODO are these options okay for neo?
val command = mutableListOf("64tass", "--case-sensitive", "--long-branch",
"-Wall", // "-Werror", "-Wno-strict-bool"
"--no-monitor"
)
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
command.add("--list=$listFile")
val outFile = when (options.output) {
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
else -> throw AssemblyError("invalid output type, need 'raw'")
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command
}
else -> throw AssemblyError("invalid compilation target")
}
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
val result = proc.waitFor()
if (result == 0 && compTarget.name!="atari") {
if (result == 0 && compTarget.name !in targetWithoutBreakpointsForEmulator) {
removeGeneratedLabelsFromMonlist()
generateBreakpointList()
}

View File

@ -1,7 +1,5 @@
package prog8.codegen.cpu6502
import prog8.code.StMemVar
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
@ -33,7 +31,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
@ -44,8 +41,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"ror2" -> funcRor2(fcall)
"setlsb" -> funcSetLsbMsb(fcall, false)
"setmsb" -> funcSetLsbMsb(fcall, true)
"sort" -> funcSort(fcall)
"reverse" -> funcReverse(fcall)
"memory" -> funcMemory(fcall, discardResult, resultRegister)
"peekw" -> funcPeekW(fcall, resultRegister)
"peekf" -> funcPeekF(fcall, resultRegister)
@ -67,7 +62,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"rrestore" -> funcRrestore()
"cmp" -> funcCmp(fcall)
"callfar" -> funcCallFar(fcall, resultRegister)
"callfar2" -> funcCallFar2(fcall, resultRegister)
"call" -> funcCall(fcall)
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
@ -82,14 +80,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when (resultType) {
DataType.UBYTE -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A)
asmgen.out(" tay | jsr math.multiply_bytes")
asmgen.out(" tay | jsr prog8_math.multiply_bytes")
if(resultRegister!=null) {
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), CpuRegister.A, false, false)
}
}
DataType.UWORD -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr math.square")
asmgen.out(" jsr prog8_math.square")
if(resultRegister!=null) {
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), RegisterOrPair.AY)
}
@ -106,7 +104,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A ,false)
// math.divmod_ub_asm: -- divide A by Y, result quotient in Y, remainder in A (unsigned)
asmgen.out(" jsr math.divmod_ub_asm")
asmgen.out(" jsr prog8_math.divmod_ub_asm")
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
@ -120,7 +118,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
asmgen.out(" jsr math.divmod_uw_asm")
asmgen.out(" jsr prog8_math.divmod_uw_asm")
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
@ -178,33 +176,124 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcCall(fcall: PtBuiltinFunctionCall) {
// note: the routine can return a word value (in AY)
val constAddr = fcall.args[0].asConstInteger()
if(constAddr!=null) {
asmgen.out(" jsr ${constAddr.toHex()}")
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) // jump address
asmgen.out("""
sta (+)+1
sty (+)+2
+ jsr 0 ; modified""")
return
}
// note: the routine can return a word value (in AY)
val identifier = fcall.args[0] as? PtIdentifier
if(identifier!=null) {
asmgen.out("""
; push a return address so the jmp becomes indirect jsr
lda #>((+)-1)
pha
lda #<((+)-1)
pha
jmp (${asmgen.asmSymbolName(identifier)})
+""")
return
}
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
asmgen.out("""
; push a return address so the jmp becomes indirect jsr
lda #>((+)-1)
pha
lda #<((+)-1)
pha
jmp (P8ZP_SCRATCH_W2)
+""")
}
private fun funcCallFar(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callfar only works on cx16 target at this time")
val targetName = asmgen.options.compTarget.name
if(targetName !in arrayOf("cx16", "c64", "c128"))
throw AssemblyError("callfar only works on cx16, c64 and c128 targets at this time")
val jsrfar = when(targetName) {
"cx16" -> "cx16.JSRFAR"
"c64" -> "c64.x16jsrfar"
"c128" -> "c128.x16jsrfar"
else -> TODO("jsrfar routine")
}
val constBank = fcall.args[0].asConstInteger()
val constAddress = fcall.args[1].asConstInteger()
if(constBank!=null && constAddress!=null) {
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.AY) // uword argument
asmgen.out("""
jsr $jsrfar
.word ${constAddress.toHex()}
.byte $constBank""")
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
asmgen.out(" sta (++)+0")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
asmgen.out(" sta (+)+0 | sty (+)+1")
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.AY) // uword argument
asmgen.out("""
jsr $jsrfar
+ .word 0
+ .byte 0""")
}
// note that by convention the values in A+Y registers are now the return value of the call.
if(resultRegister!=null) {
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), RegisterOrPair.AY)
}
}
private fun funcCallFar2(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val targetName = asmgen.options.compTarget.name
if(targetName !in arrayOf("cx16", "c64", "c128"))
throw AssemblyError("callfar2 only works on cx16, c64 and c128 targets at this time")
fun assignArgs() {
fun assign(value: PtExpression, register: Char) {
when(value) {
is PtBool -> asmgen.out(" ld$register #${value.asInt()}")
is PtNumber -> asmgen.out(" ld$register #${value.number.toInt()}")
is PtIdentifier -> asmgen.out(" ld$register ${asmgen.asmVariableName(value)}")
else -> TODO("callfar2: support non-simple expressions for arguments")
}
}
assign(fcall.args[2], 'a')
assign(fcall.args[3], 'x')
assign(fcall.args[4], 'y')
val carry = fcall.args[5].asConstInteger()
if(carry!=null)
asmgen.out(if(carry==0) " clc" else " sec")
else
TODO("callfar2: support non-const argument values")
}
val jsrfar = when(targetName) {
"cx16" -> "cx16.JSRFAR"
"c64" -> "c64.x16jsrfar"
"c128" -> "c128.x16jsrfar"
else -> TODO("jsrfar routine")
}
val constBank = fcall.args[0].asConstInteger()
val constAddress = fcall.args[1].asConstInteger()
if(constBank!=null && constAddress!=null) {
assignArgs()
asmgen.out("""
jsr $jsrfar
.word ${constAddress.toHex()}
.byte $constBank""")
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
asmgen.out(" sta (++)+0")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
asmgen.out(" sta (+)+0 | sty (+)+1")
assignArgs()
asmgen.out("""
jsr $jsrfar
+ .word 0
+ .byte 0""")
}
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
asmgen.out(" sta (++)+0")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
asmgen.out(" sta (+)+0 | sty (+)+1")
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.AY) // uword argument
asmgen.out("""
jsr cx16.JSRFAR
+ .word 0
+ .byte 0""")
// note that by convention the values in A+Y registers are now the return value of the call.
if(resultRegister!=null) {
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), RegisterOrPair.AY)
@ -225,6 +314,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.number.toInt()}")
}
is PtBool -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.asInt()}")
}
is PtMemoryByte -> {
if(arg2.address is PtNumber) {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
@ -253,6 +346,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
cmp ${asmgen.asmVariableName(arg2)}
+""")
}
is PtBool -> TODO("word compare against bool")
is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
@ -309,115 +403,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun funcReverse(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single() as PtIdentifier
val symbol = asmgen.symbolTable.lookup(variable.name)
val (dt, numElements) = when(symbol) {
is StStaticVariable -> symbol.dt to symbol.length!!
is StMemVar -> symbol.dt to symbol.length!!
else -> DataType.UNDEFINED to 0
}
val varName = asmgen.asmVariableName(variable)
when (dt) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b""")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_w""")
}
DataType.STR -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #${numElements-1}
jsr prog8_lib.func_reverse_b""")
}
DataType.ARRAY_F -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr floats.func_reverse_f""")
}
in SplitWordArrayTypes -> {
// reverse the lsb and msb arrays both, independently
asmgen.out("""
lda #<${varName}_lsb
ldy #>${varName}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b
lda #<${varName}_msb
ldy #>${varName}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b""")
}
else -> throw AssemblyError("weird type")
}
}
private fun funcSort(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single() as PtIdentifier
val symbol = asmgen.symbolTable.lookup(variable.name)
val varName = asmgen.asmVariableName(variable)
val (dt, numElements) = when(symbol) {
is StStaticVariable -> symbol.dt to symbol.length!!
is StMemVar -> symbol.dt to symbol.length!!
else -> DataType.UNDEFINED to 0
}
when (dt) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (dt == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (dt == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
}
DataType.STR -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #${numElements-1}
jsr prog8_lib.func_sort_ub""")
}
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
in SplitWordArrayTypes -> TODO("split words sort")
else -> throw AssemblyError("weird type")
}
}
private fun funcRor2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
@ -665,6 +650,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
is PtAddressOf -> {
val mem = PtMemoryByte(fcall.position)
if((fcall.args[0] as PtAddressOf).isFromArrayElement)
TODO("address-of arrayelement")
if(msb) {
val address = PtBinaryExpression("+", DataType.UWORD, fcall.args[0].position)
address.add(fcall.args[0])
@ -730,8 +717,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
when (dt) {
when (val dt = fcall.args.single().type) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
@ -742,63 +728,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true, true)
}
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
val dt = fcall.args.single().type
val array = fcall.args[0] as PtIdentifier
when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A")
}
DataType.ARRAY_F -> {
outputAddressAndLengthOfArray(array)
asmgen.out(" jsr floats.func_${fcall.name}_f_into_A")
}
in SplitWordArrayTypes -> {
val numElements = (asmgen.symbolTable.lookup(array.name) as StStaticVariable).length
when(fcall.name) {
"any" -> {
// any(lsb-array) or any(msb-array)
val arrayName = asmgen.asmVariableName(array)
asmgen.out("""
lda #<${arrayName}_lsb
ldy #>${arrayName}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
""")
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
asmgen.out(" bne +") // shortcircuit
asmgen.out("""
pha
lda #<${arrayName}_msb
ldy #>${arrayName}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
""")
asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A")
asmgen.out("""
sta P8ZP_SCRATCH_REG
pla
ora P8ZP_SCRATCH_REG
+""")
}
"all" -> {
TODO("split words all")
}
else -> throw AssemblyError("weird call")
}
}
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes, true)
}
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
@ -1194,7 +1123,44 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
when(resultRegister) {
if(arg is PtArrayIndexer && resultRegister in setOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the msb byte out of the word array
if(arg.splitWords) {
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" lda $arrayVar,y")
}
RegisterOrPair.Y -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
asmgen.out(" ldy $arrayVar,x")
}
RegisterOrPair.X -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" ldx $arrayVar,y")
}
else -> throw AssemblyError("invalid reg")
}
} else {
val arrayVar = asmgen.asmVariableName(arg.variable)
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" lda $arrayVar+1,y")
}
RegisterOrPair.Y -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
asmgen.out(" ldy $arrayVar+1,x")
}
RegisterOrPair.X -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
asmgen.out(" ldx $arrayVar+1,y")
}
else -> throw AssemblyError("invalid reg")
}
}
} else when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" tya")
@ -1209,6 +1175,23 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" pla")
}
RegisterOrPair.AY -> {
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" tya | ldy #0 | cmp #0")
}
RegisterOrPair.AX -> {
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AX)
asmgen.out(" txa | ldx #0 | cmp #0")
}
RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(arg, RegisterOrPair.XY)
asmgen.out(" tya | tax | ldy #0 | cpx #0")
}
in Cx16VirtualRegisters -> {
val reg = "cx16.${resultRegister.toString().lowercase()}"
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" sty ${reg}L | lda #0 | sta ${reg}H | lda ${reg}L")
}
else -> throw AssemblyError("invalid reg")
}
}
@ -1250,7 +1233,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
RegisterOrPair.Y -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.X)
asmgen.out(" lda $arrayVar,x")
asmgen.out(" ldy $arrayVar,x")
}
RegisterOrPair.X -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
@ -1288,35 +1271,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" ldy #0 | cpx #0")
}
in Cx16VirtualRegisters -> {
asmgen.assignExpressionToRegister(arg, resultRegister)
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
zero.parent=fcall
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
asmgen.out(" lda cx16.r0L")
val reg = "cx16.${resultRegister.toString().lowercase()}"
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" sta ${reg}L | ldy #0 | sty ${reg}H | cmp #0")
}
else -> throw AssemblyError("invalid reg")
}
}
}
private fun outputAddressAndLengthOfArray(arg: PtIdentifier) {
// address goes in P8ZP_SCRATCH_W1, number of elements in A
val symbol = asmgen.symbolTable.lookup(arg.name)
val numElements = when(symbol) {
is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!!
else -> 0
}
val identifierName = asmgen.asmVariableName(arg)
asmgen.out("""
lda #<$identifierName
ldy #>$identifierName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
""")
}
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
val signature = BuiltinFunctions.getValue(call.name)
val callConv = signature.callConvention(call.args.map { it.type})

View File

@ -17,7 +17,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
emptyList()
else {
val register = when (returntype!!) {
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
@ -31,7 +31,7 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
return when(returntype) {
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
null -> null

View File

@ -37,7 +37,7 @@ internal class ForLoopsAsmGen(
val endLabel = asmgen.makeLabel("for_end")
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopEndLabels.add(endLabel)
val stepsize=range.step.asConstInteger()!!
if(stepsize < -1) {
@ -48,25 +48,43 @@ internal class ForLoopsAsmGen(
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
if (stepsize==1 || stepsize==-1) {
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
if (stepsize==-1 && range.to.asConstInteger()==0) {
// simple loop downto 0 step -1
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
dec $varname
lda $varname
cmp #255
bne $loopLabel""")
}
else if (stepsize==-1 && range.to.asConstInteger()==1) {
// simple loop downto 1 step -1
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
dec $varname
bne $loopLabel""")
}
else if (stepsize==1 || stepsize==-1) {
// bytes array, step 1 or -1
val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar
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)
if(stepsize<0) {
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
}
else
asmgen.out("""
sec
@ -75,12 +93,13 @@ internal class ForLoopsAsmGen(
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
if(stepsize<0) {
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
}
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
@ -100,8 +119,6 @@ $modifiedLabel cmp #0 ; modified
// bytes, step >= 2 or <= -2
// loop over byte range via loopvar
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) {
@ -155,58 +172,85 @@ $modifiedLabel cmp #0 ; modified
}
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
when {
val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range)
if(stepsize==-1 && range.to.asConstInteger()==0) {
// simple loop downto 0 step -1 (words)
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
bne ++
lda $varname+1
beq $endLabel
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
}
else if (stepsize==-1 && range.to.asConstInteger()==1) {
// simple loop downto 1 step -1 (words)
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
cmp #1
bne +
lda $varname+1
beq $endLabel
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
}
else if (stepsize == 1 || stepsize == -1) {
// words, step 1 or -1
stepsize == 1 || stepsize == -1 -> {
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
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.statements)
asmgen.out("""
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname+1
$modifiedLabel cmp #0 ; modified
bne +
lda $varname
$modifiedLabel2 cmp #0 ; modified
beq $endLabel""")
if(stepsize==1) {
asmgen.out("""
if(stepsize==1) {
asmgen.out("""
+ inc $varname
bne $loopLabel
inc $varname+1""")
asmgen.jmp(loopLabel)
} else {
asmgen.out("""
asmgen.jmp(loopLabel)
} else {
asmgen.out("""
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
}
asmgen.out(endLabel)
asmgen.jmp(loopLabel)
}
stepsize > 0 -> {
// (u)words, step >= 2
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
asmgen.out(endLabel)
}
else if (stepsize > 0) {
// (u)words, step >= 2
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.statements)
asmgen.translate(stmt.statements)
if (iterableDt == DataType.ARRAY_UW) {
asmgen.out("""
if (iterableDt == DataType.ARRAY_UW) {
asmgen.out("""
lda $varname
clc
adc #<$stepsize
@ -222,8 +266,8 @@ $modifiedLabel2 lda #0 ; modified
bcc $endLabel
bcs $loopLabel
$endLabel""")
} else {
asmgen.out("""
} else {
asmgen.out("""
lda $varname
clc
adc #<$stepsize
@ -239,22 +283,20 @@ $modifiedLabel lda #0 ; modified
eor #$80
+ bpl $loopLabel
$endLabel""")
}
}
else -> {
}
else {
// (u)words, step <= -2
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
// (u)words, step <= -2
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.statements)
asmgen.translate(stmt.statements)
asmgen.out("""
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
@ -271,13 +313,12 @@ $modifiedLabel sbc #0 ; modified
eor #$80
+ bpl $loopLabel
$endLabel""")
}
}
}
else -> throw AssemblyError("range expression can only be byte or word")
}
asmgen.loopEndLabels.pop()
asmgen.loopEndLabels.removeLast()
}
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
@ -333,10 +374,9 @@ $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)
asmgen.loopEndLabels.add(endLabel)
val iterableName = asmgen.asmVariableName(ident)
val symbol = asmgen.symbolTable.lookup(ident.name)
val numElements = when(symbol) {
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!!
else -> 0
@ -359,7 +399,7 @@ $loopLabel lda ${65535.toHex()} ; modified
bne $loopLabel
$endLabel""")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
val indexVar = asmgen.makeLabel("for_index")
asmgen.out("""
ldy #0
@ -478,17 +518,19 @@ $loopLabel sty $indexVar
}
else -> throw AssemblyError("can't iterate over $iterableDt")
}
asmgen.loopEndLabels.pop()
asmgen.loopEndLabels.removeLast()
}
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) {
if(range.last==range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt==DataType.ARRAY_UB)
}
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
if(range.last==range.first) return translateForSimpleWordRangeAsc(stmt, range)
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
}
@ -496,7 +538,7 @@ $loopLabel sty $indexVar
// not one of the easy cases, generate more complex code...
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopEndLabels.add(endLabel)
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
// loop over byte range via loopvar, step >= 2 or <= -2
@ -508,7 +550,7 @@ $loopLabel""")
asmgen.translate(stmt.statements)
when (range.step) {
0, 1, -1 -> {
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $range ${stmt.position}")
}
2 -> {
if(range.last==255 || range.last==254) {
@ -602,13 +644,13 @@ $loopLabel""")
}
else -> throw AssemblyError("range expression can only be byte or word")
}
asmgen.loopEndLabels.pop()
asmgen.loopEndLabels.removeLast()
}
private fun translateForSimpleByteRangeAsc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopEndLabels.add(endLabel)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #${range.first}
@ -628,50 +670,50 @@ $endLabel""")
bne $loopLabel
$endLabel""")
}
asmgen.loopEndLabels.pop()
asmgen.loopEndLabels.removeLast()
}
private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression) {
private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression, unsigned: Boolean) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #${range.first}
sta $varname
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.statements)
when (range.last) {
0 -> {
asmgen.out("""
lda $varname
beq $endLabel
dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
if(!unsigned || range.first<=127) {
asmgen.out("""
dec $varname
bpl $loopLabel""")
} else {
asmgen.out("""
dec $varname
lda $varname
cmp #255
bne $loopLabel""")
}
}
1 -> {
asmgen.out("""
dec $varname
bne $loopLabel
$endLabel""")
bne $loopLabel""")
}
else -> {
asmgen.out("""
dec $varname
lda $varname
cmp #${range.last-1}
bne $loopLabel
$endLabel""")
bne $loopLabel""")
}
}
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleWordRangeAsc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopEndLabels.add(endLabel)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #<${range.first}
@ -692,13 +734,13 @@ $loopLabel""")
inc $varname+1""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
asmgen.loopEndLabels.pop()
asmgen.loopEndLabels.removeLast()
}
private fun translateForSimpleWordRangeDesc(stmt: PtForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopEndLabels.add(endLabel)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
lda #<${range.first}
@ -707,20 +749,29 @@ $loopLabel""")
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.statements)
if(range.last==0) {
asmgen.out("""
lda $varname
bne ++
lda $varname+1
beq $endLabel""")
} else {
asmgen.out("""
lda $varname
cmp #<${range.last}
bne +
lda $varname+1
cmp #>${range.last}
beq $endLabel""")
}
asmgen.out("""
lda $varname
cmp #<${range.last}
bne +
lda $varname+1
cmp #>${range.last}
beq $endLabel
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
asmgen.loopEndLabels.pop()
asmgen.loopEndLabels.removeLast()
}
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =

View File

@ -16,8 +16,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
(sub.parameters.size==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 translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call
@ -40,13 +40,90 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
asmgen.out(" \t; inlined routine end: ${sub.name}")
} else {
asmgen.out(" jsr $subAsmName")
val bank = sub.address?.constbank?.toString()
if(bank==null) {
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
if(varbank!=null) {
when(asmgen.options.compTarget.name) {
"cx16" -> {
// JSRFAR can jump to a banked RAM address as well!
asmgen.out("""
php
pha
lda $varbank
sta +
pla
plp
jsr cx16.JSRFAR
.word $subAsmName ; ${sub.address!!.address.toHex()}
+ .byte 0 ; modified"""
)
}
"c64" -> {
asmgen.out("""
php
pha
lda $varbank
sta +
pla
plp
jsr c64.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
+ .byte 0 ; modified"""
)
}
"c128" -> {
asmgen.out("""
php
pha
lda $varbank
sta +
pla
plp
jsr c128.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
+ .byte 0 ; modified"""
)
}
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
}
} else {
asmgen.out(" jsr $subAsmName")
}
}
else {
when(asmgen.options.compTarget.name) {
"cx16" -> {
// JSRFAR can jump to a banked RAM address as well!
asmgen.out("""
jsr cx16.JSRFAR
.word $subAsmName ; ${sub.address!!.address.toHex()}
.byte $bank"""
)
}
"c64" -> {
asmgen.out("""
jsr c64.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
.byte $bank"""
)
}
"c128" -> {
asmgen.out("""
jsr c128.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
.byte $bank"""
)
}
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
}
}
}
}
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
@ -81,15 +158,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
is PtAddressOf -> false
is PtIdentifier -> false
is PtMachineRegister -> false
is PtMemoryByte -> false
is PtIrRegister -> false
is PtMemoryByte -> arg.address !is PtNumber && arg.address !is PtIdentifier
is PtNumber -> false
is PtBool -> false
else -> true
}
}
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
val registersUsed = mutableListOf<RegisterOrStatusflag>();
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}
@ -109,9 +187,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
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)
if(usedX()) asmgen.saveRegisterStack(CpuRegister.X, usedA())
if(usedY()) asmgen.saveRegisterStack(CpuRegister.Y, usedA())
if(usedA()) asmgen.saveRegisterStack(CpuRegister.A, usedA())
val used = argumentViaRegister(sub, IndexedValue(it, param.second), arg)
if(usedA()) asmgen.restoreRegisterStack(CpuRegister.A, false)
if(usedY()) asmgen.restoreRegisterStack(CpuRegister.Y, true)
@ -152,7 +230,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
if (statusflag!=null) {
if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte value is required")
throw AssemblyError("for statusflag, byte or bool value is required")
if (statusflag == Statusflag.Pc) {
// 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
@ -161,6 +239,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc")
}
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
@ -201,7 +282,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(value is PtIdentifier) {
val addr = PtAddressOf(Position.DUMMY)
addr.add(value)
addr.parent = sub as PtNode
addr.parent = scope as PtNode
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
@ -209,7 +290,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
}
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, target.position), scope)
}
return RegisterOrStatusflag(register, null)
}
@ -218,6 +299,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
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)

File diff suppressed because it is too large Load Diff

View File

@ -1,191 +0,0 @@
package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.core.*
internal class PostIncrDecrAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
internal fun translate(stmt: PtPostIncrDecr) {
val incr = stmt.operator=="++"
val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.array
when {
targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent)
when (stmt.target.type) {
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 -> {
fun incDecViaExprEval() {
asmgen.assignExpressionToRegister(targetMemory.address, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
fun tryOptimizedPointerIncDec(address: PtBinaryExpression): Boolean {
if(address.operator=="+") {
val offset = address.right.asConstInteger()
if(offset!=null && offset<256) {
// we have @(ptr + 255) ++ , or @(ptr+255)--
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.out("""
sta (+) + 1
sty (+) + 2
ldx #$offset""")
if(incr)
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
return true
} else if(address.right.type in ByteDatatypes) {
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.X, false)
if(incr)
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
return true
} else if((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.X, false)
if(incr)
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
return true
}
}
return false
}
when (val addressExpr = targetMemory.address) {
is PtNumber -> {
val what = addressExpr.number.toHex()
asmgen.out(if(incr) " inc $what" else " dec $what")
}
is PtIdentifier -> {
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")
}
is PtBinaryExpression -> {
if(!tryOptimizedPointerIncDec(addressExpr)) {
incDecViaExprEval()
}
}
else -> {
incDecViaExprEval()
}
}
}
targetArrayIdx!=null -> {
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
val elementDt = targetArrayIdx.type
val constIndex = targetArrayIdx.index.asConstInteger()
if(targetArrayIdx.splitWords) {
if(constIndex!=null) {
if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb+$constIndex | bne + | inc ${asmArrayvarname}_msb+$constIndex |+")
else
asmgen.out("""
lda ${asmArrayvarname}_lsb+$constIndex
bne +
dec ${asmArrayvarname}_msb+$constIndex
+ dec ${asmArrayvarname}_lsb+$constIndex""")
} else {
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
else
asmgen.out("""
lda ${asmArrayvarname}_lsb,x
bne +
dec ${asmArrayvarname}_msb,x
+ dec ${asmArrayvarname}_lsb,x""")
}
return
}
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, CpuRegister.X)
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")
}
}
}
}
}
}

View File

@ -32,10 +32,6 @@ internal class ProgramAndVarsGen(
internal fun generate() {
header()
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) }
@ -75,6 +71,12 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
if(compTarget.name=="c64") {
if(options.floats)
asmgen.out("PROG8_C64_BANK_CONFIG=31 ; basic+IO+kernal")
else
asmgen.out("PROG8_C64_BANK_CONFIG=30 ; IO+kernal, no basic")
}
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(".endweak")
@ -89,6 +91,12 @@ internal class ProgramAndVarsGen(
OutputType.RAW -> {
asmgen.out("; ---- raw assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
OutputType.PRG -> {
when(options.launcher) {
@ -102,26 +110,36 @@ 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")
asmgen.out("prog8_entrypoint")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
CbmPrgLauncherType.NONE -> {
// this is the same as RAW
asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
}
}
OutputType.XEX -> {
asmgen.out("; ---- atari xex program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out(" cld")
asmgen.out(" tsx ; save stackpointer for sys.exit()")
asmgen.out(" stx prog8_lib.orig_stackpointer")
if(!options.noSysInit)
asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr sys.init_system_phase2")
asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2")
}
}
@ -139,17 +157,20 @@ internal class ProgramAndVarsGen(
if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp sys.cleanup_at_exit")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr p8b_main.p8s_start | lda #31 | sta $01")
asmgen.out(" jmp sys.cleanup_at_exit")
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
"c128" -> {
asmgen.out(" jsr p8b_main.p8s_start | lda #0 | sta ${"$"}ff00")
asmgen.out(" jmp sys.cleanup_at_exit")
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
else -> {
asmgen.out(" jsr p8b_main.p8s_start")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
else -> asmgen.jmp("p8b_main.p8s_start")
}
}
@ -173,6 +194,7 @@ internal class ProgramAndVarsGen(
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 ?")
@ -264,6 +286,8 @@ internal class ProgramAndVarsGen(
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
}
}
asmgen.out(" ; memtop check")
asmgen.out(" .cerror * > ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${options.memtopAddress.toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
}
private fun block2asm(block: PtBlock) {
@ -271,12 +295,6 @@ internal class ProgramAndVarsGen(
asmgen.out("; ---- block: '${block.name}' ----")
if(block.options.address!=null)
asmgen.out("* = ${block.options.address!!.toHex()}")
else {
if(block.options.alignment==PtBlock.BlockAlignment.WORD)
asmgen.out("\t.align 2")
else if(block.options.alignment==PtBlock.BlockAlignment.PAGE)
asmgen.out("\t.align $100")
}
asmgen.out("${block.name}\t" + (if(block.options.forceOutput) ".block" else ".proc"))
asmgen.outputSourceLine(block)
@ -404,7 +422,7 @@ internal class ProgramAndVarsGen(
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 ByteDatatypes)
if(dt in ByteDatatypesWithBoolean)
asmgen.assignRegister(RegisterOrPair.A, target)
else
asmgen.assignRegister(RegisterOrPair.AY, target)
@ -450,22 +468,11 @@ internal class ProgramAndVarsGen(
}
private fun entrypointInitialization() {
asmgen.out("; program startup initialization")
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""")
// zero out the BSS area first, before setting the variable init values
// this is mainly to make sure the arrays are all zero'd out at program startup
asmgen.out(" jsr prog8_lib.program_startup_clear_bss")
// initialize block-level (global) variables at program start
blockVariableInitializers.forEach {
if (it.value.isNotEmpty())
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
@ -508,17 +515,15 @@ internal class ProgramAndVarsGen(
stringVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value"
outputStringvar(varname, it.value.second, it.value.first)
outputStringvar(varname, 0, it.value.second, it.value.first)
}
arrayVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value"
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
}
asmgen.out("""+
clv
clc""")
asmgen.out("+")
}
private class ZpStringWithInitial(
@ -539,8 +544,8 @@ internal class ProgramAndVarsGen(
for (variable in vars) {
val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationStringValue!=null)
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
if(svar?.initializationStringValue!=null)
result.add(ZpStringWithInitial(scopedName, variable.value, svar.initializationStringValue!!))
}
return result
}
@ -551,8 +556,8 @@ internal class ProgramAndVarsGen(
for (variable in vars) {
val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationArrayValue!=null)
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
if(svar?.initializationArrayValue!=null)
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.initializationArrayValue!!))
}
return result
}
@ -578,25 +583,44 @@ internal class ProgramAndVarsGen(
asmgen.out("")
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
if(varsNoInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables without initialization value")
asmgen.out("; non-zeropage variables")
asmgen.out(" .section BSS")
varsNoInit.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt }).forEach {
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt }).forEach {
uninitializedVariable2asm(it)
}
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt }).forEach {
uninitializedVariable2asm(it)
}
asmgen.out(" .send BSS")
}
if(varsWithInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables")
asmgen.out("; non-zeropage variables with init value")
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt == DataType.STR }
stringvars.forEach {
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
notAlignedStrings.forEach {
outputStringvar(
it.name,
it.onetimeInitializationStringValue!!.second,
it.onetimeInitializationStringValue!!.first
it.align,
it.initializationStringValue!!.second,
it.initializationStringValue!!.first
)
}
othervars.sortedBy { it.type }.forEach {
alignedStrings.sortedBy { it.align }.forEach {
outputStringvar(
it.name,
it.align,
it.initializationStringValue!!.second,
it.initializationStringValue!!.first
)
}
notAlignedOther.sortedBy { it.type }.forEach {
staticVariable2asm(it)
}
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
staticVariable2asm(it)
}
}
@ -604,17 +628,19 @@ internal class ProgramAndVarsGen(
private fun uninitializedVariable2asm(variable: StStaticVariable) {
when (variable.dt) {
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
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 -> {
alignVar(variable.align)
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 -> {
alignVar(variable.align)
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes")
}
@ -624,17 +650,22 @@ internal class ProgramAndVarsGen(
}
}
private fun alignVar(align: Int) {
if(align > 1)
asmgen.out(" .align ${align.toHex()}")
}
private fun staticVariable2asm(variable: StStaticVariable) {
val initialValue: Number =
if(variable.onetimeInitializationNumericValue!=null) {
if(variable.initializationNumericValue!=null) {
if(variable.dt== DataType.FLOAT)
variable.onetimeInitializationNumericValue!!
variable.initializationNumericValue!!
else
variable.onetimeInitializationNumericValue!!.toInt()
variable.initializationNumericValue!!.toInt()
} else 0
when (variable.dt) {
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
DataType.BOOL, 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")
@ -649,16 +680,19 @@ internal class ProgramAndVarsGen(
DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
in ArrayDatatypes -> {
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
}
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
alignVar(align)
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()}")
@ -726,7 +760,7 @@ 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
}
@ -748,11 +782,16 @@ internal class ProgramAndVarsGen(
.filter { it is PtAsmSub && it.address!=null }
.forEach { asmsub ->
asmsub as PtAsmSub
asmgen.out(" ${asmsub.name} = ${asmsub.address!!.toHex()}")
val address = asmsub.address!!
val bank = if(address.constbank!=null) "; @bank ${address.constbank}"
else if(address.varbank!=null) "; @bank ${address.varbank?.name}"
else ""
asmgen.out(" ${asmsub.name} = ${address.address.toHex()} $bank")
}
}
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
alignVar(align)
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
@ -763,6 +802,16 @@ 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 {

View File

@ -48,9 +48,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
val numberOfAllocatableVariables = allVariables.size
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariables.count { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
var numVariablesAllocatedInZP = 0
var numberOfNonIntegerVariables = 0
@ -86,12 +86,12 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
// no need to check for allocation error, if there is one, just allocate in normal system ram.
}
// try to allocate any other interger variables into the zeropage until it is full.
// try to allocate the "don't care" 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()) {
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
for (variable in sortedList) {
if(variable.dt in IntegerDatatypes) {
if(variable.dt in IntegerDatatypesWithBoolean) {
if(zeropage.free.isEmpty()) {
break
} else {
@ -110,9 +110,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
}
}
// println(" number of allocated vars: $numberOfAllocatableVariables")
// println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
// println(" zeropage free space: ${zeropage.free.size} bytes")
// note: no zeropage allocation is done at all for the @nozp variables. This means they will always end up outside the zeropage.
}
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {

View File

@ -18,12 +18,12 @@ internal class AnyExprAsmGen(
) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) {
in ByteDatatypes -> {
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
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 by assignOptimizedComparisonWords()")
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)
@ -35,7 +35,7 @@ internal class AnyExprAsmGen(
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
"both operands must be words"
}
return assignWordBinExpr(expr)
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
}
DataType.FLOAT -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
@ -47,30 +47,6 @@ internal class AnyExprAsmGen(
}
}
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
when(expr.operator) {
"+" -> TODO("word + at ${expr.position}")
"-" -> TODO("word - at ${expr.position}")
"*" -> TODO("word * at ${expr.position}")
"/" -> TODO("word / at ${expr.position}")
"<<" -> TODO("word << at ${expr.position}")
">>" -> TODO("word >> at ${expr.position}")
"%" -> TODO("word % at ${expr.position}")
"and" -> TODO("word 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) {
"+" -> {
@ -89,21 +65,11 @@ internal class AnyExprAsmGen(
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}")
"/" -> 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}")
"&" -> {
@ -130,24 +96,12 @@ internal class AnyExprAsmGen(
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}")
}
"==" -> 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
}
}
@ -221,17 +175,14 @@ internal class AnyExprAsmGen(
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()
}
// C64 math library has a quirk: you have always make sure FAC2/ARG is loaded last (done using CONUPK)
// otherwise the result of certain floating point operations such as FDIVT will be wrong.
// see https://www.c64-wiki.com/wiki/CONUPK
// Unfortunately this means we have to push and pop an intermediary floating point value to and from memory.
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC1, true)
asmgen.pushFAC1()
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)
asmgen.popFAC2()
}
Cx16Target.NAME -> {
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1, true)

View File

@ -14,6 +14,7 @@ internal enum class TargetStorageKind {
}
internal enum class SourceStorageKind {
LITERALBOOLEAN,
LITERALNUMBER,
VARIABLE,
ARRAY,
@ -43,8 +44,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
}
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 {
@ -133,6 +134,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null,
val number: PtNumber? = null,
val boolean: PtBool? = null,
val expression: PtExpression? = null
)
{
@ -147,6 +149,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
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) {
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
@ -194,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) {

View File

@ -5,7 +5,7 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypes -> 1
in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5
else -> 2
}
@ -58,6 +58,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
}
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") }

View File

@ -26,7 +26,8 @@ class TestCodegen: FunSpec({
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu
)
}
@ -47,13 +48,45 @@ class TestCodegen: FunSpec({
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))
sub.add(PtVariable(
"pi",
DataType.UBYTE,
ZeropageWish.DONTCARE,
0u,
PtNumber(DataType.UBYTE, 0.0, Position.DUMMY),
null,
Position.DUMMY
))
sub.add(PtVariable(
"particleX",
DataType.ARRAY_UB,
ZeropageWish.DONTCARE,
0u,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"particleDX",
DataType.ARRAY_UB,
ZeropageWish.DONTCARE,
0u,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"xx",
DataType.WORD,
ZeropageWish.DONTCARE,
0u,
PtNumber(DataType.WORD, 1.0, Position.DUMMY),
null,
Position.DUMMY
))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(Position.DUMMY).also {
val target = PtAssignTarget(false, 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))
@ -68,7 +101,7 @@ class TestCodegen: FunSpec({
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
prefixAssign.add(prefixTarget)
@ -76,7 +109,7 @@ class TestCodegen: FunSpec({
sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
@ -84,7 +117,7 @@ class TestCodegen: FunSpec({
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)

View File

@ -6,9 +6,8 @@ plugins {
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
@ -29,7 +28,7 @@ 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.18"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
}

View File

@ -6,9 +6,8 @@ plugins {
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
@ -28,9 +27,13 @@ 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.18"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
}
sourceSets {

View File

@ -14,6 +14,7 @@
<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="io.kotest.framework.datatest" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component>
</module>

View File

@ -1,10 +1,7 @@
package prog8.codegen.intermediate
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.SignedDatatypes
import prog8.code.core.SplitWordArrayTypes
import prog8.code.core.*
import prog8.intermediate.*
@ -12,8 +9,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult {
return when(call.name) {
"any" -> funcAny(call)
"all" -> funcAll(call)
"abs__byte", "abs__word", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call)
@ -22,6 +17,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> funcCallfar(call)
"callfar2" -> funcCallfar2(call)
"call" -> funcCall(call)
"msb" -> funcMsb(call)
"lsb" -> funcLsb(call)
@ -37,14 +33,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
"sort" -> funcSort(call)
"reverse" -> funcReverse(call)
"setlsb" -> funcSetLsbMsb(call, false)
"setmsb" -> funcSetLsbMsb(call, true)
"rol" -> funcRolRor(call)
"ror" -> funcRolRor(call)
"rol2" -> funcRolRor(call)
"ror2" -> funcRolRor(call)
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse/ifExpression statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse/ifExpression statement")
"prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
@ -73,10 +69,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val addressTr = exprGen.translateExpression(call.args[0])
addToResult(result, addressTr, addressTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.CALLI, reg1 = addressTr.resultReg), null)
if(call.void)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
return if(call.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else
return ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY...
}
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -92,6 +88,29 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, IRDataType.WORD, argumentwordTr.resultReg, -1)
}
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1])
val argumentA = exprGen.translateExpression(call.args[2])
val argumentX = exprGen.translateExpression(call.args[3])
val argumentY = exprGen.translateExpression(call.args[4])
val argumentCarry = exprGen.translateExpression(call.args[5])
addToResult(result, bankTr, bankTr.resultReg, -1)
addToResult(result, addressTr, addressTr.resultReg, -1)
addToResult(result, argumentA, argumentA.resultReg, -1)
addToResult(result, argumentX, argumentX.resultReg, -1)
addToResult(result, argumentY, argumentY.resultReg, -1)
addToResult(result, argumentCarry, argumentCarry.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.CALLFAR2, listOf(IRDataType.BYTE to bankTr.resultReg, IRDataType.WORD to addressTr.resultReg,
IRDataType.BYTE to argumentA.resultReg,
IRDataType.BYTE to argumentX.resultReg,
IRDataType.BYTE to argumentY.resultReg,
IRDataType.BYTE to argumentCarry.resultReg), IRDataType.WORD to addressTr.resultReg)
return ExpressionCodeResult(result, IRDataType.WORD, addressTr.resultReg, -1)
}
private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val number = call.args[0]
@ -145,79 +164,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, dt, leftTr.resultReg, -1)
}
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val result = mutableListOf<IRCodeChunkBase>()
val lengthReg = codeGen.registers.nextFree()
if(arrayName.type in SplitWordArrayTypes) {
// any(lsb-array) or any(msb-array)
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
addToResult(result, trLsb, trLsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trLsb.resultReg)
val shortcircuitLabel = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = trLsb.resultReg, immediate = 0)
it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcircuitLabel)
it += IRInstruction(Opcode.PREPARECALL, immediate = 2)
}
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
addToResult(result, trMsb, trMsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trMsb.resultReg)
addInstr(result, IRInstruction(Opcode.ORR, IRDataType.BYTE, reg1=trLsb.resultReg, reg2=trMsb.resultReg), null)
result += IRCodeChunk(shortcircuitLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, trLsb.resultReg, -1)
}
val syscall =
when (arrayName.type) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ANY_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> IMSyscall.ANY_WORD
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type")
}
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
if(arrayName.type in SplitWordArrayTypes) {
// this is a bit complicated to calculate.... have to check all recombined (lsb,msb) words for $0000
TODO("all(split words $arrayName)")
}
val syscall =
when(arrayName.type) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ALL_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> IMSyscall.ALL_WORD
DataType.ARRAY_F -> IMSyscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type")
}
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>()
@ -310,65 +256,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val lengthReg = codeGen.registers.nextFree()
val result = mutableListOf<IRCodeChunkBase>()
if(arrayName.type in SplitWordArrayTypes) {
// reverse the lsb and msb arrays both, independently
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position))
addToResult(result, trLsb, trLsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), null)
val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position))
addToResult(result, trMsb, trMsb.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
val syscall =
when(arrayName.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("weird type to reverse")
}
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val syscall =
when(arrayName.type) {
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
DataType.ARRAY_B -> IMSyscall.SORT_BYTE
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
DataType.ARRAY_W -> IMSyscall.SORT_WORD
DataType.STR -> IMSyscall.SORT_UBYTE
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw IllegalArgumentException("weird type to sort")
}
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val resultReg = codeGen.registers.nextFree()
@ -655,6 +542,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val arr = (arg as? PtArrayIndexer)
val index = arr?.index?.asConstInteger()
if(arr!=null && index!=null) {
if(arr.splitWords) TODO("IR rol/ror on split words array")
val variable = arr.variable.name
val itemsize = codeGen.program.memsizer.memorySize(arr.type)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
@ -795,10 +683,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position)
val assignTarget = PtAssignTarget(false, target.position)
assignTarget.children.add(target)
assignment.children.add(assignTarget)
assignment.children.add(PtMachineRegister(register, target.type, target.position))
assignment.children.add(PtIrRegister(register, target.type, target.position))
val result = mutableListOf<IRCodeChunkBase>()
result += codeGen.translateNode(assignment)
return result

File diff suppressed because it is too large Load Diff

View File

@ -52,22 +52,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|| 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 (ins.opcode == Opcode.CONCAT && idx>0) {
// 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) {
@ -182,8 +179,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[0]
for(ix in 1 until sub.chunks.size) {
val lastChunk = chunks.last()
val candidate = sub.chunks[ix]
when(candidate) {
when(val candidate = sub.chunks[ix]) {
is IRCodeChunk -> {
if(mayJoinCodeChunks(lastChunk, candidate)) {
lastChunk.instructions += candidate.instructions
@ -267,6 +263,24 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
}
}
if(ins.opcode== Opcode.SEI || ins.opcode== Opcode.CLI) {
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.SEI && insAfter.opcode== Opcode.CLI) {
chunk.instructions.removeAt(idx)
changed = true
}
else if(ins.opcode== Opcode.CLI && insAfter.opcode== Opcode.SEI) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
return changed
}
@ -285,7 +299,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
// remove useless RETURN
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) {
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR || ins.opcode==Opcode.RETURNI)) {
val previous = chunk.instructions[idx-1]
if(previous.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx)
@ -337,10 +351,33 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
}
}
// 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 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
@ -410,21 +447,34 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
return false
/*
var changed = false
indexedInstructions.forEach { (idx, ins) ->
if(ins.opcode==Opcode.STOREM && idx>0) {
val prev = indexedInstructions[idx-1].value
if(prev.opcode==Opcode.LOADM) {
// loadm.X rX,something | storem.X rX,something ?? -> get rid of the store.
if(ins.labelSymbol!=null && ins.labelSymbol==prev.labelSymbol && ins.labelSymbolOffset==prev.labelSymbolOffset) {
changed=true
chunk.instructions.removeAt(idx)
}
else if(ins.address!=null && ins.address==prev.address) {
changed=true
chunk.instructions.removeAt(idx)
}
}
}
// 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 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
/*
Possible other optimizations:
// detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
// detect multiple stores to the same target, only keep first (if target is not I/O memory)
// detect multiple float ffrom/fto to the same target, only keep first
// detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
// detect multiple same ands, ors; only keep first
// detect multiple registers being assigned the same value (and not changed) - use only 1 of them (hard!)
// ...
*/
}
return changed
*/
}
}

View File

@ -1,103 +0,0 @@
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)
}
*/
}

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