Compare commits

...
226 Commits
Author SHA1 Message Date
Irmen de Jong 7657edcb7d latest symboldumps 2024-12-22 09:24:45 +01:00
Irmen de Jong 580e786952 change math.crc32 to the same algorithm as pkzip/zlib uses (ISO-HDLC). Add math.crc32_end_result(). Fix a parse error in profiler.py script. 2024-12-22 09:19:54 +01:00
Irmen de Jong c0ae35b3a3 tweaks, bump version 11.0 2024-12-22 06:34:17 +01:00
Irmen de Jong c3dc74788a added diskio.get_loadaddress()
added compression.decode_tscrunch_inplace()
2024-12-22 03:17:16 +01:00
Irmen de Jong 379d241a0d various library modules now also use regular asm symbol prefixing rules: buffers, compression, cx16logo, test_stack. 2024-12-21 06:34:55 +01:00
Irmen de Jong 1f49e8fe75 in diskio.f_readline make sure AY result isn't clobbered 2024-12-21 06:25:56 +01:00
Irmen de Jong d70cfbb661 added sorting module and sortingbench example 2024-12-21 06:18:35 +01:00
Irmen de Jong 5482ac0302 simplify grammar of @tags, also improving their error message 2024-12-21 01:44:58 +01:00
Irmen de Jong 131d5ceb4f avoid re-reading all source files when sourcelines are requested in the asm 2024-12-21 00:06:18 +01:00
Irmen de Jong 512ddd1694 cleanups 2024-12-20 22:59:20 +01:00
Irmen de Jong 14a213bff9 compression module: added decode_zx0 and decode_tscrunch
two very fast decompressors while still having pretty good compression ratio
2024-12-20 20:44:57 +01:00
Irmen de Jong d586846bc5 use simpler set_screen_mode() 2024-12-19 22:17:09 +01:00
Irmen de Jong ef4efcb112 cleanup 2024-12-19 21:06:51 +01:00
Irmen de Jong b01555d75e cx16.set_screen_mode() no longer returns anything.
tweak when codegen slightly.
allow trailing comma in array literals.

set_screen_mode failure status is really uncommon and still returned by the real kernal routine screen_mode().
2024-12-19 20:56:07 +01:00
Irmen de Jong 3804fba0f1 moved jdk version config back to main gradle build file, version consistency 2024-12-19 13:39:27 +01:00
Irmen de Jong f93b7e3303 changed IR JUMPI instruction to support more indirect jump cases 2024-12-19 04:29:16 +01:00
Irmen de Jong 73baaeff1f avoid compiler crash when using char literal in str initialization
fix compiler crash when using str var in an expression without &
2024-12-18 15:08:45 +01:00
Irmen de Jong 7c79cdbd2f fix symbol prefixing on goto with expression
added coroutines example
2024-12-17 16:16:38 +01:00
Irmen de Jong 8ea032ed66 fix compiler crash on certain split array values 2024-12-17 12:31:47 +01:00
Irmen de Jong e7a0cb636c add $< and $> operators to get the lsb and msb addresses of a @split array respectively.
document the new split array things.
2024-12-16 17:45:54 +01:00
Irmen de Jong 02f3f5d0f5 @split is back to force splitting of word arrays 2024-12-16 14:51:32 +01:00
Irmen de Jong 1e9bbd662b add palette.set_rgb_nosplit() and set_rbg_be_nosplit()
fix stream-wav missing rts which corrupted playback
fix showbmx example palette and image centering
2024-12-16 02:00:51 +01:00
Irmen de Jong 8644a4ae91 more split array fixes 2024-12-15 22:54:06 +01:00
Irmen de Jong 1e85f7812f removed anyall library module altogether. The routines weren't very optimized and didn't work on split word arrays. 2024-12-15 17:45:31 +01:00
Irmen de Jong 80d88b3c61 fix many split array issues 2024-12-15 17:08:07 +01:00
Irmen de Jong d2827a7431 fix ast printer for arrays containing label addresses 2024-12-15 13:53:24 +01:00
Irmen de Jong 28c721fa7d add a split-array version for word containment check 2024-12-15 13:45:47 +01:00
Irmen de Jong 8f799567cf make word arrays split by default (w.i.p.) 2024-12-15 08:12:34 +01:00
Irmen de Jong 9e8cc8b54d goto can now accept any expression as address (instead of just a constant), and ofcourse a label name still. 2024-12-15 05:22:37 +01:00
Irmen de Jong cc59069876 allow goto to take any expression, not only an integer or an identifier (part 1) 2024-12-14 01:01:32 +01:00
Irmen de Jong 697d54e10a fix asmgen for call $3000 2024-12-13 22:33:26 +01:00
Irmen de Jong 1679ca79b4 can now use boolean params mapped to Rx register 2024-12-13 20:47:23 +01:00
Irmen de Jong 124ec77b58 update zsmkit to version 2.4, including the new on_deck routines 2024-12-13 20:15:36 +01:00
Irmen de Jong 3675d7961b boolean variables can now also be memory-mapped (including boolean arrays) 2024-12-11 18:25:27 +01:00
Irmen de Jong f8aaa2d13c explicit integer type check for @R0-R15 parameters
avoids weird type inconsistency for boolean parameters that would get aliased as unsigned byte instead invisibly
2024-12-10 23:19:41 +01:00
Irmen de Jong b7afda781a Optimize 6502 bitwise operations on word values where only the msb or lsb is touched 2024-12-10 21:42:42 +01:00
Irmen de Jong 535ec13072 improved codegen for testing for single bits: x & mask == mask 2024-12-09 04:05:00 +01:00
Irmen de Jong 26d0a174db optimize codegen for while loops with empty body 2024-12-09 03:21:20 +01:00
Irmen de Jong b2e821755c optimized palette module
removed palette.set_monochrome(), added start color index to several color set functions
removed mcf example
update gradle wrapper
2024-12-08 15:30:42 +01:00
Irmen de Jong 2e303041c1 fix crash when using undefined variable in for loop 2024-12-06 21:50:22 +01:00
Irmen de Jong 96bed8f57f tweaks 2024-12-06 00:37:16 +01:00
Irmen de Jong 86d4a4309f cleanups 2024-12-05 21:56:00 +01:00
Irmen de Jong 1a1ab0dac6 changed the data type system to composite types 2024-12-05 21:48:51 +01:00
Irmen de Jong ba8c3d14f7 diskio docs, remove super harmless warning message 2024-12-05 20:51:44 +01:00
Irmen de Jong 617ea15c3a fix failing optimization of 'not' in if statements 2024-12-04 19:03:24 +01:00
Irmen de Jong ef192a5778 easier notation for builtin function signatures by using varargs 2024-12-04 01:57:02 +01:00
Irmen de Jong 565973c520 diskio read & write routines now always reset the io channels back to the defaults before returning
This means you don't have to call CLRCHN yourself anymore inbetween if you want to do screen output or keyboard input while a file is open
2024-12-03 23:46:07 +01:00
Irmen de Jong 25b1043572 c64 diskio: Always call CLRCHN before CHKIN/CHKOUT calls
this seems to work around a Vice emulator issue when using host filesystem disk emulation.
Fixes #156
2024-12-03 19:15:44 +01:00
Irmen de Jong 1ebfff7c7b add -plaintext and -ignorefootguns options 2024-12-03 19:12:30 +01:00
Irmen de Jong 8341f9c066 diskio.status(): remove unreliable device not present error detection 2024-12-02 23:33:33 +01:00
Irmen de Jong 28cac291de diskio.f_open_w() now also resets io channels back to defaults, like f_open() already did 2024-12-02 22:25:32 +01:00
Irmen de Jong 8fa14a10e2 Optimize diskio.f_read for size=1, also improve ST check 2024-12-02 21:25:38 +01:00
Irmen de Jong 55dbd095ed fix IR codegen missing a CMPI after if not condition
fix IR codegen for containmentcheck
2024-12-02 03:06:06 +01:00
Irmen de Jong 31ad8bdd8d remove bankof(), documented msw() and lsw() 2024-12-01 21:24:26 +01:00
Irmen de Jong 181f3e9eb1 remove the unary/prefix operators ^ and << again 2024-12-01 20:50:33 +01:00
Irmen de Jong 50c3d809dc fix type casting issues and unary ^ operator
signed numbers are no longer implicitly converted to unsigned
proper range check on bankof()
2024-12-01 17:43:53 +01:00
Irmen de Jong 58f696d00a document the @R0 - @R15 register support for normal subroutine parameters 2024-11-30 20:46:31 +01:00
Irmen de Jong f603c543d3 restructure documentation to get rid of redundant syntax chapter 2024-11-30 20:26:06 +01:00
Irmen de Jong 6aaa0f928e IR: fix invalid asm name matching that resulted in not removing subs with a name matching an IR asm instruction 2024-11-30 00:10:57 +01:00
Irmen de Jong feb8aa435e monogfx, gfx_lores, gfx_hires now all uses 8kb stack from buffers module; no more broken flood fills. fill() has an extra byte parameter now where you need to pass in the ram bank to use for the stack. (not on virtual target) 2024-11-29 21:28:34 +01:00
Irmen de Jong 310e8f15cd update to latest zsmkit lib v2.2 2024-11-29 18:37:06 +01:00
Irmen de Jong da03941582 fix build 2024-11-29 01:01:59 +01:00
Irmen de Jong dcbb36a3bd update gradle wrapper version 2024-11-29 00:54:21 +01:00
Irmen de Jong 53558f5c1d add zmskit example for zsmkit v2 2024-11-29 00:04:57 +01:00
Irmen de Jong 189399d5f8 update to kotlin 2.1.0 2024-11-28 03:49:07 +01:00
Irmen de Jong 5406a992f5 improved buffers library, added to docs 2024-11-28 03:30:32 +01:00
Irmen de Jong bc9683cc54 add compression.decode_rle_vram() to decompress RLE data directly to X16's VRAM.
Document the compression library.
2024-11-26 02:06:35 +01:00
Irmen de Jong 2eed75f602 call convention for @Rx parameters, also use cpu registers if possible, like normal parameters 2024-11-25 22:22:24 +01:00
Irmen de Jong d58f9f56c4 tests for register args for normal subs
some warnings demoted into infos
2024-11-24 19:21:45 +01:00
Irmen de Jong 2e35f3c3a3 code check cleanups 2024-11-24 16:14:22 +01:00
Irmen de Jong 5c6bd9c091 register params support for normal subroutines 2024-11-24 15:56:54 +01:00
Irmen de Jong 857d2eefca added floats.interpolate(), math.interpolate(), and LERP example 2024-11-24 10:00:21 +01:00
Irmen de Jong 90f1e7fd6a ast printing fixes, added alias to syntax files 2024-11-24 07:28:33 +01:00
Irmen de Jong 18e37accf9 improve detection of register re-use in parameters 2024-11-24 05:27:43 +01:00
Irmen de Jong cc53d698bf added msw() and lsw() builtin functions (experimental) 2024-11-24 03:53:37 +01:00
Irmen de Jong cb86206698 added unary ^ and << operators (experimental) (gets bank and address of a long integer) 2024-11-24 03:07:18 +01:00
Irmen de Jong d77b1944fb rename bnk() to bankof() 2024-11-24 00:53:09 +01:00
Irmen de Jong a58cb43c4a fixed weird error messages when attempting to create variable with type long 2024-11-23 21:35:57 +01:00
Irmen de Jong 88574c87c4 convert vtui and zsmkit to new extsub address expression capability 2024-11-23 21:21:52 +01:00
Irmen de Jong 3a7a7091c0 update some docs 2024-11-23 21:01:18 +01:00
Irmen de Jong 906b137a7c renamed 'string' module to 'strings' for consistency 2024-11-23 15:51:38 +01:00
Irmen de Jong 42e2c5f605 fix some deprecated code in tests
silence redundant error about unused txt block
2024-11-23 15:48:18 +01:00
Irmen de Jong cc13a51493 fix import order problem related to %option merge 2024-11-23 12:15:15 +01:00
Irmen de Jong f569ce6141 setting a byte >=128 or word >=32768 now results in an out-of-range error, instead of an invalid casted value 2024-11-22 21:24:04 +01:00
Irmen de Jong 4958463e75 moved floats.MIN/MAX to sys.MIN_FLOAT/MAX_FLOAT
added txt.print_f as alias to floats.print
2024-11-22 00:46:23 +01:00
Irmen de Jong 2360625927 added min/max values for the various integer types as sys.MAX_XXX and sys.MIN_XXX
renamed sys.sizeof_xxx into sys.SIZEOF_XXX to be consistent with the uppercasing of the other constants
2024-11-21 23:25:02 +01:00
Irmen de Jong 8badc40883 added several float limits contants such as floats.EPSILON, E, MIN, MAX
fix VM float min max limits
2024-11-21 23:25:02 +01:00
Irmen de Jong 844c97930f fix Antlr grammar build and convert final build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
Irmen de Jong 5c09dc10ae convert build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
Irmen de Jong 9fd9e9ab5f change block sort order so that blocks with address are now sorted last 2024-11-20 23:23:26 +01:00
Irmen de Jong 35c477b5a6 Make extsub address a (constant) expression instead of a numeric literal
this makes it easier to define API jump tables
2024-11-20 23:23:26 +01:00
Irmen de Jong ae0cadb383 added bnk() builtin function 2024-11-20 23:23:21 +01:00
Irmen de Jong 984230e8fa removed txt.VERA_TEXTMATRIX_BANK/VERA_TEXTMATRIX_ADDR it's now just txt.VERA_TEXTMATRIX (long const) 2024-11-20 23:22:56 +01:00
Irmen de Jong a874aec6a1 implementing const long 2024-11-20 23:22:56 +01:00
Irmen de Jong ea1daa97d3 remove the 'addmissingrts' compiler option 2024-11-20 23:22:56 +01:00
Irmen de Jong fb0d9b46b0 remove 'romsub' as a recognised alternative for 'extsub' 2024-11-20 23:22:56 +01:00
Irmen de Jong 9da70bdf05 simplify ReturnConvention a little 2024-11-20 23:22:56 +01:00
Irmen de Jong d640cfbe13 removed BuiltinFunctionCallStatement redundant ast node type 2024-11-20 23:22:56 +01:00
Irmen de Jong 51a05ec4b7 removed BuiltinFunctionCall redundant ast node type 2024-11-20 23:22:56 +01:00
Irmen de Jong 1f5706bbeb version 10.5.1 2024-11-20 22:54:26 +01:00
Irmen de Jong 25c9b2fea4 remove an archaic machine code monitor bank setting at program exit
what did it even do?  in any case, $2d is just a user zero page location it should no longer be associated with the monitor nowadays.
2024-11-20 19:28:31 +01:00
Irmen de Jong 154f9b300f fix crash: byte c = if a < b -1 else 1 "both values should be the same type" 2024-11-19 23:46:25 +01:00
Irmen de Jong d78ce77536 improve vm error message when referencing a block name 2024-11-19 20:57:58 +01:00
Irmen de Jong b4fb43bc80 fix the if not check in ir codegen 2024-11-18 22:33:47 +01:00
Irmen de Jong e0e01f794e fix dt compiler crash 2024-11-17 17:39:36 +01:00
Irmen de Jong 08865dbb4e todo 2024-11-16 02:10:57 +01:00
Irmen de Jong b9ad7e0e55 forgot to mention floats 2024-11-15 23:37:08 +01:00
Irmen de Jong 07158a6f1a improve manual about subroutine call convention 2024-11-15 22:59:47 +01:00
Irmen de Jong 957c42bc1d tweak 2024-11-15 02:52:21 +01:00
Irmen de Jong f784da2da6 fix asm optimization regression caused by wrong label prefix comparison 2024-11-14 21:24:46 +01:00
Irmen de Jong c080fbe59a target machine config tweak and fix possible compiler crash on wrong type name 2024-11-13 21:04:46 +01:00
Irmen de Jong d70b8303b1 added sprites.reset() to remove sprites from the screen 2024-11-13 20:26:04 +01:00
Irmen de Jong 1d38c3582a progstart() added to complement progend() 2024-11-13 19:29:50 +01:00
Irmen de Jong 9438e996d7 Fixed math.mul16_last_upper().
Added math.lerpw() a LERP routine for words (to complement the existing math.lerp() for bytes)
Described the LERP routines in the library chapter in the docs.
2024-11-12 18:31:24 +01:00
Irmen de Jong 3b4a5e27f7 renamed gfx_hires4 module to just gfx_hires
to be consistent with gfx_lores
2024-11-12 17:48:35 +01:00
Irmen de Jong 648d9fc269 todo 2024-11-12 00:57:10 +01:00
Irmen de Jong 54fccec7d7 now also support using defer inside if statements 2024-11-12 00:11:19 +01:00
Irmen de Jong 4f9693055e fix compiler crash when extsub has both FAC1 and FAC2 float parameters 2024-11-11 20:48:25 +01:00
Irmen de Jong 555c50ee10 scripts/cx16_images : added an option to keep only the first palette entry fixed to a given color (such as, black.) 2024-11-11 19:27:06 +01:00
Irmen de Jong bf98ceca2c make repeat support 65536 iterations 2024-11-11 01:58:27 +01:00
Irmen de Jong 573cecb087 make memtop adjust automatically when you use %address larger than the default memtop setting. 2024-11-10 23:44:10 +01:00
Irmen de Jong 1b528491c2 make %memtop exclusive i.e. the first address NOT to use (like kernal MEMTOP) 2024-11-10 23:35:25 +01:00
Irmen de Jong 4bdabe1961 move shared cbm diskio to its own file (c64/c128) so that pet/atari/neo targets give better error message when trying to import non existing diskio module there
sorted the command line options alphabetically
2024-11-10 16:38:49 +01:00
Irmen de Jong a3fa527378 move shared cbm diskio to its own file (c64/c128) so that pet/atari/neo targets give better error message when trying to import non existing diskio module there 2024-11-10 15:35:36 +01:00
Irmen de Jong 84f5ffa426 fix generated labels prefix and filtering in the vice symbol dump file 2024-11-10 15:34:35 +01:00
Irmen de Jong 25d2b42283 textelite now with sysinit, so it runs on the c128 as well (needs banking) 2024-11-09 16:03:16 +01:00
Irmen de Jong 300d1a871c c128 banks out basic, added banks() and getbanks() 2024-11-09 15:44:08 +01:00
Irmen de Jong 2fcb83a39f version 10.5 2024-11-09 14:04:04 +01:00
Irmen de Jong 3ba1d00a7c add unit test for @dirty variables 2024-11-09 13:31:54 +01:00
Irmen de Jong 64164c1c72 changed @initonce to @dirty and meaning is now: not initialized at all. 2024-11-08 22:05:31 +01:00
Irmen de Jong 3ee6058524 todo 2024-11-08 19:57:38 +01:00
Irmen de Jong 93a0a41e73 Merge branch 'initonce-var-tag'
# Conflicts:
#	examples/test.p8
2024-11-08 19:32:30 +01:00
Irmen de Jong e7ab7b6d7a neo skeletons added in docs 2024-11-08 19:19:11 +01:00
Irmen de Jong 7d4dc3c063 update 2024-11-08 19:04:51 +01:00
Irmen de Jong a50400b7d1 initial neo6502 target 2024-11-08 19:04:49 +01:00
Irmen de Jong f89f1a84d0 @initonce variable tag to skip variable reinitialization 2024-11-08 19:03:48 +01:00
Irmen de Jong 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
Irmen de Jong b88f550c5b todo 2024-11-07 00:48:13 +01:00
Irmen de Jong 9864abd393 romsub keyword is now extsub 2024-11-06 22:14:53 +01:00
Irmen de Jong c702c4a6df internal rename of romsub to extsub 2024-11-06 21:42:16 +01:00
Irmen de Jong 77e376f6bf romsub @bank now also accepts a variable so the bank can be dynamic 2024-11-06 00:02:36 +01:00
Irmen de Jong 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
Irmen de Jong a5c7393561 tweaking program startup and cleanup stuff 2024-11-05 21:12:27 +01:00
Irmen de Jong 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
Irmen de Jong 459e9f8f3b jsrfar stuff 2024-11-05 01:06:06 +01:00
Irmen de Jong 5b1143bcb3 C64: add support for calling romsub with bank ('jsrfar') 2024-11-04 23:26:21 +01:00
Irmen de Jong 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
Irmen de Jong e514eeba17 added c64.banks() and c64.getbanks() and c64 banking example 2024-11-04 20:14:30 +01:00
Irmen de Jong c11a52b278 added cx16 banking example 2024-11-03 21:52:04 +01:00
Irmen de Jong 85e87dfe2e consolidate @rombank and @rambank into just @bank 2024-11-03 21:15:11 +01:00
Irmen de Jong cb47e2c149 documented the romsub bank additions 2024-11-03 20:39:44 +01:00
Irmen de Jong 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
Irmen de Jong 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
Irmen de Jong 178e60bba0 fix ast source gen for romsub 2024-11-03 15:04:53 +01:00
Irmen de Jong 9f84aa5fb2 fix double %option merge problem where it deleted all of the blocks 2024-11-03 13:36:14 +01:00
Irmen de Jong 66fc109ce5 correct program name in help 2024-11-02 22:16:57 +01:00
Irmen de Jong a231872821 tip for using aliases for the virtual registers r0-r15 2024-11-02 22:09:20 +01:00
Irmen de Jong 7cfb33a448 tweak & fix if expression with word condition 2024-11-02 22:01:57 +01:00
Irmen de Jong 3b798097b9 added memtop to machine definition and asm source code check
added %memtop directive
2024-11-02 00:59:07 +01:00
Irmen de Jong 6fb05bdefc replaced deprecated cx16 ZSOUND example by new ZSMKIT examples 2024-11-01 23:17:23 +01:00
Irmen de Jong 64ea72ed4d tweak plot 2024-11-01 21:56:27 +01:00
Irmen de Jong 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
Irmen de Jong 925b9d845d fix split array possible compiler loop (due to wrong datatype replacement) 2024-11-01 19:18:03 +01:00
Irmen de Jong ad074076c2 remove last references to gfx2 module 2024-11-01 18:41:36 +01:00
Irmen de Jong a2194c43a6 fix benchmark 2024-11-01 03:50:13 +01:00
Irmen de Jong 4b23b1dc86 don't always import math automatically anymore 2024-11-01 03:39:52 +01:00
Irmen de Jong 9005c7994a added Linear Interpolation (LERP) functions: math.lerp(), floats.lerp(), floats.lerp_fast() 2024-11-01 02:05:48 +01:00
Irmen de Jong 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
Irmen de Jong 09cbdf410a added diskio.exists(), made f_close_w() idempotent like f_close() already was 2024-10-31 21:25:22 +01:00
Irmen de Jong df6a43c7f0 gfx_lores now has drawmode_eor() (used by Paint, for example) 2024-10-31 01:28:29 +01:00
Irmen de Jong 4ce130dc8b split up cx16.gfx2 module into gfx_lores and gfx_hires4 modules 2024-10-30 22:21:07 +01:00
Irmen de Jong 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
Irmen de Jong 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
Irmen de Jong 66b06d6c40 added gfx2.safe_vertical_line, gfx2.safe_rect, gfx2.safe_fillrect for completeness 2024-10-30 19:03:40 +01:00
Irmen de Jong eeeb8d81f4 merge now also allows monkeypatching if signature is 100% identical 2024-10-30 01:15:56 +01:00
Irmen de Jong 6f727aff88 fix beanshell compile with jdk11 2024-10-29 23:42:37 +01:00
Irmen de Jong 518e5a30c2 slight parser rule tweak 2024-10-29 23:18:17 +01:00
Irmen de Jong bbba4b3d60 new block merge semantics and implementation 2024-10-29 22:57:54 +01:00
Irmen de Jong 967adb9a87 Merge branch 'beanshell' 2024-10-29 20:55:09 +01:00
Irmen de Jong 040a6c62de added a beanshell interpreter experiment 2024-10-29 20:52:41 +01:00
Irmen de Jong 483d193ced vm: implemented reading/writing files in diskio 2024-10-29 02:34:53 +01:00
Irmen de Jong 62458216c9 first skeleton of LSP language server 2024-10-28 21:42:20 +01:00
Irmen de Jong 76b05cb5fd fix chained aliasing 2024-10-28 18:35:23 +01:00
Irmen de Jong 570b574b93 added sys.memcmp 2024-10-28 00:41:26 +01:00
Irmen de Jong a82f211f9a added alias statement 2024-10-28 00:36:10 +01:00
Irmen de Jong 504c80cddf fix parser rule for identifiers (void is a keyword, not an identifier) 2024-10-27 15:57:27 +01:00
Irmen de Jong 4b4af9b527 no longer silently add RTS to asmsubs that don't have one 2024-10-27 13:49:00 +01:00
Irmen de Jong 28b383f888 docs and syntax for @alignxxx and %align 2024-10-27 00:47:52 +02:00
Irmen de Jong 40ce7725a1 cleanup c64 sprite examples 2024-10-26 21:36:11 +02:00
Irmen de Jong 1f2d46628e remove %option align_xxx (block level alignment, as we now have better alternatives) 2024-10-26 21:18:34 +02:00
Irmen de Jong c9535049c8 %align directive and @align64 2024-10-26 20:58:35 +02:00
Irmen de Jong 9317cf8a35 sorting aligned vars to shrink prg size 2024-10-26 18:33:51 +02:00
Irmen de Jong 1cd754f05d adding @alignword/page on individual variables 2024-10-26 17:00:38 +02:00
Irmen de Jong 97b8cb748d more ifexpression codegen tweaks 2024-10-25 22:52:26 +02:00
Irmen de Jong 84d9040b57 make BIT test also work on signed byte variables. Fixed an address-of optimization error. 2024-10-23 22:34:18 +02:00
Irmen de Jong fdd18c615c more ifexpresssion codegen tweaks 2024-10-23 21:04:55 +02:00
Irmen de Jong c14f6cfc2b more optimal if expression code 2024-10-22 23:49:24 +02:00
Irmen de Jong 326eab3dd1 unit test for defer, describe defer and if expression in docs 2024-10-22 22:19:49 +02:00
Irmen de Jong 6da1f7eb4c don't remove essential subroutines even though they seem unused 2024-10-22 21:17:02 +02:00
Irmen de Jong 1e82483152 ast printer correctly prints ifexpression 2024-10-22 21:14:55 +02:00
Irmen de Jong 6e2fd41a8b ast printer correctly prints unroll and continue 2024-10-22 21:14:26 +02:00
Irmen de Jong 9927af1095 about var inits 2024-10-22 01:08:42 +02:00
Irmen de Jong 7585b6ef6f fix issues with calling the defer handler 2024-10-21 19:49:38 +02:00
Irmen de Jong 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
Irmen de Jong 0247fb0d84 some ast2 var tweaks 2024-10-21 00:20:54 +02:00
Irmen de Jong 6de760885f fix defer push/pop typecasting issues 2024-10-19 21:45:49 +02:00
Irmen de Jong 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
Irmen de Jong d5fc69d3e4 fix instruction index error in optimizer 2024-10-19 14:45:53 +02:00
Irmen de Jong a40d120f2a more defer sanity checks 2024-10-18 22:32:49 +02:00
Irmen de Jong 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
Irmen de Jong 272a1001a8 fix bad optimization of floats.pop/push call 2024-10-18 21:04:18 +02:00
Irmen de Jong 2a52241f1c defer is now done *after* calculating a return value 2024-10-18 20:56:27 +02:00
Irmen de Jong d8f1822c12 fixes 2024-10-18 20:32:46 +02:00
Irmen de Jong ce7d094adb Zig-like "defer" to clean up stuff when leaving the scope of the current routine. 2024-10-18 01:30:20 +02:00
Irmen de Jong a0cf1889a3 omit more redundant 0-initializations ("stz's") 2024-10-17 22:51:39 +02:00
Irmen de Jong 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
Irmen de Jong 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
Irmen de Jong 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
Irmen de Jong 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
Irmen de Jong 65ddcf91d0 remove unused syscalls 2024-10-15 18:24:55 +02:00
Irmen de Jong 5280e1b449 err msgs 2024-10-13 21:33:13 +02:00
Irmen de Jong b6ffb81909 Merge branch 'next-version' 2024-10-13 21:20:10 +02:00
Irmen de Jong 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
Irmen de Jong 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
Irmen de Jong 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
Irmen de Jong 7a0eaf3148 Remove array initialization by single value.
New compiler and kotlin version.
2024-10-13 04:31:56 +02:00
396 changed files with 22382 additions and 17323 deletions

No files matched your search

+4 -2
View File
@@ -1,9 +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
@@ -12,7 +15,6 @@ output/
*.vm.txt
*.vice-mon-list
docs/build
out/
parser/**/*.interp
parser/**/*.tokens
parser/**/*.java
@@ -23,6 +25,7 @@ compiler/src/prog8/buildversion/*
.eggs/
/MANIFEST
.tox/
.kotlin/
__pycache__/
parser.out
parsetab.py
@@ -31,7 +34,6 @@ parsetab.py
compiler/lib/
.gradle
/prog8compiler.jar
sd*.img
*.d64
+13 -3
View File
@@ -1,9 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="11" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.24" />
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.1" />
<option name="languageVersion" value="2.1" />
</component>
</project>
<component name="KotlinCompilerSettings">
<option name="additionalArguments" value="-Xwhen-guards" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.0" />
</component>
</project>
+10 -10
View File
@@ -1,23 +1,23 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.20" />
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.20/kotlin-stdlib-jdk8-2.0.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.20/kotlin-stdlib-2.0.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.20/kotlin-stdlib-jdk7-2.0.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.20/kotlin-stdlib-jdk8-2.0.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.20/kotlin-stdlib-2.0.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.20/kotlin-stdlib-jdk7-2.0.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.20/kotlin-stdlib-jdk8-2.0.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.20/kotlin-stdlib-2.0.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.20/kotlin-stdlib-jdk7-2.0.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-sources.jar!/" />
</SOURCES>
</library>
</component>
+13
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>
+2 -2
View File
@@ -4,13 +4,13 @@
<option name="perGrammarGenerationSettings">
<list>
<PerGrammarGenerationSettings>
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" />
<option name="fileName" value="$PROJECT_DIR$/parser/src/main/antlr/Prog8ANTLR.g4" />
<option name="autoGen" value="true" />
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
<option name="libDir" value="" />
<option name="encoding" value="" />
<option name="pkg" value="" />
<option name="language" value="" />
<option name="language" value="Java" />
<option name="generateListener" value="false" />
</PerGrammarGenerationSettings>
</list>
+4 -2
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" />
@@ -12,10 +14,10 @@
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.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$/benchmark-program/benchmark-program.iml" filepath="$PROJECT_DIR$/benchmark-program/benchmark-program.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>
</project>
</project>
+2 -2
View File
@@ -4,9 +4,9 @@
all:
gradle installdist installshadowdist
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"
@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/p8compile"
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/prog8c"
+6 -2
View File
@@ -66,11 +66,14 @@ What does Prog8 provide?
- 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
- ``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
- 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 (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:*
@@ -81,11 +84,12 @@ What does Prog8 provide?
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- "c64": Commodore-64 (6502 like CPU)
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- "pet32": Commodore PET (experimental)
- "pet32": Commodore PET (limited support)
- "atari": Atari 8 bit such as 800XL (experimental)
- "neo": Neo6502 (experimental)
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<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="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>
+65
View File
@@ -0,0 +1,65 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("application")
kotlin("jvm")
}
val serverMainClassName = "prog8lsp.MainKt"
val applicationName = "prog8-beanshell"
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")
}
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.installDist {
finalizedBy("fixFilePermissions")
}
tasks.build {
finalizedBy("installDist")
}
Binary file not shown.
@@ -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
}
}
}
+23
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
))
}
+4 -6
View File
@@ -1,12 +1,10 @@
.PHONY: all clean emu
.PHONY: clean run
all: benchmark.prg
run:
prog8c -target cx16 benchmark.p8
x16emu -run -prg benchmark.prg -warp
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
p8compile $< -target cx16
+6 -6
View File
@@ -64,14 +64,14 @@ rotate3d {
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 ]
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] 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
word[len(xcoor)] rotatedx
word[len(ycoor)] rotatedy
word[len(zcoor)] rotatedz
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
; rotate around origin (0,0,0)
+3 -3
View File
@@ -25,8 +25,8 @@ adpcm {
; 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 = [
byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
uword[] 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,
@@ -79,7 +79,7 @@ adpcm {
; elif predicted < -32767:
; predicted = - 32767
index += t_index[nibble]
index += t_index[nibble] as ubyte
if_neg
index = 0
else if index >= len(t_step)-1
+7 -7
View File
@@ -1,11 +1,11 @@
%import gfx2
%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
uword[MAX_NUM_CIRCLES] circle_x
uword[MAX_NUM_CIRCLES] circle_y
ubyte[MAX_NUM_CIRCLES] circle_radius
ubyte color
uword total_num_circles
@@ -14,7 +14,7 @@ circles {
if use_kernal
void cx16.set_screen_mode(128)
else
gfx2.screen_mode(1)
gfx_lores.graphics_mode()
math.rndseed(12345,6789)
cbm.SETTIM(0,0,0)
@@ -28,14 +28,14 @@ circles {
cx16.GRAPH_clear()
}
else
gfx2.clear_screen(0)
gfx_lores.clear_screen(0)
total_num_circles += draw_circles(use_kernal, max_time)
}
if use_kernal
void cx16.set_screen_mode(3)
else {
gfx2.screen_mode(0)
gfx_lores.text_mode()
}
return total_num_circles
@@ -66,7 +66,7 @@ circles {
cx16.GRAPH_draw_oval(x-radius, y-radius, radius*2, radius*2, true)
}
else
gfx2.disc(x, y as ubyte, radius, color)
gfx_lores.disc(x, y as ubyte, radius, color)
circle_x[num_circles] = x
circle_y[num_circles] = y
circle_radius[num_circles] = radius
+43 -44
View File
@@ -1,7 +1,6 @@
%import textio
%import conv
%import string
%import string
%import strings
textelite {
@@ -172,7 +171,7 @@ textelite {
sub next_input(str buffer) -> ubyte {
input_index++
return string.copy(inputs[input_index], buffer)
return strings.copy(inputs[input_index], buffer)
}
}
@@ -571,7 +570,7 @@ elite_galaxy {
ubyte distance = elite_planet.distance(px, py)
if distance <= max_distance {
elite_planet.name = make_current_planet_name()
elite_planet.name[0] = string.upperchar(elite_planet.name[0])
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
uword tx = elite_planet.x
uword ty = elite_planet.y
if local {
@@ -743,42 +742,42 @@ elite_galaxy {
}
elite_planet {
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
str[] words91 = ["planet", "world", "place", "little planet", "dump"]
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"]
str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""]
str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"]
str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"]
str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"]
str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
uword[] @shared wordlists = [
words81, words82, words83, words84, words85, words86, words87, words88,
@@ -840,7 +839,7 @@ elite_planet {
}
}
randname[nx] = 0
randname[0] = string.upperchar(randname[0])
randname[0] = strings.upperchar(randname[0])
return randname
}
@@ -912,12 +911,12 @@ elite_planet {
source_ptr = source_stack[stack_ptr]
} else {
if c == $b0 {
@(result_ptr) = string.upperchar(name[0])
@(result_ptr) = strings.upperchar(name[0])
result_ptr++
concat_string(&name + 1)
}
else if c == $b1 {
@(result_ptr) = string.upperchar(name[0])
@(result_ptr) = strings.upperchar(name[0])
result_ptr++
ubyte ni
for ni in 1 to len(name) {
@@ -981,7 +980,7 @@ elite_util {
if pc == 0
return true
; to lowercase for case insensitive compare:
if string.lowerchar(pc)!=string.lowerchar(sc)
if strings.lowerchar(pc)!=strings.lowerchar(sc)
return false
prefixptr++
stringptr++
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/compiled" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+1 -1
View File
@@ -62,7 +62,7 @@ main {
benchmark_score[benchmark_number] = adpcm.decode_benchmark(300)
benchmark_number++
announce_benchmark("circles with gfx2")
announce_benchmark("circles with gfx_lores")
benchmark_score[benchmark_number] = circles.draw(false, 300)
benchmark_number++
-10
View File
@@ -1,10 +0,0 @@
plugins {
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
+26
View File
@@ -0,0 +1,26 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm") version "2.1.0"
}
allprojects {
apply(plugin="kotlin")
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
compilerOptions {
freeCompilerArgs = listOf("-Xwhen-guards")
jvmTarget = JvmTarget.JVM_11
}
}
java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11
}
}
-42
View File
@@ -1,42 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
// should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!
+24
View File
@@ -0,0 +1,24 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
// should have no dependencies to other modules
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
}
// note: there are no unit tests in this module!
+54 -27
View File
@@ -1,10 +1,14 @@
package prog8.code
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtNode
import prog8.code.ast.PtProgram
import prog8.code.core.*
const val internedStringsModuleName = "prog8_interned_strings"
/**
* Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
@@ -97,7 +101,7 @@ enum class StNodeType {
// MODULE, // not used with current scoping rules
BLOCK,
SUBROUTINE,
ROMSUB,
EXTSUB,
LABEL,
STATICVAR,
MEMVAR,
@@ -178,37 +182,54 @@ 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 || dt==DataType.BOOL)
if(initializationNumericValue!=null) {
require(dt.isNumericOrBool)
}
if(onetimeInitializationArrayValue!=null) {
require(dt in ArrayDatatypes)
require(length==onetimeInitializationArrayValue.size)
if(initializationArrayValue!=null) {
require(dt.isArray)
require(length == initializationArrayValue.size)
}
if(onetimeInitializationStringValue!=null) {
require(dt == DataType.STR)
require(length == onetimeInitializationStringValue.first.length+1)
if(initializationStringValue!=null) {
require(dt.isString)
require(length == initializationStringValue.first.length + 1)
}
if(align > 0) {
require(dt.isString || dt.isArray)
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
}
}
}
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode) :
StNode(name, StNodeType.CONSTANT, astNode)
@@ -220,8 +241,8 @@ 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(!dt.isString)
if(dt.isStringly && !dt.isWord)
requireNotNull(length)
}
}
@@ -239,18 +260,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?, val boolean: Boolean?)
class StSubroutineParameter(val name: String, val type: DataType, val register: RegisterOrPair?)
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>
+21 -15
View File
@@ -3,7 +3,6 @@ package prog8.code
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.VMTarget
import kotlin.collections.ArrayDeque
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
@@ -11,7 +10,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
val st = SymbolTable(program)
BuiltinFunctions.forEach {
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
val dt = DataType.forDt(it.value.returnType ?: BaseDataType.UNDEFINED)
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, dt, Position.DUMMY)))
}
val scopestack = ArrayDeque<StNode>()
@@ -23,10 +23,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
if(options.compTarget.name != VMTarget.NAME) {
listOf(
PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE), options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD), options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD), options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
).forEach {
it.parent = program
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
@@ -39,15 +39,16 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
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)
}
is PtConstant -> {
StConstant(node.name, node.type, node.value, node)
require(node.type.isNumericOrBool)
StConstant(node.name, node.type.base, node.value, node)
}
is PtLabel -> {
StNode(node.name, StNodeType.LABEL, node)
@@ -56,7 +57,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
}
is PtSub -> {
val params = node.parameters.map {StSubroutineParameter(it.name, it.type) }
val params = node.parameters.map {StSubroutineParameter(it.name, it.type, it.register) }
StSub(node.name, params, node.returntype, node)
}
is PtVariable -> {
@@ -66,23 +67,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()
}
}
@@ -95,7 +99,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") {
@@ -131,7 +138,6 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
TODO("address-of array element $it in initial array value")
StArrayElement(null, it.identifier.name, null)
}
is PtIdentifier -> StArrayElement(null, it.name, null)
is PtNumber -> StArrayElement(it.number, null, null)
is PtBool -> StArrayElement(null, null, it.value)
else -> throw AssemblyError("invalid array element $it")
+11 -10
View File
@@ -3,7 +3,7 @@ package prog8.code.ast
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.core.Position
import prog8.code.core.SourceCode
import prog8.code.source.SourceCode
import java.nio.file.Path
// New simplified AST for the code generator.
@@ -80,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)
}
@@ -103,12 +96,20 @@ class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Positi
}
class PtLabel(name: String, position: Position) : PtNamedNode(name, position)
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
companion object {
// all automatically generated labels everywhere need to have the same label name prefix:
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
}
}
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)
+63 -42
View File
@@ -1,7 +1,7 @@
package prog8.code.ast
import prog8.code.core.*
import java.util.Objects
import java.util.*
import kotlin.math.abs
import kotlin.math.truncate
@@ -9,7 +9,7 @@ import kotlin.math.truncate
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init {
if(type==DataType.UNDEFINED) {
if(type.isUndefined) {
@Suppress("LeakingThis")
when(this) {
is PtBuiltinFunctionCall -> { /* void function call */ }
@@ -66,32 +66,31 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
}
infix fun isSameAs(target: PtAssignTarget): Boolean {
return when {
target.memory != null && this is PtMemoryByte-> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
infix fun isSameAs(target: PtAssignTarget): Boolean = when {
target.memory != null && this is PtMemoryByte -> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
}
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
fun asConstValue(): Double? = (this as? PtNumber)?.number ?: (this as? PtBool)?.asInt()?.toDouble()
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
is PtBuiltinFunctionCall -> {
when (name) {
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
else -> false
}
}
@@ -106,6 +105,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtRange -> true
is PtString -> true
is PtTypeCast -> value.isSimple()
is PtIfExpression -> condition.isSimple() && truevalue.isSimple() && falsevalue.isSimple()
}
}
@@ -137,7 +137,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
*/
}
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
class PtAddressOf(position: Position, val isMsbForSplitArray: Boolean=false) : PtExpression(DataType.forDt(BaseDataType.UWORD), position) {
val identifier: PtIdentifier
get() = children[0] as PtIdentifier
val arrayIndexExpr: PtExpression?
@@ -154,15 +154,16 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
val index: PtExpression
get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
get() = variable.type.isSplitWordArray
init {
require(elementType in NumericDatatypesWithBoolean)
require(elementType.isNumericOrBool)
}
}
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)
@@ -182,7 +183,7 @@ class PtBuiltinFunctionCall(val name: String,
position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
require(!type.isUndefined)
}
val args: List<PtExpression>
@@ -198,14 +199,24 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
init {
if(operator in ComparisonOperators + LogicalOperators)
require(type==DataType.BOOL)
require(type.isBool)
else
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
require(!type.isBool) { "no bool allowed for this operator $operator"}
}
}
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
class PtIfExpression(type: DataType, position: Position): PtExpression(type, position) {
val condition: PtExpression
get() = children[0] as PtExpression
val truevalue: PtExpression
get() = children[1] as PtExpression
val falsevalue: PtExpression
get() = children[2] as PtExpression
}
class PtContainmentCheck(position: Position): PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
val needle: PtExpression
get() = children[0] as PtExpression
val haystackHeapVar: PtIdentifier?
@@ -214,8 +225,8 @@ class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, positi
get() = children[1] as? PtArray
companion object {
val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
const val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
const val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
}
}
@@ -226,6 +237,13 @@ class PtFunctionCall(val name: String,
position: Position) : PtExpression(type, position) {
val args: List<PtExpression>
get() = children.map { it as PtExpression }
init {
if(void) require(type.isUndefined) {
"void fcall should have undefined datatype"
}
// note: non-void calls can have UNDEFINED type: is if they return more than 1 value
}
}
@@ -238,13 +256,13 @@ class PtIdentifier(val name: String, type: DataType, position: Position) : PtExp
}
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
class PtMemoryByte(position: Position) : PtExpression(DataType.forDt(BaseDataType.UBYTE), position) {
val address: PtExpression
get() = children.single() as PtExpression
}
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
override fun hashCode(): Int = Objects.hash(type, value)
override fun equals(other: Any?): Boolean {
@@ -259,21 +277,29 @@ class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOO
}
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
class PtNumber(type: BaseDataType, val number: Double, position: Position) : PtExpression(DataType.forDt(type), position) {
companion object {
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
PtNumber(BaseDataType.UBYTE, if(bool) 1.0 else 0.0, position)
}
init {
if(type==DataType.BOOL)
if(type==BaseDataType.BOOL)
throw IllegalArgumentException("use PtBool instead")
if(type!=DataType.FLOAT) {
if(type!=BaseDataType.FLOAT) {
val trunc = truncate(number)
if (trunc != number)
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
}
when(type) {
BaseDataType.UBYTE -> require(number in 0.0..255.0)
BaseDataType.BYTE -> require(number in -128.0..127.0)
BaseDataType.UWORD -> require(number in 0.0..65535.0)
BaseDataType.WORD -> require(number in -32728.0..32767.0)
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
else -> require(type.isNumeric) { "numeric literal type should be numeric: $type" }
}
}
override fun hashCode(): Int = Objects.hash(type, number)
@@ -281,7 +307,7 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
override fun equals(other: Any?): Boolean {
return if(other==null || other !is PtNumber)
false
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
else if(!type.isBool && !other.type.isBool)
number==other.number
else
type==other.type && number==other.number
@@ -298,7 +324,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
get() = children.single() as PtExpression
init {
require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" }
require(operator in PrefixOperators) { "invalid prefix operator: $operator" }
}
}
@@ -330,18 +356,17 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
val fromLv = from as? PtNumber
val toLv = to as? PtNumber
val stepLv = step as? PtNumber
if(fromLv==null || toLv==null || stepLv==null)
if(fromLv==null || toLv==null)
return null
val fromVal = fromLv.number.toInt()
val toVal = toLv.number.toInt()
val stepVal = stepLv.number.toInt()
val stepVal = step.number.toInt()
return makeRange(fromVal, toVal, stepVal)
}
}
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.forDt(BaseDataType.STR), position) {
override fun hashCode(): Int = Objects.hash(value, encoding)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtString)
@@ -351,7 +376,7 @@ class PtString(val value: String, val encoding: Encoding, position: Position) :
}
class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) {
class PtTypeCast(type: BaseDataType, position: Position) : PtExpression(DataType.forDt(type), position) {
val value: PtExpression
get() = children.single() as PtExpression
}
@@ -359,7 +384,3 @@ 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 PtIrRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
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
+55 -37
View File
@@ -1,34 +1,41 @@
package prog8.code.ast
import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.escape
import prog8.code.core.toHex
/**
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
* passing it as a String to the specified receiver function.
*/
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
fun type(dt: DataType) = "!${dt}!"
fun txt(node: PtNode): String {
return when(node) {
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 -> {
if(node.isFromArrayElement)
if(node.isMsbForSplitArray)
"&>"
else if(node.isFromArrayElement)
"& array-element"
else
"&"
}
is PtArray -> {
val valuelist = node.children.map {
val valuelist = node.children.joinToString(", ") {
when (it) {
is PtBool -> it.toString()
is PtNumber -> it.number.toString()
is PtIdentifier -> it.name
else -> "?"
is PtAddressOf -> "& ${it.identifier.name}"
else -> "invalid array element $it"
}
}.joinToString(", ")
}
"array len=${node.children.size} ${type(node.type)} [ $valuelist ]"
}
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
@@ -46,14 +53,14 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
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()
val numstr = if(node.type.isFloat) node.number.toString() else node.number.toHex()
"$numstr ${type(node.type)}"
}
is PtBool -> node.value.toString()
is PtPrefix -> node.operator
is PtRange -> "<range>"
is PtString -> "\"${node.value.escape()}\""
is PtTypeCast -> "as ${node.type.name.lowercase()}"
is PtTypeCast -> "as ${node.type}"
is PtForLoop -> "for"
is PtIfElse -> "ifelse"
is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}"
@@ -64,12 +71,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"%asm {{ ...${node.assembly.length} characters... }}"
}
is PtJump -> {
if(node.identifier!=null)
"goto ${node.identifier.name}"
else if(node.address!=null)
"goto ${node.address.toHex()}"
else
"???"
"goto ${txt(node.target)}"
}
is PtAsmSub -> {
val params = node.parameters.joinToString(", ") {
@@ -87,54 +89,65 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
}"
}
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 = when(node.type) {
DataType.BOOL -> if(node.value==0.0) "false" else "true"
in IntegerDatatypes -> node.value.toInt().toString()
val value = when {
node.type.isBool -> if(node.value==0.0) "false" else "true"
node.type.isInteger -> node.value.toInt().toString()
else -> node.value.toString()
}
"const ${node.type.name.lowercase()} ${node.name} = $value"
"const ${node.type} ${node.name} = $value"
}
is PtLabel -> "${node.name}:"
is PtMemMapped -> {
if(node.type in ArrayDatatypes) {
if(node.type.isArray) {
val arraysize = if(node.arraySize==null) "" else node.arraySize.toString()
val eltType = ArrayToElementTypes.getValue(node.type)
"&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}"
"&${node.type.elementType()}[$arraysize] ${node.name} = ${node.address.toHex()}"
} else {
"&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}"
"&${node.type} ${node.name} = ${node.address.toHex()}"
}
}
is PtSub -> {
val params = node.parameters.joinToString(", ") { "${it.type} ${it.name}" }
val params = node.parameters.joinToString(", ") {
val reg = if(it.register!=null) "@${it.register}" else ""
"${it.type} ${it.name} $reg"
}
var str = "sub ${node.name}($params) "
if(node.returntype!=null)
str += "-> ${node.returntype.name.lowercase()}"
str += "-> ${node.returntype}"
str
}
is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
val split = if(node.type.isSplitWordArray) "" else "@nosplit"
val align = when(node.align) {
0u -> ""
2u -> "@alignword"
64u -> "@align64"
256u -> "@alignpage"
else -> throw IllegalArgumentException("invalid alignment size")
}
else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] $split ${node.name}"
val str = if(node.arraySize!=null) {
val eltType = node.type.elementType()
"${eltType}[${node.arraySize}] $split $align ${node.name}"
}
else if(node.type.isArray) {
val eltType = node.type.elementType()
"${eltType}[] $split $align ${node.name}"
}
else
"${node.type.name.lowercase()} ${node.name}"
"${node.type} ${node.name}"
if(node.value!=null)
str + " = " + txt(node.value)
else
@@ -145,7 +158,10 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtProgram -> "PROGRAM ${node.name}"
is PtRepeatLoop -> "repeat"
is PtReturn -> "return"
is PtSubroutineParameter -> "${node.type.name.lowercase()} ${node.name}"
is PtSubroutineParameter -> {
val reg = if(node.register!=null) "@${node.register}" else ""
"${node.type} ${node.name} $reg"
}
is PtWhen -> "when"
is PtWhenChoice -> {
if(node.isElse)
@@ -153,6 +169,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else
"->"
}
is PtDefer -> "<defer>"
is PtIfExpression -> "<ifexpr>"
}
}
+26 -14
View File
@@ -10,13 +10,16 @@ sealed interface IPtSubroutine {
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(
@@ -27,16 +30,16 @@ class PtSub(
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
if(parameters.any{ !it.type.isNumericOrBool })
throw AssemblyError("non-numeric/non-bool parameter")
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
if(returntype!=null && !returntype.isNumericOrBool)
throw AssemblyError("non-numeric/non-bool returntype $returntype")
parameters.forEach { it.parent=this }
}
}
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
class PtSubroutineParameter(name: String, val type: DataType, val register: RegisterOrPair?, position: Position): PtNamedNode(name, position)
sealed interface IPtAssignment {
@@ -113,12 +116,9 @@ class PtIfElse(position: Position) : PtNode(position) {
}
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
val address: UInt?,
position: Position) : PtNode(position) {
init {
identifier?.let {it.parent = this }
}
class PtJump(position: Position) : PtNode(position) {
val target: PtExpression
get() = children.single() as PtExpression
}
@@ -131,7 +131,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())
@@ -148,7 +149,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}
}
@@ -160,7 +169,7 @@ class PtConstant(name: String, override val type: DataType, val value: Double, p
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)
require(!type.isString)
}
}
@@ -179,3 +188,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
+16
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}"}
}
}
}
*/
}
+101 -84
View File
@@ -1,7 +1,7 @@
package prog8.code.core
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?)
class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String {
val paramConvs = params.mapIndexed { index, it ->
@@ -13,35 +13,43 @@ class CallConvention(val params: List<ParamConvention>, val returns: ReturnConve
}
val returnConv =
when {
returns.reg!=null -> returns.reg.toString()
returns.floatFac1 -> "floatFAC1"
returns.reg == RegisterOrPair.FAC1 -> "floatFAC1"
returns.reg != null -> returns.reg.toString()
else -> "<no returnvalue>"
}
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
}
}
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
private val ByteDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
private val ArrayDatatypes = arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
class FSignature(val pure: Boolean, // does it have side effects?
val parameters: List<FParam>,
val returnType: DataType?) {
val returnType: BaseDataType?,
vararg val parameters: FParam) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention {
val returns: ReturnConvention = when (returnType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(returnType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
null -> ReturnConvention(null, null, false)
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
null -> ReturnConvention(null, null)
else -> {
// return type depends on arg type
when (val paramType = actualParamTypes.first()) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(paramType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
else -> ReturnConvention(paramType, null, false)
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
else -> ReturnConvention(paramType, null)
}
}
}
@@ -49,16 +57,27 @@ class FSignature(val pure: Boolean, // does it have side effects?
return when {
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
actualParamTypes.size==1 -> {
// one parameter goes via register/registerpair
// One parameter goes via register/registerpair.
// this avoids repeated code for every caller to store the value in the subroutine's argument variable.
// (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site).
val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
BaseDataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
}
CallConvention(listOf(paramConv), returns)
}
actualParamTypes.size==2 && (actualParamTypes[0] in ByteDatatypes && actualParamTypes[1].isWord) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==3 && actualParamTypes.all { it.isByte } -> {
TODO("opportunity to pass 3 byte arguments in A,Y and X registers but not implemented yet")
}
else -> {
// multiple parameters go via variables
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
@@ -69,67 +88,65 @@ class FSignature(val pure: Boolean, // does it have side effects?
}
val BuiltinFunctions: Map<String, FSignature> = mapOf(
// this set of function have no return value and operate in-place:
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
// cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
"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", 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("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),
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT),
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"rsave" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
"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),
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
"prog8_ifelse_bittest_set" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"prog8_ifelse_bittest_notset" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"abs__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE)),
"abs__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD)),
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"rsave" to FSignature(false, null),
"rrestore" to FSignature(false, null),
"memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
"callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
"callfar2" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
"call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
)
val InplaceModifyingBuiltinFunctions = setOf(
@@ -14,6 +14,7 @@ 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,
@@ -26,8 +27,9 @@ class CompilationOptions(val output: OutputType,
var varsGolden: Boolean = false,
var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false,
var dontSplitWordArrays: Boolean = false,
var breakpointCpuInstruction: String? = null,
var ignoreFootguns: Boolean = false,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()
) {
+4 -2
View File
@@ -11,6 +11,7 @@ fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
@@ -19,7 +20,7 @@ fun Number.toHex(): String {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
else -> "$"+integer.toString(16).padStart(8,'0')
}
}
@@ -27,11 +28,12 @@ fun UInt.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
return when (this) {
in 0u until 16u -> this.toString()
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
else -> "$"+this.toString(16).padStart(8,'0')
}
}
+246 -79
View File
@@ -1,63 +1,255 @@
package prog8.code.core
enum class DataType {
import java.util.Objects
enum class BaseDataType {
UBYTE, // pass by value 8 bits unsigned
BYTE, // pass by value 8 bits signed
UWORD, // pass by value 16 bits unsigned
WORD, // pass by value 16 bits signed
LONG, // pass by value 32 bits signed
FLOAT, // pass by value machine dependent
BOOL, // pass by value bit 0 of a 8 bit byte
BOOL, // pass by value bit 0 of an 8-bit byte
STR, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
ARRAY_W, // pass by reference
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference
ARRAY, // pass by reference, subtype is the element type
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
UNDEFINED;
/**
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
BOOL -> targetType == BOOL
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
LONG -> targetType.oneOf(LONG, FLOAT)
FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this
else -> false
}
fun oneOf(vararg types: DataType) = this in types
infix fun largerThan(other: DataType) =
fun largerSizeThan(other: BaseDataType) =
when {
this == other -> false
this in ByteDatatypesWithBoolean -> false
this in WordDatatypes -> other in ByteDatatypesWithBoolean
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
this.isByteOrBool -> false
this.isWord -> other.isByteOrBool
this == LONG -> other.isByteOrBool || other.isWord
this == STR && other == UWORD || this == UWORD && other == STR -> false
this.isArray -> other != FLOAT
this == STR -> other != FLOAT
else -> true
}
infix fun equalsSize(other: DataType) =
fun equalsSize(other: BaseDataType) =
when {
this == other -> true
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
this in WordDatatypes -> other in WordDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> true
this.isByteOrBool -> other.isByteOrBool
this.isWord -> other.isWord
this == STR && other== UWORD || this== UWORD && other== STR -> true
this == STR && other.isArray -> true
this.isArray && other == STR -> true
else -> false
}
}
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
val BaseDataType.isPassByRef get() = this.isIterable
val BaseDataType.isPassByValue get() = !this.isIterable
sealed class SubType(val dt: BaseDataType) {
companion object {
private val types by lazy {
// lazy because of static initialization order
mapOf(
BaseDataType.UBYTE to SubUnsignedByte,
BaseDataType.BYTE to SubSignedByte,
BaseDataType.UWORD to SubUnsignedWord,
BaseDataType.WORD to SubSignedWord,
BaseDataType.FLOAT to SubFloat,
BaseDataType.BOOL to SubBool
)}
fun forDt(dt: BaseDataType) = types.getValue(dt)
}
}
data object SubUnsignedByte: SubType(BaseDataType.UBYTE)
data object SubSignedByte: SubType(BaseDataType.BYTE)
data object SubUnsignedWord: SubType(BaseDataType.UWORD)
data object SubSignedWord: SubType(BaseDataType.WORD)
data object SubBool: SubType(BaseDataType.BOOL)
data object SubFloat: SubType(BaseDataType.FLOAT)
class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
init {
if(base.isArray) {
require(sub != null)
if(base.isSplitWordArray)
require(sub.dt == BaseDataType.UWORD || sub.dt == BaseDataType.WORD)
}
else if(base==BaseDataType.STR)
require(sub?.dt==BaseDataType.UBYTE) { "STR subtype should be ubyte" }
else
require(sub == null)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DataType) return false
return base == other.base && sub == other.sub
}
override fun hashCode(): Int = Objects.hash(base, sub)
companion object {
private val simpletypes = mapOf(
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
BaseDataType.STR to DataType(BaseDataType.STR, SubUnsignedByte),
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
)
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
return if(splitwordarray && elementDt.isWord)
DataType(BaseDataType.ARRAY_SPLITW, SubType.forDt(actualElementDt))
else
DataType(BaseDataType.ARRAY, SubType.forDt(actualElementDt))
}
}
fun elementToArray(splitwords: Boolean = true): DataType {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
else arrayFor(base, false)
}
fun elementType(): DataType =
if(base.isArray || base==BaseDataType.STR)
forDt(sub!!.dt)
else
throw IllegalArgumentException("not an array")
override fun toString(): String = when(base) {
BaseDataType.ARRAY -> {
when(sub) {
SubBool -> "bool[]"
SubFloat -> "float[]"
SubSignedByte -> "byte[]"
SubSignedWord -> "word[]"
SubUnsignedByte -> "ubyte[]"
SubUnsignedWord -> "uword[]"
null -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubSignedWord -> "word[] (split)"
SubUnsignedWord -> "uword[] (split)"
else -> throw IllegalArgumentException("invalid sub type")
}
}
else -> base.name.lowercase()
}
fun sourceString(): String = when (base) {
BaseDataType.BOOL -> "bool"
BaseDataType.UBYTE -> "ubyte"
BaseDataType.BYTE -> "byte"
BaseDataType.UWORD -> "uword"
BaseDataType.WORD -> "word"
BaseDataType.LONG -> "long"
BaseDataType.FLOAT -> "float"
BaseDataType.STR -> "str"
BaseDataType.ARRAY -> {
when(sub) {
SubUnsignedByte -> "ubyte["
SubUnsignedWord -> "@nosplit uword["
SubBool -> "bool["
SubSignedByte -> "byte["
SubSignedWord -> "@nosplit word["
SubFloat -> "float["
null -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubUnsignedWord -> "uword["
SubSignedWord -> "word["
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
}
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
infix fun isAssignableTo(targetType: DataType) =
when(base) {
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
BaseDataType.UNDEFINED -> false
}
fun largerSizeThan(other: DataType): Boolean {
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
return base.largerSizeThan(other.base)
}
fun equalsSize(other: DataType): Boolean {
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
return base.equalsSize(other.base)
}
val isUndefined = base == BaseDataType.UNDEFINED
val isByte = base.isByte
val isUnsignedByte = base == BaseDataType.UBYTE
val isSignedByte = base == BaseDataType.BYTE
val isByteOrBool = base.isByteOrBool
val isWord = base.isWord
val isUnsignedWord = base == BaseDataType.UWORD
val isSignedWord = base == BaseDataType.WORD
val isInteger = base.isInteger
val isIntegerOrBool = base.isIntegerOrBool
val isNumeric = base.isNumeric
val isNumericOrBool = base.isNumericOrBool
val isSigned = base.isSigned
val isUnsigned = !base.isSigned
val isArray = base.isArray
val isBoolArray = base.isArray && sub?.dt == BaseDataType.BOOL
val isByteArray = base.isArray && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub?.dt == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && sub?.dt == BaseDataType.BYTE
val isWordArray = base.isArray && (sub?.dt == BaseDataType.UWORD || sub?.dt == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && sub?.dt == BaseDataType.UWORD
val isSignedWordArray = base.isArray && sub?.dt == BaseDataType.WORD
val isFloatArray = base.isArray && sub?.dt == BaseDataType.FLOAT
val isString = base == BaseDataType.STR
val isBool = base == BaseDataType.BOOL
val isFloat = base == BaseDataType.FLOAT
val isLong = base == BaseDataType.LONG
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE))
val isSplitWordArray = base.isSplitWordArray
val isSplitUnsignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.WORD
val isIterable = base.isIterable
val isPassByRef = base.isPassByRef
val isPassByValue = base.isPassByValue
}
enum class CpuRegister {
A,
X,
@@ -95,6 +287,17 @@ enum class RegisterOrPair {
else -> throw IllegalArgumentException("no cpu hardware register for $this")
}
fun asScopedNameVirtualReg(type: DataType?): List<String> {
require(this in Cx16VirtualRegisters)
val suffix = when(type?.base) {
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> ""
else -> throw kotlin.IllegalArgumentException("invalid register param type")
}
return listOf("cx16", name.lowercase()+suffix)
}
} // only used in parameter and return value specs in asm subroutines
enum class Statusflag {
@@ -123,48 +326,6 @@ enum class BranchCondition {
VC
}
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL
)
val PassByValueDatatypes = NumericDatatypesWithBoolean
val PassByReferenceDatatypes = IterableDatatypes
val ArrayToElementTypes = mapOf(
DataType.STR to DataType.UBYTE,
DataType.ARRAY_B to DataType.BYTE,
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_W_SPLIT to DataType.WORD,
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL
)
val ElementToArrayTypes = mapOf(
DataType.BYTE to DataType.ARRAY_B,
DataType.UBYTE to DataType.ARRAY_UB,
DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL,
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
)
val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
@@ -172,7 +333,7 @@ val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
)
val CpuRegisters = setOf(
val CpuRegisters = arrayOf(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
)
@@ -203,3 +364,9 @@ enum class ZeropageWish {
DONTCARE,
NOT_IN_ZEROPAGE
}
enum class SplitWish {
DONTCARE,
SPLIT,
NOSPLIT
}
@@ -13,7 +13,9 @@ interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
val PROGRAM_LOAD_ADDRESS : UInt
val PROGRAM_MEMTOP_ADDRESS: UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
val BSSGOLDENRAM_START: UInt
+2 -2
View File
@@ -1,6 +1,6 @@
package prog8.code.core
interface IMemSizer {
fun memorySize(dt: DataType): Int
fun memorySize(arrayDt: DataType, numElements: Int): Int
fun memorySize(dt: DataType, numElements: Int?): Int
fun memorySize(dt: SubType): Int
}
+17 -18
View File
@@ -38,7 +38,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
for (reserved in options.zpReserved)
reserve(reserved)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
}
}
@@ -70,9 +70,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
return Err(MemAllocationError("zero page usage has been disabled"))
val size: Int =
when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
when {
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
datatype.isString || datatype.isArray -> {
val memsize = options.compTarget.memorySize(datatype, numElements!!)
if(position!=null)
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
@@ -80,9 +80,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
memsize
}
DataType.FLOAT -> {
datatype.isFloat -> {
if (options.floats) {
val memsize = options.compTarget.memorySize(DataType.FLOAT)
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
if(position!=null)
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
else
@@ -94,7 +94,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
}
synchronized(this) {
if(free.size > 0) {
if(free.isNotEmpty()) {
if(size==1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if(oneSeparateByteFree(candidate))
@@ -118,10 +118,10 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
require(size>=0)
free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) {
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)
allocatedVariables[name] = when {
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
datatype.isString -> VarAllocation(address, datatype, size)
datatype.isArray -> VarAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt")
}
}
@@ -150,14 +150,13 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
val size: Int =
when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
options.compTarget.memorySize(datatype, numElements!!)
}
DataType.FLOAT -> {
when {
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
datatype.isString -> numElements!!
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
datatype.isFloat -> {
if (options.floats) {
options.compTarget.memorySize(DataType.FLOAT)
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
} else return Err(MemAllocationError("floating point option not enabled"))
}
else -> throw MemAllocationError("weird dt")
+5 -5
View File
@@ -1,10 +1,10 @@
package prog8.code.core
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", "in")
val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not")
val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation
val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = arrayOf("and", "or", "xor", "not", "in")
val BitwiseOperators = arrayOf("&", "|", "^", "~")
val PrefixOperators = arrayOf("+", "-", "~", "not")
fun invertedComparisonOperator(operator: String) =
when (operator) {
+3 -3
View File
@@ -1,6 +1,6 @@
package prog8.code.core
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import prog8.code.source.SourceCode
import java.nio.file.InvalidPathException
import kotlin.io.path.Path
import kotlin.io.path.absolute
@@ -10,12 +10,12 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
fun toClickableStr(): String {
if(this===DUMMY)
return ""
if(file.startsWith(LIBRARYFILEPREFIX))
if(SourceCode.isLibraryResource(file))
return "$file:$line:$startCol:"
return try {
val path = Path(file).absolute().normalize().toString()
"file://$path:$line:$startCol:"
} catch(x: InvalidPathException) {
} catch(_: InvalidPathException) {
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
"file://$file:$line:$startCol:"
}
+69 -34
View File
@@ -1,6 +1,6 @@
package prog8.code.optimize
import prog8.code.StRomSub
import prog8.code.StExtSub
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
@@ -11,7 +11,7 @@ fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st:
return
while (errors.noErrors() &&
(optimizeBitTest(program, options)
+ optimizeAssignTargets(program, st, errors)) > 0
+ optimizeAssignTargets(program, st)) > 0
) {
// keep rolling
}
@@ -27,7 +27,7 @@ private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
}
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: IErrorReporter): Int {
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable): Int {
var changes = 0
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtAssignment) {
@@ -39,7 +39,7 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: I
}
if(functionName!=null) {
val stNode = st.lookup(functionName)
if (stNode is StRomSub) {
if (stNode is StExtSub) {
require(node.children.size==stNode.returns.size+1) {
"number of targets must match return values"
}
@@ -60,7 +60,7 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: I
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, value.type, value.position)
val voidCall = PtFunctionCall(functionName, true, DataType.forDt(BaseDataType.UNDEFINED), value.position)
value.children.forEach { voidCall.add(it) }
node.parent.children[index] = voidCall
voidCall.parent = node.parent
@@ -78,38 +78,73 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
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.forDt(BaseDataType.BOOL), condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(BaseDataType.UBYTE, 7.0, and.right.position))
else
bittestCall.add(PtNumber(BaseDataType.UBYTE, 6.0, and.right.position))
return bittestCall
}
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.isUnsignedByte) {
val bitmask = and.right.asConstInteger()
if(bitmask==128 || bitmask==64) {
val variable = and.left as? PtIdentifier
if (variable != null && variable.type.isByte) {
return Triple(and, variable, bitmask)
}
val typecast = and.left as? PtTypeCast
if (typecast != null && typecast.type.isUnsignedByte) {
val castedVariable = typecast.value as? PtIdentifier
if(castedVariable!=null && castedVariable.type.isByte)
return Triple(and, castedVariable, bitmask)
}
}
}
}
}
return null
}
var changes = 0
var recurse = true
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtIfElse) {
val condition = node.condition as? PtBinaryExpression
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 variable = and.left as? PtIdentifier
val bitmask = and.right.asConstInteger()
if(variable!=null && variable.type in ByteDatatypes && (bitmask==128 || bitmask==64)) {
val setOrNot = if(condition.operator=="!=") "set" else "notset"
val index = node.parent.children.indexOf(node)
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.BOOL, node.condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(DataType.UBYTE, 7.0, and.right.position))
else
bittestCall.add(PtNumber(DataType.UBYTE, 6.0, and.right.position))
val ifElse = PtIfElse(node.position)
ifElse.add(bittestCall)
ifElse.add(node.ifScope)
if(node.hasElse())
ifElse.add(node.elseScope)
node.parent.children[index] = ifElse
ifElse.parent = node.parent
changes++
recurse = false
}
}
}
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
}
}
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
@@ -130,12 +165,12 @@ internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister:
cx16.r?sL BYTE
cx16.r?sH BYTE
*/
if(identifier.type in ByteDatatypes && type in ByteDatatypes) {
if(identifier.type.isByte && type.isByte) {
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) {
else if(identifier.type.isWord && type.isWord) {
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
return identifierRegName.substring(2) in arrayOf("", "s")
}
@@ -0,0 +1,60 @@
package prog8.code.source
import prog8.code.core.Position
import java.nio.file.Path
import kotlin.io.path.Path
// Resource caching "filesystem".
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
object ImportFileSystem {
fun getFile(path: Path): SourceCode {
val cached = cache[path.toString()]
if (cached != null) return cached
val file = SourceCode.File(path)
cache[path.toString()] = file
return file
}
fun getResource(name: String): SourceCode {
val cached = cache[name]
if (cached != null) return cached
val resource = SourceCode.Resource(name)
cache[name] = resource
return resource
}
fun retrieveSourceLine(position: Position): String {
if(SourceCode.isLibraryResource(position.file)) {
val cached = cache[SourceCode.withoutPrefix(position.file)]
if(cached != null)
return getLine(cached, position.line)
}
val cached = cache[position.file]
if(cached != null)
return getLine(cached, position.line)
val path = Path(position.file).toAbsolutePath().normalize()
val cached2 = cache[path.toString()]
if(cached2 != null)
return getLine(cached2, position.line)
throw NoSuchElementException("cannot get source line $position")
}
private fun getLine(code: SourceCode, lineIndex: Int): String {
var spans = lineSpanCache[code]
if(spans==null) {
val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first }
val ends = lineSpans.drop(1) + code.text.length
spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray()
lineSpanCache[code] = spans
}
val span = spans[lineIndex - 1]
return code.text.substring(span.start, span.end).trim()
}
private class LineSpan(val start: Int, val end: Int)
private val cache = mutableMapOf<String, SourceCode>()
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
}
@@ -1,16 +1,11 @@
package prog8.code.core
package prog8.code.source
import java.io.File
import java.io.IOException
import java.nio.file.Path
import java.text.Normalizer
import kotlin.io.path.Path
import kotlin.io.path.readText
const val internedStringsModuleName = "prog8_interned_strings"
/**
* Encapsulates - and ties together - actual source code (=text) and its [origin].
*/
@@ -55,14 +50,21 @@ sealed class SourceCode {
/**
* filename prefix to designate library files that will be retreived from internal resources rather than disk
*/
const val LIBRARYFILEPREFIX = "library:"
const val STRINGSOURCEPREFIX = "string:"
private const val LIBRARYFILEPREFIX = "library:"
private const val STRINGSOURCEPREFIX = "string:"
val curdir: Path = Path(".").toAbsolutePath()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
fun isRegularFilesystemPath(pathString: String) =
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
fun withoutPrefix(path: String): String {
return if(isLibraryResource(path))
path.removePrefix(LIBRARYFILEPREFIX)
else if(isStringResource(path))
path.removePrefix(STRINGSOURCEPREFIX)
else
path
}
}
/**
@@ -80,12 +82,13 @@ sealed class SourceCode {
/**
* Get [SourceCode] from the file represented by the specified Path.
* This immediately reads the file fully into memory.
* You can only get an instance of this via the ImportFileSystem object.
*
* [origin] will be the given path in absolute and normalized form.
* @throws NoSuchFileException if the file does not exist
* @throws FileSystemException if the file cannot be read
*/
class File(path: Path): SourceCode() {
internal class File(path: Path): SourceCode() {
override val text: String
override val origin: String
override val name: String
@@ -109,8 +112,9 @@ sealed class SourceCode {
/**
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
* You can only get an instance of this via the ImportFileSystem object.
*/
class Resource(pathString: String): SourceCode() {
internal class Resource(pathString: String): SourceCode() {
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
override val isFromResources = true
@@ -124,7 +128,7 @@ sealed class SourceCode {
if (rscURL == null) {
val rscRoot = object {}.javaClass.getResource("/")
throw NoSuchFileException(
File(normalized),
java.io.File(normalized),
reason = "looked in resources rooted at $rscRoot"
)
}
@@ -144,34 +148,4 @@ sealed class SourceCode {
override val origin: String = name
override val text: String = "<generated code node, no text representation>"
}
}
object SourceLineCache {
private val cache = mutableMapOf<String, List<String>>()
private fun getCachedFile(file: String): List<String> {
val existing = cache[file]
if(existing!=null)
return existing
if (SourceCode.isRegularFilesystemPath(file)) {
val source = SourceCode.File(Path(file))
cache[file] = source.text.split('\n', '\r').map { it.trim() }
return cache.getValue(file)
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
cache[file] = source.text.split('\n', '\r').map { it.trim()}
return cache.getValue(file)
}
return emptyList()
}
fun retrieveLine(position: Position): String? {
if (position.line>0) {
val lines = getCachedFile(position.file)
if(lines.isNotEmpty())
return lines[position.line-1]
}
return null
}
}
+18 -12
View File
@@ -13,19 +13,25 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
const val NAME = "atari"
}
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(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
+3 -1
View File
@@ -27,6 +27,7 @@ val CompilationTargets = listOf(
Cx16Target.NAME,
PETTarget.NAME,
AtariTarget.NAME,
Neo6502Target.NAME,
VMTarget.NAME
)
@@ -37,5 +38,6 @@ fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
PETTarget.NAME -> PETTarget()
AtariTarget.NAME -> AtariTarget()
VMTarget.NAME -> VMTarget()
Neo6502Target.NAME -> Neo6502Target()
else -> throw IllegalArgumentException("invalid compilation target")
}
}
@@ -0,0 +1,37 @@
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, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
+18 -11
View File
@@ -12,18 +12,25 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
const val NAME = "virtual"
}
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(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
@@ -11,7 +11,9 @@ class AtariMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x2000u
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // TODO what's memtop?
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
@@ -28,7 +28,7 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.FULL -> {
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
free.addAll(0x00u..0xffu)
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
// TODO atari free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x80u..0xffu) // TODO
@@ -13,7 +13,9 @@ class C128MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val PROGRAM_MEMTOP_ADDRESS = 0xff00u
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
@@ -6,7 +6,7 @@ import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
// reference: "Mapping the C128" zero page chapter.
// reference: "Mapping the C128" zeropage chapter.
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
@@ -33,18 +33,18 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.FULL -> {
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
free.addAll(0x0au..0xffu)
free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
free.removeAll(arrayOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x0au..0x8fu) // BASIC variables
free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
}
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> {
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
free.addAll(0x1bu..0x23u)
free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
0x55u, 0x56u, 0x57u, 0x58u,
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
@@ -53,7 +53,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
// if(options.zeropage==ZeropageType.BASICSAFE) {
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
@@ -14,7 +14,10 @@ class C64MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 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
@@ -52,7 +55,7 @@ class C64MachineDefinition: IMachineDefinition {
val process: Process
try {
process=processb.start()
} catch(x: IOException) {
} catch(_: IOException) {
continue // try the next emulator executable
}
process.waitFor()
@@ -21,11 +21,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FULL) {
free.addAll(0x02u..0xffu)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
} else {
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
free.addAll(listOf(
free.addAll(arrayOf(
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
@@ -43,7 +43,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zeropage locations used for floating point operations from the free list
free.removeAll(listOf(
free.removeAll(arrayOf(
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
@@ -56,7 +56,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if(options.zeropage != ZeropageType.DONTUSE) {
// add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
} else {
@@ -81,16 +81,16 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
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.
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
for(reg in 0..15) {
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
free.remove((4+reg*2).toUInt())
free.remove((5+reg*2).toUInt())
}
@@ -1,21 +1,30 @@
package prog8.code.target.cbm
import prog8.code.core.*
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.IMemSizer
import prog8.code.core.SubType
internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * Mflpt5.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> Mflpt5.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
@@ -13,7 +13,9 @@ class CX16MachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
@@ -54,15 +54,15 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
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.
// However, to be able for the compiler to "see" them as zeropage 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
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
}
}
}
@@ -7,7 +7,7 @@ import java.io.CharConversionException
object PetsciiEncoding {
// decoding: from Petscii/Screencodes (0-255) to unicode
// decoding: from Petscii/Screencodes (0-255) to Unicode
// character tables used from https://github.com/irmen/cbmcodecs2
private val decodingPetsciiLowercase = charArrayOf(
@@ -1089,7 +1089,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})
@@ -1135,7 +1135,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})
@@ -1157,16 +1157,16 @@ object PetsciiEncoding {
}
}
fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
val code: UInt = when {
petscii_code <= 0x1fu -> petscii_code + 128u
petscii_code <= 0x3fu -> petscii_code.toUInt()
petscii_code <= 0x5fu -> petscii_code - 64u
petscii_code <= 0x7fu -> petscii_code - 32u
petscii_code <= 0x9fu -> petscii_code + 64u
petscii_code <= 0xbfu -> petscii_code - 64u
petscii_code <= 0xfeu -> petscii_code - 128u
petscii_code == 255.toUByte() -> 95u
petsciicode <= 0x1fu -> petsciicode + 128u
petsciicode <= 0x3fu -> petsciicode.toUInt()
petsciicode <= 0x5fu -> petsciicode - 64u
petsciicode <= 0x7fu -> petsciicode - 32u
petsciicode <= 0x9fu -> petsciicode + 64u
petsciicode <= 0xbfu -> petsciicode - 64u
petsciicode <= 0xfeu -> petsciicode - 128u
petsciicode == 255.toUByte() -> 95u
else -> return Err(CharConversionException("petscii code out of range"))
}
if(inverseVideo) {
@@ -0,0 +1,50 @@
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 STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0800u
override val PROGRAM_MEMTOP_ADDRESS = 0xfc00u // kernal starts here
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)
}
}
@@ -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 zeropage 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.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
}
}
}
@@ -13,7 +13,9 @@ class PETMachineDefinition: IMachineDefinition {
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0401u
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
override val BSSHIGHRAM_START = 0u
override val BSSHIGHRAM_END = 0u
@@ -10,10 +10,12 @@ class VirtualMachineDefinition: IMachineDefinition {
override val cpu = CpuType.VIRTUAL
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 8 // 64-bits double
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used
-63
View File
@@ -1,63 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
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:2.0.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 {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}
+46
View File
@@ -0,0 +1,46 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
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:2.0.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.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
sourceSets {
main {
java {
setSrcDirs(listOf("$projectDir/src"))
}
resources {
setSrcDirs(listOf("$projectDir/res"))
}
}
test {
java {
setSrcDirs(listOf("$projectDir/test"))
}
}
}
tasks.test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn("cleanTest")
// Show test results.
testLogging {
events("skipped", "failed")
}
}
+332 -158
View File
@@ -7,9 +7,10 @@ import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.source.ImportFileSystem
import prog8.code.source.SourceCode
import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.assignment.*
import kotlin.collections.ArrayDeque
import kotlin.io.path.Path
import kotlin.io.path.writeLines
@@ -17,15 +18,15 @@ import kotlin.io.path.writeLines
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSequenceNr: Int): ICodeGeneratorBackend {
override fun generate(
program: PtProgram,
symbolTable: SymbolTable,
options: CompilationOptions,
errors: IErrorReporter
errors: IErrorReporter,
): IAssemblyProgram? {
val st = if(prefixSymbols) prefixSymbols(program, options, symbolTable) else symbolTable
val asmgen = AsmGen6502Internal(program, st, options, errors)
val asmgen = AsmGen6502Internal(program, st, options, errors, lastGeneratedLabelSequenceNr)
return asmgen.compileToAssembly()
}
@@ -37,10 +38,9 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
when(node) {
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
is PtBlock -> node.name = "p8b_${node.name}"
is PtLabel -> node.name = "p8l_${node.name}"
is PtLabel -> if(!node.name.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
is PtConstant -> node.name = "p8c_${node.name}"
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
else -> node.name = "p8_${node.name}"
}
}
@@ -49,6 +49,9 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
is PtAsmSub -> {
prefixNamedNode(node)
node.parameters.forEach { (_, param) -> prefixNamedNode(param) }
if(node.address?.varbank!=null) {
node.address!!.varbank = node.address!!.varbank!!.prefix(node, st)
}
}
is PtSub -> {
prefixNamedNode(node)
@@ -63,7 +66,7 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
}
is PtIdentifier -> {
var lookupName = node.name
if(node.type in SplitWordArrayTypes && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
lookupName = lookupName.dropLast(4)
}
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node")
@@ -72,13 +75,6 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
nodesToPrefix += node.parent to index
}
}
is PtJump -> {
val stNode = st.lookup(node.identifier!!.name) ?: throw AssemblyError("name not found ${node.identifier}")
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
is PtBlock -> prefixNamedNode(node)
is PtConstant -> prefixNamedNode(node)
is PtLabel -> prefixNamedNode(node)
@@ -103,7 +99,6 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
when(val node = parent.children[index]) {
is PtIdentifier -> parent.children[index] = node.prefix(parent, st)
is PtFunctionCall -> throw AssemblyError("PtFunctionCall should be processed in their own list, last")
is PtJump -> parent.children[index] = node.prefix(parent, st)
is PtVariable -> parent.children[index] = node.prefix(parent, st)
else -> throw AssemblyError("weird node to prefix $node")
}
@@ -124,11 +119,15 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
}
private fun prefixScopedName(name: String, type: Char): String {
if('.' !in name)
if('.' !in name) {
if(name.startsWith(PtLabel.GENERATED_LABEL_PREFIX))
return name
return "p8${type}_$name"
}
val parts = name.split('.')
val firstPrefixed = "p8b_${parts[0]}"
val lastPrefixed = "p8${type}_${parts.last()}"
val lastPart = parts.last()
val lastPrefixed = if(lastPart.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) lastPart else "p8${type}_$lastPart"
// the parts in between are assumed to be subroutine scopes.
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
@@ -145,7 +144,6 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
val newValue = PtArray(arrayValue.type, arrayValue.position)
arrayValue.children.forEach { elt ->
when(elt) {
is PtIdentifier -> newValue.add(elt.prefix(arrayValue, st))
is PtBool -> newValue.add(elt)
is PtNumber -> newValue.add(elt)
is PtAddressOf -> {
@@ -163,20 +161,13 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
else -> throw AssemblyError("weird array value element $elt")
}
}
val result = PtVariable(name, type, zeropage, newValue, arraySize, position)
val result = PtVariable(name, type, zeropage, align, newValue, arraySize, position)
result.parent = parent
result
}
else this
}
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
val prefixedIdent = identifier!!.prefix(this, st)
val jump = PtJump(prefixedIdent, address, position)
jump.parent = parent
return jump
}
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
val newName = prefixScopedName(name, 's')
val call = PtFunctionCall(newName, void, type, position)
@@ -201,7 +192,7 @@ private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
val prefixType = when(target!!.type) {
StNodeType.BLOCK -> 'b'
StNodeType.SUBROUTINE, StNodeType.ROMSUB -> 's'
StNodeType.SUBROUTINE, StNodeType.EXTSUB -> 's'
StNodeType.LABEL -> 'l'
StNodeType.STATICVAR, StNodeType.MEMVAR -> 'v'
StNodeType.CONSTANT -> 'c'
@@ -220,11 +211,12 @@ class AsmGen6502Internal (
val program: PtProgram,
internal val symbolTable: SymbolTable,
internal val options: CompilationOptions,
internal val errors: IErrorReporter
internal val errors: IErrorReporter,
private var generatedLabelSequenceNumber: Int
) {
internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
internal val optimizedByteMultiplications = arrayOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
internal val optimizedWordMultiplications = arrayOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
internal val loopEndLabels = ArrayDeque<String>()
private val zeropage = options.compTarget.machine.zeropage
private val allocator = VariableAllocator(symbolTable, options, errors)
@@ -236,7 +228,8 @@ class AsmGen6502Internal (
private val anyExprGen = AnyExprAsmGen(this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, allocator, assignmentAsmGen, errors)
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, assignmentAsmGen, errors)
private val ifExpressionAsmgen = IfExpressionAsmGen(this, assignmentAsmGen, errors)
fun compileToAssembly(): IAssemblyProgram? {
@@ -344,11 +337,10 @@ class AsmGen6502Internal (
lastSourceLineNumber = node.position.line
val srcComment = "\t; source: ${node.position.file}:${node.position.line}"
val line = SourceLineCache.retrieveLine(node.position)
if(line==null)
out(srcComment, false)
else
if(node.position.line>0) {
val line = ImportFileSystem.retrieveSourceLine(node.position)
out("$srcComment $line", false)
}
}
internal fun out(str: String, splitlines: Boolean = true) {
@@ -408,17 +400,17 @@ class AsmGen6502Internal (
internal val tempVarsCounters = mutableMapOf(
DataType.BOOL to 0,
DataType.BYTE to 0,
DataType.UBYTE to 0,
DataType.WORD to 0,
DataType.UWORD to 0,
DataType.FLOAT to 0
BaseDataType.BOOL to 0,
BaseDataType.BYTE to 0,
BaseDataType.UBYTE to 0,
BaseDataType.WORD to 0,
BaseDataType.UWORD to 0,
BaseDataType.FLOAT to 0
)
internal fun buildTempVarName(dt: DataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
internal fun buildTempVarName(dt: BaseDataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
internal fun getTempVarName(dt: DataType): String {
internal fun getTempVarName(dt: BaseDataType): String {
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
}
@@ -582,6 +574,7 @@ class AsmGen6502Internal (
internal fun translate(stmt: PtNode) {
outputSourceLine(stmt)
when(stmt) {
is PtAlign -> if(stmt.align > 1u) out(" .align ${stmt.align.toHex()}")
is PtReturn -> translate(stmt)
is PtSub -> programGen.translateSubroutine(stmt)
is PtAsmSub -> programGen.translateAsmSubroutine(stmt)
@@ -594,8 +587,9 @@ class AsmGen6502Internal (
}
is PtAugmentedAssign -> assignmentAsmGen.translate(stmt)
is PtJump -> {
val (asmLabel, indirect) = getJumpTarget(stmt)
jmp(asmLabel, indirect)
val target = getJumpTarget(stmt)
require(!target.needsExpressionEvaluation)
jmp(target.asmLabel, target.indirect)
}
is PtLabel -> translate(stmt)
is PtConditionalBranch -> translate(stmt)
@@ -604,9 +598,10 @@ class AsmGen6502Internal (
is PtRepeatLoop -> translate(stmt)
is PtWhen -> translate(stmt)
is PtIncludeBinary -> translate(stmt)
is PtBreakpoint -> translate(stmt)
is PtBreakpoint -> translateBrk()
is PtVariable, is PtConstant, is PtMemMapped -> { /* do nothing; variables are handled elsewhere */ }
is PtBlock -> throw AssemblyError("block should have been handled elsewhere")
is PtDefer -> throw AssemblyError("defer should have been transformed")
is PtNodeGroup -> stmt.children.forEach { translate(it) }
is PtNop -> {}
else -> throw AssemblyError("missing asm translation for $stmt")
@@ -620,7 +615,7 @@ class AsmGen6502Internal (
val indexValue = if(expr.splitWords)
indexnum
else
indexnum * options.compTarget.memorySize(expr.type)
indexnum * options.compTarget.memorySize(expr.type, null)
out(" ld$reg #$indexValue")
return
}
@@ -630,11 +625,11 @@ class AsmGen6502Internal (
return
}
when (expr.type) {
in ByteDatatypesWithBoolean -> {
when {
expr.type.isByteOrBool -> {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
}
in WordDatatypes -> {
expr.type.isWord -> {
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
out(" asl a")
when (register) {
@@ -643,8 +638,8 @@ class AsmGen6502Internal (
CpuRegister.Y -> out(" tay")
}
}
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
expr.type.isFloat -> {
require(options.compTarget.machine.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
out("""
sta P8ZP_SCRATCH_REG
@@ -663,7 +658,7 @@ class AsmGen6502Internal (
}
}
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? =
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): BaseDataType? =
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultRegister)
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) =
@@ -685,7 +680,7 @@ class AsmGen6502Internal (
when(reg) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype in SignedDatatypes, true)
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype.isSigned, true)
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY,
@@ -697,8 +692,8 @@ class AsmGen6502Internal (
}
internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) {
when (target.datatype) {
in ByteDatatypesWithBoolean -> {
when {
target.datatype.isByteOrBool -> {
if (value.asConstInteger()==0) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@@ -718,7 +713,7 @@ class AsmGen6502Internal (
}
}
TargetStorageKind.REGISTER -> {
val zero = PtNumber(DataType.UBYTE, 0.0, value.position)
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
zero.parent = value
assignExpressionToRegister(zero, target.register!!, false)
return
@@ -730,7 +725,7 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A)
assignRegister(RegisterOrPair.A, target)
}
in WordDatatypes, in PassByReferenceDatatypes -> {
target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment(
AsmAssignment(
@@ -739,7 +734,7 @@ class AsmGen6502Internal (
), value.definingISub()
)
}
DataType.FLOAT -> {
target.datatype.isFloat -> {
assignExpressionToRegister(value, RegisterOrPair.FAC1)
assignRegister(RegisterOrPair.FAC1, target)
}
@@ -782,7 +777,7 @@ class AsmGen6502Internal (
when {
iterations == 0 -> {}
iterations == 1 -> translate(stmt.statements)
iterations<0 || iterations>65535 -> throw AssemblyError("invalid number of iterations")
iterations<0 || iterations>65536 -> throw AssemblyError("invalid number of iterations")
iterations <= 256 -> repeatByteCount(iterations, stmt)
else -> repeatWordCount(iterations, stmt)
}
@@ -791,25 +786,25 @@ class AsmGen6502Internal (
val symbol = symbolTable.lookup((stmt.count as PtIdentifier).name)
val vardecl = symbol!!.astNode as IPtVariable
val name = asmVariableName(stmt.count as PtIdentifier)
when(vardecl.type) {
DataType.UBYTE, DataType.BYTE -> {
when {
vardecl.type.isByte -> {
assignVariableToRegister(name, RegisterOrPair.Y, stmt.definingISub(), stmt.count.position)
repeatCountInY(stmt, endLabel)
}
DataType.UWORD, DataType.WORD -> {
vardecl.type.isWord -> {
assignVariableToRegister(name, RegisterOrPair.AY, stmt.definingISub(), stmt.count.position)
repeatWordCountInAY(endLabel, stmt)
}
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
else -> throw AssemblyError("invalid loop variable datatype ${vardecl.type}")
}
}
else -> {
when (stmt.count.type) {
in ByteDatatypes -> {
when {
stmt.count.type.isByte -> {
assignExpressionToRegister(stmt.count, RegisterOrPair.Y)
repeatCountInY(stmt, endLabel)
}
in WordDatatypes -> {
stmt.count.type.isWord -> {
assignExpressionToRegister(stmt.count, RegisterOrPair.AY)
repeatWordCountInAY(endLabel, stmt)
}
@@ -822,10 +817,10 @@ class AsmGen6502Internal (
}
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
require(iterations in 257..65535) { "invalid repeat count ${stmt.position}" }
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt)
val loopcount = if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt)
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
out("""
ldy #>$loopcount
lda #<$loopcount
@@ -844,7 +839,7 @@ $repeatLabel""")
// note: A/Y must have been loaded with the number of iterations!
// the iny + double dec is microoptimization of the 16 bit loop
val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, false, stmt)
out("""
cmp #0
beq +
@@ -867,13 +862,13 @@ $repeatLabel""")
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel)
translate(stmt.statements)
out(" dec $counterVar | bne $repeatLabel")
} else {
val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel)
translate(stmt.statements)
@@ -885,13 +880,13 @@ $repeatLabel""")
val repeatLabel = makeLabel("repeat")
out(" cpy #0")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
out(" beq $endLabel | sty $counterVar")
out(repeatLabel)
translate(stmt.statements)
out(" dec $counterVar | bne $repeatLabel")
} else {
val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
out(" beq $endLabel | sty $counterVar")
out(repeatLabel)
translate(stmt.statements)
@@ -900,7 +895,7 @@ $repeatLabel""")
out(endLabel)
}
private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
private fun createRepeatCounterVar(dt: BaseDataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
val scope = stmt.definingISub()!!
val asmInfo = subroutineExtra(scope)
var parent = stmt.parent
@@ -922,8 +917,8 @@ $repeatLabel""")
val counterVar = makeLabel("counter")
when(dt) {
DataType.UBYTE, DataType.UWORD -> {
val result = zeropage.allocate(counterVar, dt, null, stmt.position, errors)
BaseDataType.UBYTE, BaseDataType.UWORD -> {
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
result.fold(
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
@@ -935,33 +930,38 @@ $repeatLabel""")
}
private fun translate(stmt: PtWhen) {
val endLabel = makeLabel("choice_end")
val endLabel = makeLabel("when_end")
val choiceBlocks = mutableListOf<Pair<String, PtNodeGroup>>()
val conditionDt = stmt.value.type
if(conditionDt in ByteDatatypes)
if(conditionDt.isByte)
assignExpressionToRegister(stmt.value, RegisterOrPair.A)
else
assignExpressionToRegister(stmt.value, RegisterOrPair.AY)
for(choiceNode in stmt.choices.children) {
val choice = choiceNode as PtWhenChoice
val choiceLabel = makeLabel("choice")
var choiceLabel = makeLabel("choice")
if(choice.isElse) {
translate(choice.statements)
} else {
choiceBlocks.add(choiceLabel to choice.statements)
if(choice.statements.children.isEmpty()) {
// no statements for this choice value, jump to the end immediately
choiceLabel = endLabel
} else {
choiceBlocks.add(choiceLabel to choice.statements)
}
for (cv in choice.values.children) {
val value = (cv as PtNumber).number.toInt()
if(conditionDt in ByteDatatypes) {
if(conditionDt.isByte) {
out(" cmp #${value.toHex()} | beq $choiceLabel")
} else {
out("""
cmp #<${value.toHex()}
bne +
cpy #>${value.toHex()}
beq $choiceLabel
cmp #<${value.toHex()}
bne +
cpy #>${value.toHex()}
beq $choiceLabel
+
""")
""")
}
}
}
@@ -970,7 +970,7 @@ $repeatLabel""")
for(choiceBlock in choiceBlocks.withIndex()) {
out(choiceBlock.value.first)
translate(choiceBlock.value.second)
if(choiceBlock.index<choiceBlocks.size-1)
if (choiceBlock.index < choiceBlocks.size - 1)
jmp(endLabel)
}
out(endLabel)
@@ -988,16 +988,17 @@ $repeatLabel""")
if(jump!=null) {
// branch with only a jump (goto)
val instruction = branchInstruction(stmt.condition, false)
val (asmLabel, indirect) = getJumpTarget(jump)
if(indirect) {
val target = getJumpTarget(jump)
require(!target.needsExpressionEvaluation)
if(target.indirect) {
val complementedInstruction = branchInstruction(stmt.condition, true)
out("""
$complementedInstruction +
jmp ($asmLabel)
jmp (${target.asmLabel})
+""")
}
else {
out(" $instruction $asmLabel")
out(" $instruction ${target.asmLabel}")
}
translate(stmt.falseScope)
} else {
@@ -1023,43 +1024,48 @@ $repeatLabel""")
}
}
internal fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
val ident = jump.identifier
val addr = jump.address
return when {
ident!=null -> {
// can be a label, or a pointer variable
val symbol = symbolTable.lookup(ident.name)
if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
Pair(asmSymbolName(ident), true) // indirect jump if the jump symbol is a variable
else
Pair(asmSymbolName(ident), false)
class JumpTarget(val asmLabel: String, val indirect: Boolean, val needsExpressionEvaluation: Boolean)
internal fun getJumpTarget(jump: PtJump, evaluateAddressExpression: Boolean = true): JumpTarget {
val ident = jump.target as? PtIdentifier
if(ident!=null) {
// can be a label, or a pointer variable
val symbol = symbolTable.lookup(ident.name)
return if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
JumpTarget(asmSymbolName(ident), true, false) // indirect jump if the jump symbol is a variable
else
JumpTarget(asmSymbolName(ident), false, false)
}
val addr = jump.target.asConstInteger()
if(addr!=null)
return JumpTarget(addr.toHex(), false, false)
else {
if(evaluateAddressExpression) {
// we can do the address evaluation right now and just use a temporary pointer variable
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD))
return JumpTarget("P8ZP_SCRATCH_W1", true, false)
} else {
return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, true)
}
addr!=null -> Pair(addr.toHex(), false)
else -> Pair("????", false)
}
}
private fun translate(ret: PtReturn, withRts: Boolean=true) {
private fun translate(ret: PtReturn) {
ret.value?.let { returnvalue ->
val sub = ret.definingSub()!!
val returnReg = sub.returnRegister()!!
when (sub.returntype) {
in NumericDatatypes, DataType.BOOL -> {
assignExpressionToRegister(returnvalue, returnReg.registerOrPair!!)
}
else -> {
// all else take its address and assign that also to AY register pair
val addrofValue = PtAddressOf(returnvalue.position)
addrofValue.add(returnvalue as PtIdentifier)
addrofValue.parent = ret.parent
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair!!, false)
}
if (sub.returntype?.isNumericOrBool==true) {
assignExpressionToRegister(returnvalue, returnReg.registerOrPair!!)
}
else {
// all else take its address and assign that also to AY register pair
val addrofValue = PtAddressOf(returnvalue.position)
addrofValue.add(returnvalue as PtIdentifier)
addrofValue.parent = ret.parent
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair!!, false)
}
}
if(withRts)
out(" rts")
out(" rts")
}
private fun translate(asm: PtInlineAssembly) {
@@ -1084,7 +1090,7 @@ $repeatLabel""")
out(" .binary \"$pathForAssembler\" $offset $length")
}
private fun translate(brk: PtBreakpoint) {
private fun translateBrk() {
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
breakpointLabels.add(label)
out(label)
@@ -1093,11 +1099,11 @@ $repeatLabel""")
}
}
internal fun signExtendAYlsb(valueDt: DataType) {
internal fun signExtendAYlsb(valueDt: BaseDataType) {
// sign extend signed byte in A to full word in AY
when(valueDt) {
DataType.UBYTE -> out(" ldy #0")
DataType.BYTE -> out("""
BaseDataType.UBYTE -> out(" ldy #0")
BaseDataType.BYTE -> out("""
ldy #0
cmp #$80
bcc +
@@ -1108,16 +1114,16 @@ $repeatLabel""")
}
}
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
internal fun signExtendVariableLsb(asmvar: String, valueDt: BaseDataType) {
// sign extend signed byte in a var to a full word in that variable
when(valueDt) {
DataType.UBYTE -> {
BaseDataType.UBYTE -> {
if(isTargetCpu(CpuType.CPU65c02))
out(" stz $asmvar+1")
else
out(" lda #0 | sta $asmvar+1")
}
DataType.BYTE -> {
BaseDataType.BYTE -> {
out("""
lda $asmvar
ora #$7f
@@ -1150,24 +1156,24 @@ $repeatLabel""")
if (operator != "+") return null
val leftDt = left.type
val rightDt = right.type
if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE)
if(leftDt.isUnsignedWord && rightDt.isUnsignedByte)
return Pair(left, right)
if(leftDt == DataType.UBYTE && rightDt == DataType.UWORD)
if(leftDt.isUnsignedByte && rightDt.isUnsignedWord)
return Pair(right, left)
if(leftDt == DataType.UWORD && rightDt == DataType.UWORD) {
if(leftDt.isUnsignedWord && rightDt.isUnsignedWord) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = right as? PtNumber
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
val num = PtNumber(DataType.UBYTE, constIdx.number, constIdx.position)
val num = PtNumber(BaseDataType.UBYTE, constIdx.number, constIdx.position)
num.parent = right.parent
return Pair(left, num)
}
// could be that the index was typecasted into uword, check that
val rightTc = right as? PtTypeCast
if(rightTc!=null && rightTc.value.type == DataType.UBYTE)
if(rightTc!=null && rightTc.value.type.isUnsignedByte)
return Pair(left, rightTc.value)
val leftTc = left as? PtTypeCast
if(leftTc!=null && leftTc.value.type == DataType.UBYTE)
if(leftTc!=null && leftTc.value.type.isUnsignedByte)
return Pair(right, leftTc.value)
}
return null
@@ -1177,8 +1183,7 @@ $repeatLabel""")
// optimize pointer,indexregister if possible
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
val dt = expr.type
if(dt != DataType.UBYTE && dt != DataType.BYTE)
if(!expr.type.isByte)
return true
return when(expr) {
is PtIdentifier -> false
@@ -1217,14 +1222,14 @@ $repeatLabel""")
if(saveA)
out(" pha")
if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
if(saveA)
out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y")
} else {
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
restoreRegisterStack(CpuRegister.Y, true)
if(saveA)
out(" pla")
@@ -1238,12 +1243,12 @@ $repeatLabel""")
} else {
// copy the pointer var to zp first
if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (P8ZP_SCRATCH_W2),y")
} else {
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
restoreRegisterStack(CpuRegister.Y, false)
out(" lda (P8ZP_SCRATCH_W2),y")
}
@@ -1268,22 +1273,22 @@ $repeatLabel""")
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
if(left.isSimple()) {
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
pushCpuStack(BaseDataType.UBYTE, left)
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
out(" pla")
}
}
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
if(left.isSimple()) {
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
pushCpuStack(BaseDataType.UWORD, left)
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@@ -1292,7 +1297,7 @@ $repeatLabel""")
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
fun assignViaExprEval() {
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
if (isTargetCpu(CpuType.CPU65c02)) {
out(" lda (P8ZP_SCRATCH_W2)")
} else {
@@ -1319,12 +1324,12 @@ $repeatLabel""")
}
}
internal fun pushCpuStack(dt: DataType, value: PtExpression) {
val signed = value.type.oneOf(DataType.BYTE, DataType.WORD)
if(dt in ByteDatatypesWithBoolean) {
internal fun pushCpuStack(dt: BaseDataType, value: PtExpression) {
val signed = value.type.isSigned
if(dt.isByteOrBool) {
assignExpressionToRegister(value, RegisterOrPair.A, signed)
out(" pha")
} else if(dt in WordDatatypes) {
} else if(dt.isWord) {
assignExpressionToRegister(value, RegisterOrPair.AY, signed)
if (isTargetCpu(CpuType.CPU65c02))
out(" pha | phy")
@@ -1363,14 +1368,12 @@ $repeatLabel""")
extra
}
private var generatedLabelSequenceNumber: Int = 0
internal fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++
return "label_asm_${generatedLabelSequenceNumber}_$postfix"
return "${PtLabel.GENERATED_LABEL_PREFIX}${generatedLabelSequenceNumber}_$postfix"
}
fun assignConstFloatToPointerAY(number: PtNumber) {
internal fun assignConstFloatToPointerAY(number: PtNumber) {
val floatConst = allocator.getFloatAsmConst(number.number)
out("""
pha
@@ -1381,6 +1384,177 @@ $repeatLabel""")
pla
jsr floats.copy_float""")
}
internal fun assignIfExpression(target: AsmAssignTarget, value: PtIfExpression) {
ifExpressionAsmgen.assignIfExpression(target, value)
}
internal fun cmpAwithByteValue(value: PtExpression, useSbc: Boolean) {
val compare = if(useSbc) "sec | sbc" else "cmp"
fun cmpViaScratch() {
if(assignmentAsmGen.directIntoY(value)) {
assignExpressionToRegister(value, RegisterOrPair.Y, false)
out(" sty P8ZP_SCRATCH_REG")
} else {
out(" pha")
assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", value.type)
out(" pla")
}
out(" $compare P8ZP_SCRATCH_REG")
}
when(value) {
is PtArrayIndexer -> {
val constIndex = value.index.asConstInteger()
if(constIndex!=null) {
val offset = program.memsizer.memorySize(value.type, constIndex)
if(offset<256) {
return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y")
}
}
cmpViaScratch()
}
is PtMemoryByte -> {
val constAddr = value.address.asConstInteger()
if(constAddr!=null) {
out(" $compare ${constAddr.toHex()}")
} else {
cmpViaScratch()
}
}
is PtIdentifier -> {
out(" $compare ${asmVariableName(value)}")
}
is PtNumber -> {
if(value.number!=0.0)
out(" $compare #${value.number.toInt()}")
}
else -> {
cmpViaScratch()
}
}
}
internal fun immediateAndInplace(name: String, value: Int) {
if(isTargetCpu(CpuType.CPU65c02)) {
out(" lda #${value xor 255} | trb $name") // reset bit
} else {
out(" lda $name | and #$value | sta $name")
}
}
internal fun immediateOrInplace(name: String, value: Int) {
if(isTargetCpu(CpuType.CPU65c02)) {
out(" lda #$value | tsb $name") // set bit
} else {
out(" lda $name | ora #$value | sta $name")
}
}
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
assignExpressionToRegister(condition, RegisterOrPair.A, false)
when(condition) {
is PtNumber,
is PtBool,
is PtIdentifier,
is PtIrRegister,
is PtArrayIndexer,
is PtPrefix,
is PtIfExpression,
is PtBinaryExpression -> { /* no cmp necessary the lda has been done just prior */ }
is PtTypeCast -> {
if(!condition.value.type.isByte && !condition.value.type.isWord)
out(" cmp #0")
}
else -> out(" cmp #0")
}
}
internal fun translateFloatsEqualsConditionIntoA(left: PtExpression, right: PtExpression) {
fun equalf(leftName: String, rightName: String) {
out("""
lda #<$leftName
ldy #>$leftName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$rightName
ldy #>$rightName
jsr floats.vars_equal_f""")
}
fun equalf(expr: PtExpression, rightName: String) {
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
out("""
lda #<$rightName
ldy #>$rightName
jsr floats.var_fac1_equal_f""")
}
if(left is PtIdentifier) {
when (right) {
is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right))
is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
equalf(asmVariableName(left), subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
} else {
when (right) {
is PtIdentifier -> equalf(left, asmVariableName(right))
is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
equalf(left, subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
}
}
internal fun translateFloatsLessConditionIntoA(left: PtExpression, right: PtExpression, lessOrEquals: Boolean) {
fun lessf(leftName: String, rightName: String) {
out("""
lda #<$rightName
ldy #>$rightName
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<$leftName
ldy #>$leftName""")
if(lessOrEquals)
out("jsr floats.vars_lesseq_f")
else
out("jsr floats.vars_less_f")
}
fun lessf(expr: PtExpression, rightName: String) {
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
out(" lda #<$rightName | ldy #>$rightName")
if(lessOrEquals)
out(" jsr floats.var_fac1_lesseq_f")
else
out(" jsr floats.var_fac1_less_f")
}
if(left is PtIdentifier) {
when (right) {
is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right))
is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
lessf(asmVariableName(left), subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
} else {
when (right) {
is PtIdentifier -> lessf(left, asmVariableName(right))
is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
lessf(left, subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
}
}
}
/**
@@ -1393,5 +1567,5 @@ internal class SubroutineExtraAsmInfo {
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
val extraVars = mutableListOf<Triple<DataType, String, UInt?>>()
val extraVars = mutableListOf<Triple<BaseDataType, String, UInt?>>()
}
@@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
import prog8.code.StConstant
import prog8.code.StMemVar
import prog8.code.SymbolTable
import prog8.code.ast.PtLabel
import prog8.code.core.IMachineDefinition
@@ -361,15 +362,16 @@ or *_afterif labels.
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
*/
val autoLabelPrefix = PtLabel.GENERATED_LABEL_PREFIX
if(first=="beq +" && second=="lda #1" && third=="+") {
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
(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_")) &&
else if(fourth.startsWith(autoLabelPrefix) && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
if((fifth.startsWith("beq $autoLabelPrefix") || fifth.startsWith("bne $autoLabelPrefix")) &&
(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))
@@ -519,8 +521,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
if(!haslabel(second)) {
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)
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)
@@ -11,18 +11,22 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
// 1) cx16 virtual word registers,
// 2) paired CPU registers,
// 3) single CPU registers (order Y,X,A),
// 4) CPU Carry status flag
// 4) floating point registers (FAC1, FAC2),
// 5) CPU Carry status flag
val args = sub.parameters.withIndex()
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
val (singleRegs, rest) = args3.partition { it.value.first.registerOrPair != null }
val (singleRegsMixed, rest) = args3.partition { it.value.first.registerOrPair != null }
val (singleCpuRegs, floatRegs) = singleRegsMixed.partition {it.value.first.registerOrPair != RegisterOrPair.FAC1 && it.value.first.registerOrPair != RegisterOrPair.FAC2 }
cx16regs.forEach { order += it.index }
pairedRegs.forEach { order += it.index }
singleRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
singleCpuRegs.sortedBy { it.value.first.registerOrPair!!.asCpuRegister() }.asReversed().forEach { order += it.index }
require(rest.all { it.value.first.registerOrPair==null && it.value.first.statusflag!=null})
floatRegs.forEach { order += it.index }
rest.forEach { order += it.index }
require(order.size==sub.parameters.size)
return order
}
@@ -1,7 +1,10 @@
package prog8.codegen.cpu6502
import prog8.code.ast.PtLabel
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,13 +19,14 @@ 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 = arrayOf(AtariTarget.NAME, Neo6502Target.NAME)
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
val assemblerCommand: List<String>
when (compTarget.name) {
in setOf("c64", "c128", "cx16", "pet32") -> {
in arrayOf("c64", "c128", "cx16", "pet32") -> {
// CBM machines .prg generation.
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
@@ -96,12 +100,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()
}
@@ -109,7 +148,7 @@ internal class AssemblyProgram(
}
private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""")
val lines = viceMonListFile.toFile().readLines()
viceMonListFile.toFile().outputStream().bufferedWriter().use {
for (line in lines) {
@@ -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.*
@@ -11,7 +9,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): BaseDataType? {
return translateFunctioncall(fcall, discardResult = false, resultRegister = resultRegister)
}
@@ -19,13 +17,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
translateFunctioncall(fcall, discardResult = true, resultRegister = null)
}
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): DataType? {
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): BaseDataType? {
if (discardResult && fcall.hasNoSideEffects)
return null // can just ignore the whole function call altogether
val sscope = fcall.definingISub()
when (fcall.name) {
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
"msb" -> funcMsb(fcall, resultRegister)
"lsb" -> funcLsb(fcall, resultRegister)
"mkword" -> funcMkword(fcall, resultRegister)
@@ -55,7 +55,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
memread.parent = fcall
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
asmgen.out(" pha")
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.position, memory=memread)
asmgen.assignExpressionTo(fcall.args[1], memtarget)
asmgen.out(" pla")
}
@@ -69,116 +69,27 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"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)
"prog8_lib_arraycopy" -> funcArrayCopy(fcall)
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
}
return BuiltinFunctions.getValue(fcall.name).returnType
}
private fun funcArrayCopy(fcall: PtBuiltinFunctionCall) {
val source = fcall.args[0] as PtIdentifier
val target = fcall.args[1] as PtIdentifier
val numElements = when(val sourceSymbol = asmgen.symbolTable.lookup(source.name)) {
is StStaticVariable -> sourceSymbol.length!!
is StMemVar -> sourceSymbol.length!!
else -> 0
}
val sourceAsm = asmgen.asmVariableName(source)
val targetAsm = asmgen.asmVariableName(target)
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
// split -> split words (copy lsb and msb arrays separately)
asmgen.out("""
lda #<${sourceAsm}_lsb
ldy #>${sourceAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_lsb
ldy #>${targetAsm}_lsb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numElements and 255}
jsr prog8_lib.memcopy_small
lda #<${sourceAsm}_msb
ldy #>${sourceAsm}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_msb
ldy #>${targetAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numElements and 255}
jsr prog8_lib.memcopy_small""")
}
else if(source.type in SplitWordArrayTypes) {
// split word array to normal word array (copy lsb and msb arrays separately)
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
asmgen.out("""
lda #<${sourceAsm}_lsb
ldy #>${sourceAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${sourceAsm}_msb
ldy #>${sourceAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<${targetAsm}
ldy #>${targetAsm}
ldx #${numElements and 255}
jsr prog8_lib.arraycopy_split_to_normal_words""")
}
else if(target.type in SplitWordArrayTypes) {
// normal word array to split array
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
asmgen.out("""
lda #<${targetAsm}_lsb
ldy #>${targetAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_msb
ldy #>${targetAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<${sourceAsm}
ldy #>${sourceAsm}
ldx #${numElements and 255}
jsr prog8_lib.arraycopy_normal_to_split_words""")
}
else {
// normal array to array copy, various element types
val eltsize = asmgen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
val numBytes = numElements * eltsize
asmgen.out("""
lda #<${sourceAsm}
ldy #>${sourceAsm}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}
ldy #>${targetAsm}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numBytes and 255}
jsr prog8_lib.memcopy_small""")
}
}
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) {
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: BaseDataType, resultRegister: RegisterOrPair?) {
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
when (resultType) {
DataType.UBYTE -> {
BaseDataType.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 -> {
BaseDataType.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)
}
@@ -195,11 +106,11 @@ 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)
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[3].position, var3name)
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
}
@@ -207,11 +118,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
// 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")
// 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 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 divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
asmgen.out("""
@@ -287,7 +198,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
return
}
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.forDt(BaseDataType.UWORD)) // jump address
asmgen.out("""
; push a return address so the jmp becomes indirect jsr
lda #>((+)-1)
@@ -299,15 +210,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
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 cx16.JSRFAR
jsr $jsrfar
.word ${constAddress.toHex()}
.byte $constBank""")
} else {
@@ -317,7 +235,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" sta (+)+0 | sty (+)+1")
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.AY) // uword argument
asmgen.out("""
jsr cx16.JSRFAR
jsr $jsrfar
+ .word 0
+ .byte 0""")
}
@@ -329,8 +247,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcCallFar2(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callfar2 only works on cx16 target at this time")
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) {
@@ -351,12 +270,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
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 cx16.JSRFAR
jsr $jsrfar
.word ${constAddress.toHex()}
.byte $constBank""")
} else {
@@ -366,7 +291,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" sta (+)+0 | sty (+)+1")
assignArgs()
asmgen.out("""
jsr cx16.JSRFAR
jsr $jsrfar
+ .word 0
+ .byte 0""")
}
@@ -380,8 +305,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcCmp(fcall: PtBuiltinFunctionCall) {
val arg1 = fcall.args[0]
val arg2 = fcall.args[1]
if(arg1.type in ByteDatatypes) {
if(arg2.type in ByteDatatypes) {
if(arg1.type.isByte) {
if(arg2.type.isByte) {
when (arg2) {
is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
@@ -413,7 +338,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("args for cmp() should have same dt")
} else {
// arg1 is a word
if(arg2.type in WordDatatypes) {
if(arg2.type.isWord) {
when (arg2) {
is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
@@ -451,11 +376,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as PtString).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.forDt(BaseDataType.UWORD), fcall.position)
val addressOf = PtAddressOf(fcall.position)
addressOf.add(slabname)
addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UWORD), expression = addressOf)
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign, fcall.definingISub())
@@ -463,16 +388,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
when(fcall.args[0].type) {
DataType.UBYTE -> {
when(fcall.args[0].type.base) {
BaseDataType.UBYTE -> {
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
@@ -482,8 +407,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRor2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -506,7 +431,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -529,8 +454,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRor(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -573,7 +498,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -598,8 +523,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRol2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -622,7 +547,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -645,8 +570,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRol(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -689,7 +614,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -717,27 +642,33 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when(fcall.args[0]) {
is PtIdentifier -> {
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname)
}
is PtNumber -> {
val num = (fcall.args[0] as PtNumber).number + if(msb) 1 else 0
val mem = PtMemoryByte(fcall.position)
mem.add(PtNumber(DataType.UBYTE, num, fcall.position))
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
mem.add(PtNumber(BaseDataType.UBYTE, num, fcall.position))
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
}
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])
address.add(PtNumber(address.type, 1.0, fcall.args[0].position))
mem.add(address)
val addrof = fcall.args[0] as PtAddressOf
if(addrof.identifier.type.isSplitWordArray) {
TODO("address of split word array")
} else {
mem.add(fcall.args[0])
val mem = PtMemoryByte(fcall.position)
if(addrof.isFromArrayElement)
TODO("address-of arrayelement")
if(msb) {
val address = PtBinaryExpression("+", DataType.forDt(BaseDataType.UWORD), addrof.position)
address.add(addrof)
address.add(PtNumber(address.type.base, 1.0, addrof.position))
mem.add(address)
} else {
mem.add(addrof)
}
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
}
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
}
is PtArrayIndexer -> {
val indexer = fcall.args[0] as PtArrayIndexer
@@ -745,7 +676,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val msbAdd: Int
if(indexer.splitWords) {
val arrayVariable = indexer.variable
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.ARRAY_UB, arrayVariable.position)
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
indexer.children[0].parent = indexer
elementSize = 1
msbAdd = 0
@@ -757,7 +688,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// double the index because of word array (if not split), add one if msb (if not split)
val constIndexNum = (indexer.index as? PtNumber)?.number
if(constIndexNum!=null) {
indexer.children[1] = PtNumber(indexer.index.type, constIndexNum*elementSize + msbAdd, indexer.position)
indexer.children[1] = PtNumber(indexer.index.type.base, constIndexNum*elementSize + msbAdd, indexer.position)
indexer.children[1].parent = indexer
} else {
val multipliedIndex: PtExpression
@@ -766,12 +697,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
multipliedIndex = PtBinaryExpression("<<", indexer.index.type, indexer.position)
multipliedIndex.add(indexer.index)
multipliedIndex.add(PtNumber(DataType.UBYTE, 1.0, indexer.position))
multipliedIndex.add(PtNumber(BaseDataType.UBYTE, 1.0, indexer.position))
}
if(msbAdd>0) {
val msbIndex = PtBinaryExpression("+", indexer.index.type, indexer.position)
msbIndex.add(multipliedIndex)
msbIndex.add(PtNumber(DataType.UBYTE, msbAdd.toDouble(), indexer.position))
msbIndex.add(PtNumber(BaseDataType.UBYTE, msbAdd.toDouble(), indexer.position))
indexer.children[1] = msbIndex
msbIndex.parent = indexer
} else {
@@ -779,7 +710,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
multipliedIndex.parent=indexer
}
}
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, array = indexer)
}
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
}
@@ -794,12 +725,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
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")
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
when (val dt = fcall.args.single().type.base) {
BaseDataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
BaseDataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
BaseDataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
BaseDataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
BaseDataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true, true)
@@ -807,24 +738,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
val dt = fcall.args.single().type.base
when (dt) {
DataType.BYTE -> {
BaseDataType.BYTE -> {
asmgen.out(" jsr prog8_lib.abs_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false, true)
}
DataType.WORD -> {
BaseDataType.WORD -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
DataType.UBYTE -> {
BaseDataType.UBYTE -> {
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
}
else -> throw AssemblyError("weird type")
@@ -850,8 +781,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignConstFloatToPointerAY(number)
}
else -> {
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT))
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out("""
pha
@@ -926,7 +857,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr floats.MOVFM")
if(resultRegister!=null) {
assignAsmGen.assignFAC1float(
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
}
}
@@ -994,9 +925,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
when(fcall.type) {
in ByteDatatypes -> {
val signed = fcall.type.isSigned
when {
fcall.type.isByte -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
@@ -1004,7 +935,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed, true)
}
in WordDatatypes -> {
fcall.type.isWord -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value
@@ -1017,9 +948,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcMin(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
when (fcall.type) {
in ByteDatatypes -> {
val signed = fcall.type.isSigned
when {
fcall.type.isByte -> {
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left
asmgen.out(" cmp P8ZP_SCRATCH_B1")
@@ -1030,7 +961,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
}
in WordDatatypes -> {
fcall.type.isWord -> {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) {
@@ -1075,8 +1006,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
val signed = fcall.type.isSigned
if(fcall.type.isByte) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
asmgen.out(" cmp P8ZP_SCRATCH_B1")
@@ -1086,7 +1017,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
+""")
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
} else if(fcall.type in WordDatatypes) {
} else if(fcall.type.isWord) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) {
@@ -1177,7 +1108,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (arg.type !in WordDatatypes)
if (!arg.type.isWord)
throw AssemblyError("msb required word argument")
if (arg is PtNumber)
throw AssemblyError("msb(const) should have been const-folded away")
@@ -1200,7 +1131,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
if(arg is PtArrayIndexer && resultRegister in setOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
if(arg is PtArrayIndexer && resultRegister in arrayOf(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"
@@ -1276,7 +1207,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (arg.type !in WordDatatypes)
if (!arg.type.isWord)
throw AssemblyError("lsb required word argument")
if (arg is PtNumber)
throw AssemblyError("lsb(const) should have been const-folded away")
@@ -1300,7 +1231,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
if(arg is PtArrayIndexer && resultRegister in setOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the lsb byte out of the word array
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
when(resultRegister) {
@@ -1357,25 +1288,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun outputAddressAndLengthOfArray(arg: PtIdentifier) {
// address goes in P8ZP_SCRATCH_W1, number of elements in A
val numElements = when(val symbol = asmgen.symbolTable.lookup(arg.name)) {
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 and 255}""")
}
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
val signature = BuiltinFunctions.getValue(call.name)
val callConv = signature.callConvention(call.args.map { it.type})
val callConv = signature.callConvention(call.args.map {
require(it.type.isNumericOrBool)
it.type.base
})
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
return when (value) {
@@ -1393,11 +1311,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position)
val addr = PtAddressOf(value.position)
addr.add(variable)
addr.parent = call
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT))
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
}
@@ -1410,9 +1328,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when {
conv.variable -> {
val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
val src = when (conv.dt) {
DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> {
val src = when {
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> {
// put the address of the argument in AY
val addr = PtAddressOf(value.position)
addr.add(value)
@@ -1423,14 +1341,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
AsmAssignSource.fromAstSource(value, program, asmgen)
}
}
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, value.position, variableAsmName = varname)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(conv.dt), null, value.position, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign, scope)
}
conv.reg != null -> {
val src = when (conv.dt) {
DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> {
val src = when {
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> {
// put the address of the argument in AY
val addr = PtAddressOf(value.position)
addr.add(value)
@@ -3,7 +3,9 @@ package prog8.codegen.cpu6502
import prog8.code.ast.IPtSubroutine
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtSub
import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.RegisterOrPair
import prog8.code.core.RegisterOrStatusflag
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
@@ -16,10 +18,10 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
return if(returntype==null)
emptyList()
else {
val register = when (returntype!!) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
val register = when {
returntype!!.isByteOrBool -> RegisterOrStatusflag(RegisterOrPair.A, null)
returntype!!.isWord -> RegisterOrStatusflag(RegisterOrPair.AY, null)
returntype!!.isFloat -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
listOf(Pair(register, returntype!!))
@@ -30,11 +32,11 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
return when(returntype) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
null -> null
return when {
returntype?.isByteOrBool==true -> RegisterOrStatusflag(RegisterOrPair.A, null)
returntype?.isWord==true -> RegisterOrStatusflag(RegisterOrPair.AY, null)
returntype?.isFloat==true -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
returntype==null -> null
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
}
@@ -46,10 +46,10 @@ internal class ForLoopsAsmGen(
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
}
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
when {
iterableDt.isByteArray -> {
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
if (stepsize==-1 && range.to.asConstInteger()==0) {
// simple loop downto 0 step -1
asmgen.out(loopLabel)
@@ -75,7 +75,7 @@ internal class ForLoopsAsmGen(
// loop over byte range via loopvar
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0) {
asmgen.out("""
@@ -121,7 +121,7 @@ $modifiedLabel cmp #0 ; modified
// loop over byte range via loopvar
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
@@ -171,7 +171,7 @@ $modifiedLabel cmp #0 ; modified
asmgen.out(endLabel)
}
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range)
if(stepsize==-1 && range.to.asConstInteger()==0) {
@@ -249,7 +249,7 @@ $modifiedLabel2 cmp #0 ; modified
$loopLabel""")
asmgen.translate(stmt.statements)
if (iterableDt == DataType.ARRAY_UW) {
if (iterableDt.isUnsignedWordArray) {
asmgen.out("""
lda $varname
clc
@@ -324,7 +324,7 @@ $endLabel""")
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
// pre-check for end already reached.
// 'to' is in AY, do NOT clobber this!
if(iterableDt==DataType.ARRAY_W) {
if(iterableDt.isSignedWordArray) {
if(stepsize<0)
asmgen.out("""
sta P8ZP_SCRATCH_W2 ; to
@@ -381,8 +381,8 @@ $endLabel""")
is StMemVar -> symbol.length!!
else -> 0
}
when(iterableDt) {
DataType.STR -> {
when {
iterableDt.isString -> {
asmgen.out("""
lda #<$iterableName
ldy #>$iterableName
@@ -399,7 +399,7 @@ $loopLabel lda ${65535.toHex()} ; modified
bne $loopLabel
$endLabel""")
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
iterableDt.isByteArray || iterableDt.isBoolArray -> {
val indexVar = asmgen.makeLabel("for_index")
asmgen.out("""
ldy #0
@@ -424,7 +424,7 @@ $loopLabel sty $indexVar
}
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
result.fold(
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
@@ -434,7 +434,45 @@ $loopLabel sty $indexVar
}
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
iterableDt.isSplitWordArray -> {
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda ${iterableName}_lsb,y
sta $loopvarName
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
asmgen.out("$indexVar .byte 0")
}
asmgen.out(endLabel)
}
iterableDt.isWordArray -> {
val length = numElements * 2
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
@@ -465,7 +503,7 @@ $loopLabel sty $indexVar
}
if(length>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
@@ -475,45 +513,7 @@ $loopLabel sty $indexVar
}
asmgen.out(endLabel)
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda ${iterableName}_lsb,y
sta $loopvarName
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
asmgen.out("$indexVar .byte 0")
}
asmgen.out(endLabel)
}
DataType.ARRAY_F -> {
iterableDt.isFloatArray -> {
throw AssemblyError("for loop with floating point variables is not supported")
}
else -> throw AssemblyError("can't iterate over $iterableDt")
@@ -524,12 +524,12 @@ $loopLabel sty $indexVar
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(iterableDt.isByteArray) {
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, iterableDt==DataType.ARRAY_UB)
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt.isUnsignedByteArray)
}
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
else if(iterableDt.isWordArray) {
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)
@@ -539,8 +539,8 @@ $loopLabel sty $indexVar
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.add(endLabel)
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
when {
iterableDt.isByteArray -> {
// loop over byte range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
@@ -605,7 +605,7 @@ $loopLabel""")
}
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
// loop over word range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.variable)
when (range.step) {
@@ -15,9 +15,12 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// just ignore any result values from the function call.
}
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>) =
when(params.size) {
1 -> params[0].type.isIntegerOrBool
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool
else -> false
}
internal fun translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call
@@ -40,27 +43,104 @@ 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 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
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pla")
}
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
// Note that if the args fit into cpu registers, we don't concern ourselves here
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
// That is now up to the subroutine itself.
useCpuRegistersForArgs(call.args, sub)
} else {
// arguments via variables
for(arg in sub.parameters.withIndex().zip(call.args))
argumentViaVariable(sub, arg.first.value, arg.second)
val (normalParams, registerParams) = sub.parameters.withIndex().partition { it.value.register == null }
if (normalParams.isNotEmpty()) {
for (arg in normalParams.zip(call.args))
argumentViaVariable(sub, arg.first.value, arg.second)
}
if (registerParams.isNotEmpty()) {
// the R0-R15 'registers' are not really registers. They're just special variables.
for (arg in registerParams.zip(call.args))
argumentViaVariable(sub, arg.first.value, arg.second)
}
}
asmgen.out(" jsr $subAsmName")
}
@@ -69,11 +149,35 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
}
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
val params = sub.parameters
when(params.size) {
1 -> {
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], register)
}
2 -> {
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
// 2 byte params, second in Y, first in A
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
if(asmgen.needAsaveForExpr(args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
if(asmgen.needAsaveForExpr(args[1]))
asmgen.out(" pla")
} else {
throw AssemblyError("cannot use registers for word+byte")
}
}
else -> throw AssemblyError("cannot use cpu registers for >2 arguments")
}
}
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
return when(arg) {
is PtBuiltinFunctionCall -> {
if (arg.name == "lsb" || arg.name == "msb")
if (arg.name in arrayOf("lsb", "msb", "lsw", "msw"))
return usesOtherRegistersWhileEvaluating(arg.args[0])
if (arg.name == "mkword")
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
@@ -131,8 +235,15 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(!isArgumentTypeCompatible(value.type, parameter.type))
throw AssemblyError("argument type incompatible")
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type)
val reg = parameter.register
if(reg!=null) {
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
val varName = "cx16.${reg.name.lowercase()}"
asmgen.assignExpressionToVariable(value, varName, parameter.type)
} else {
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type)
}
}
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
@@ -148,7 +259,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type
if(requiredDt!=value.type) {
if(value.type largerThan requiredDt)
if(value.type.largerSizeThan(requiredDt))
throw AssemblyError("can only convert byte values to word param types")
}
if (statusflag!=null) {
@@ -187,21 +298,20 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
else {
// via register or register pair
register!!
if(requiredDt largerThan value.type) {
if(requiredDt.largerSizeThan(value.type)) {
// we need to sign extend the source, do this via temporary word variable
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE))
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
} else {
val scope = value.definingISub()
val target: AsmAssignTarget =
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
else {
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
}
val src = if(value.type in PassByReferenceDatatypes) {
val src = if(value.type.isPassByRef) {
if(value is PtIdentifier) {
val addr = PtAddressOf(Position.DUMMY)
addr.add(value)
@@ -222,18 +332,18 @@ 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)
if(argType.isBool && paramType.isBool)
return true
if(argType in ByteDatatypes && paramType in ByteDatatypes)
if(argType.isByte && paramType.isByte)
return true
if(argType in WordDatatypes && paramType in WordDatatypes)
if(argType.isWord && paramType.isWord)
return true
// we have a special rule for some types.
// strings are assignable to UWORD, for example, and vice versa
if(argType==DataType.STR && paramType==DataType.UWORD)
if(argType.isString && paramType.isUnsignedWord)
return true
if(argType==DataType.UWORD && paramType == DataType.STR)
if(argType.isUnsignedWord && paramType.isString)
return true
return false
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,370 @@
package prog8.codegen.cpu6502
import prog8.code.ast.PtBinaryExpression
import prog8.code.ast.PtBuiltinFunctionCall
import prog8.code.ast.PtExpression
import prog8.code.ast.PtIdentifier
import prog8.code.ast.PtIfExpression
import prog8.code.ast.PtNumber
import prog8.code.ast.PtPrefix
import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
import prog8.code.core.CpuRegister
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.LogicalOperators
import prog8.code.core.RegisterOrPair
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
require(target.datatype==expr.type)
val falseLabel = asmgen.makeLabel("ifexpr_false")
val endLabel = asmgen.makeLabel("ifexpr_end")
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
when {
expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
expr.type.isFloat -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else -> throw AssemblyError("weird dt")
}
}
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
if (condition is PtBinaryExpression) {
val rightDt = condition.right.type
return when {
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
else -> throw AssemblyError("weird dt")
}
}
else if(condition is PtPrefix && condition.operator=="not") {
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
} else {
// 'simple' condition, check if it is a byte bittest
val bittest = condition as? PtBuiltinFunctionCall
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
val variable = bittest.args[0] as PtIdentifier
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
val testForBitSet = bittest.name.endsWith("_set")
when (bitnumber) {
7 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bpl $falseLabel")
else asmgen.out(" bmi $falseLabel")
return
}
6 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bvc $falseLabel")
else asmgen.out(" bvs $falseLabel")
return
}
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
}
}
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
private fun translateIfExpressionByteConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
val signed = condition.left.type.isSigned
val constValue = condition.right.asConstInteger()
if(constValue==0) {
return translateIfCompareWithZeroByteBranch(condition, signed, falseLabel)
}
when(condition.operator) {
"==" -> {
// if X==value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
asmgen.out(" bne $falseLabel")
}
"!=" -> {
// if X!=value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
asmgen.out(" beq $falseLabel")
}
in LogicalOperators -> {
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A)
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
asmgen.out(" beq $falseLabel")
} else {
errors.warn("SLOW FALLBACK FOR 'IFEXPR' CODEGEN - ask for support", condition.position) // should not occur ;-)
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
else -> {
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
// TODO: special cases for <, <=, >, >= above.
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
}
private fun translateIfExpressionWordConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
// TODO can we reuse this whole thing from IfElse ?
val constValue = condition.right.asConstInteger()
if(constValue!=null) {
if (constValue == 0) {
when (condition.operator) {
"==" -> return translateWordExprIsZero(condition.left, falseLabel)
"!=" -> return translateWordExprIsNotZero(condition.left, falseLabel)
}
}
if (constValue != 0) {
when (condition.operator) {
"==" -> return translateWordExprEqualsNumber(condition.left, constValue, falseLabel)
"!=" -> return translateWordExprNotEqualsNumber(condition.left, constValue, falseLabel)
}
}
}
val variable = condition.right as? PtIdentifier
if(variable!=null) {
when (condition.operator) {
"==" -> return translateWordExprEqualsVariable(condition.left, variable, falseLabel)
"!=" -> return translateWordExprNotEqualsVariable(condition.left, variable, falseLabel)
}
}
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) {
val constValue = (condition.right as? PtNumber)?.number
if(constValue==0.0) {
if (condition.operator == "==") {
// if FL==0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | bne $elseLabel")
return
} else if(condition.operator=="!=") {
// if FL!=0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | beq $elseLabel")
return
}
}
when(condition.operator) {
"==" -> {
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" beq $elseLabel")
}
"!=" -> {
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" bne $elseLabel")
}
"<" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" beq $elseLabel")
}
"<=" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" beq $elseLabel")
}
">" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" bne $elseLabel")
}
">=" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" bne $elseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
private fun translateWordExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
// if w!=variable
// TODO reuse code from ifElse?
val varRight = asmgen.asmVariableName(variable)
if(expr is PtIdentifier) {
val varLeft = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varLeft
cmp $varRight
bne +
lda $varLeft+1
cmp $varRight+1
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out("""
cmp $varRight
bne +
cpy $varRight+1
beq $falseLabel
+""")
}
}
private fun translateWordExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
// if w==variable
// TODO reuse code from ifElse?
val varRight = asmgen.asmVariableName(variable)
if(expr is PtIdentifier) {
val varLeft = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varLeft
cmp $varRight
bne $falseLabel
lda $varLeft+1
cmp $varRight+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out("""
cmp $varRight
bne $falseLabel
cpy $varRight+1
bne $falseLabel""")
}
}
private fun translateWordExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if w!=number
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
cmp #<$number
bne +
lda $varname+1
cmp #>$number
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out("""
cmp #<$number
bne +
cpy #>$number
beq $falseLabel
+""")
}
}
private fun translateWordExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if w==number
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
cmp #<$number
bne $falseLabel
lda $varname+1
cmp #>$number
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out( """
cmp #<$number
bne $falseLabel
cpy #>$number
bne $falseLabel""")
}
}
private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) {
// if w!=0
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
ora $varname+1
beq $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
}
}
private fun translateWordExprIsZero(expr: PtExpression, falseLabel: String) {
// if w==0
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
ora $varname+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
}
}
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
// optimized code for byte comparisons with 0
asmgen.assignConditionValueToRegisterAndTest(condition.left)
when (condition.operator) {
"==" -> asmgen.out(" bne $falseLabel")
"!=" -> asmgen.out(" beq $falseLabel")
">" -> {
if(signed) asmgen.out(" bmi $falseLabel | beq $falseLabel")
else asmgen.out(" beq $falseLabel")
}
">=" -> {
if(signed) asmgen.out(" bmi $falseLabel")
else { /* always true for unsigned */ }
}
"<" -> {
if(signed) asmgen.out(" bpl $falseLabel")
else asmgen.jmp(falseLabel)
}
"<=" -> {
if(signed) {
// inverted '>'
asmgen.out("""
beq +
bpl $falseLabel
+""")
} else asmgen.out(" bne $falseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
}
@@ -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,12 +91,13 @@ internal class ProgramAndVarsGen(
OutputType.RAW -> {
asmgen.out("; ---- raw assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
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.PRG -> {
when(options.launcher) {
@@ -104,40 +107,43 @@ internal class ProgramAndVarsGen(
}
asmgen.out("; ---- basic program with sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
val year = LocalDate.now().year
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("prog8_program_start\t; start of program label")
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("prog8_program_start\t; start of program label")
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")
}
}
@@ -155,19 +161,19 @@ 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 sys.cleanup_at_exit")
asmgen.out(" jmp p8_sys_startup.cleanup_at_exit")
}
}
}
@@ -192,12 +198,12 @@ 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 ?")
DataType.UWORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
BaseDataType.BOOL -> asmgen.out("$name .byte ?")
BaseDataType.BYTE -> asmgen.out("$name .char ?")
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
BaseDataType.WORD -> asmgen.out("$name .sint ?")
BaseDataType.UWORD -> asmgen.out("$name .word ?")
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
@@ -284,6 +290,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-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
}
private fun block2asm(block: PtBlock) {
@@ -291,12 +299,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)
@@ -419,22 +421,35 @@ internal class ProgramAndVarsGen(
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
asmgen.out("; simple int arg(s) passed via register(s)")
if(sub.parameters.size==1) {
val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypesWithBoolean)
asmgen.assignRegister(RegisterOrPair.A, target)
val params = sub.parameters
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
asmgen.out("; simple int arg(s) passed via cpu register(s)")
fun varname(param: PtSubroutineParameter): String =
if(param.register==null)
param.name
else
asmgen.assignRegister(RegisterOrPair.AY, target)
} else {
require(sub.parameters.size==2)
// 2 simple byte args, first in A, second in Y
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name)
asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2)
param.register!!.asScopedNameVirtualReg(param.type).joinToString(".")
when(params.size) {
1 -> {
val dt = params[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, params[0].position, variableAsmName = varname(params[0]))
if(dt.isByteOrBool)
asmgen.assignRegister(RegisterOrPair.A, target) // single byte in A
else
asmgen.assignRegister(RegisterOrPair.AY, target) // word in AY
}
2 -> {
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[0].type, sub, params[0].position, variableAsmName = varname(params[0]))
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[1].type, sub, params[1].position, variableAsmName = varname(params[1]))
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
// 2 byte args, first in A, second in Y
asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2)
} else throw AssemblyError("cannot use registers for word+byte args")
}
else -> throw AssemblyError("cannot use registers for >2 args")
}
}
@@ -448,9 +463,9 @@ internal class ProgramAndVarsGen(
if(addr!=null)
asmgen.out("$name = $addr")
else when(dt) {
DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.UWORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
BaseDataType.UWORD -> asmgen.out("$name .word ?")
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
@@ -471,6 +486,7 @@ internal class ProgramAndVarsGen(
private fun entrypointInitialization() {
// 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
@@ -516,12 +532,12 @@ 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("+")
@@ -541,24 +557,24 @@ internal class ProgramAndVarsGen(
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
val result = mutableListOf<ZpStringWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
val vars = allocator.zeropageVars.filter { it.value.dt.isString }
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
}
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
val result = mutableListOf<ZpArrayWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
val vars = allocator.zeropageVars.filter { it.value.dt.isArray }
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
}
@@ -569,7 +585,7 @@ internal class ProgramAndVarsGen(
if (scopedName.startsWith("cx16.r"))
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
if(variable.dt in SplitWordArrayTypes) {
if(variable.dt.isSplitWordArray) {
val lsbAddr = zpvar.address
val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
@@ -586,7 +602,11 @@ internal class ProgramAndVarsGen(
if(varsNoInit.isNotEmpty()) {
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.base }).forEach {
uninitializedVariable2asm(it)
}
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it)
}
asmgen.out(" .send BSS")
@@ -594,33 +614,51 @@ internal class ProgramAndVarsGen(
if(varsWithInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables with init value")
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt == DataType.STR }
stringvars.forEach {
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
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)
}
}
}
private fun uninitializedVariable2asm(variable: StStaticVariable) {
when (variable.dt) {
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
in SplitWordArrayTypes -> {
val dt = variable.dt
when {
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ?")
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
dt.isSplitWordArray -> {
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 -> {
dt.isArray -> {
alignVar(variable.align)
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes")
}
@@ -630,21 +668,27 @@ 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.dt== DataType.FLOAT)
variable.onetimeInitializationNumericValue!!
if(variable.initializationNumericValue!=null) {
if(variable.dt.isFloat)
variable.initializationNumericValue!!
else
variable.onetimeInitializationNumericValue!!.toInt()
variable.initializationNumericValue!!.toInt()
} else 0
when (variable.dt) {
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")
DataType.FLOAT -> {
val dt=variable.dt
when {
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
dt.isFloat -> {
if(initialValue==0) {
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
} else {
@@ -652,19 +696,22 @@ internal class ProgramAndVarsGen(
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
}
}
DataType.STR -> {
dt.isString -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
dt.isArray -> {
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?) {
when(dt) {
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
alignVar(align)
when {
dt.isUnsignedByteArray || dt.isBoolArray -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.byte ${data.joinToString()}")
@@ -674,7 +721,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .byte " + chunk.joinToString())
}
}
DataType.ARRAY_B -> {
dt.isSignedByteArray -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.char ${data.joinToString()}")
@@ -684,7 +731,20 @@ internal class ProgramAndVarsGen(
asmgen.out(" .char " + chunk.joinToString())
}
}
DataType.ARRAY_UW -> {
dt.isSplitWordArray -> {
if(dt.elementType().isUnsignedWord) {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
} else {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
}
dt.isUnsignedWordArray -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.word ${data.joinToString()}")
@@ -694,7 +754,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .word " + chunk.joinToString())
}
}
DataType.ARRAY_W -> {
dt.isSignedWordArray -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.sint ${data.joinToString()}")
@@ -704,19 +764,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_UW_SPLIT -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_W_SPLIT -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_F -> {
dt.isFloatArray -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map {
compTarget.machine.getFloatAsmBytes(it.number!!)
@@ -742,7 +790,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" ${it.name} = ${it.address.toHex()}")
}
consts.sortedBy { it.name }.forEach {
if(it.dt==DataType.FLOAT)
if(it.dt==BaseDataType.FLOAT)
asmgen.out(" ${it.name} = ${it.value}")
else
asmgen.out(" ${it.name} = ${it.value.toHex()}")
@@ -754,11 +802,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') }
@@ -768,8 +821,8 @@ 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 ->
return when {
dt.isBoolArray ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
if(it.boolean!=null)
@@ -779,18 +832,23 @@ internal class ProgramAndVarsGen(
if(number==0.0) "0" else "1"
}
}
DataType.ARRAY_UB ->
dt.isUnsignedByteArray ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
}
else if(it.addressOfSymbol!=null) {
asmgen.asmSymbolName(it.addressOfSymbol!!)
val addrOfSymbol = it.addressOfSymbol!!
val symbol = symboltable.lookup(addrOfSymbol)!!
if(symbol is StStaticVariable && symbol.dt.isSplitWordArray)
asmgen.asmSymbolName(addrOfSymbol+"_lsb") // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(addrOfSymbol)
}
else
throw AssemblyError("weird array elt")
@@ -801,14 +859,14 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) {
return when {
// byte array can never contain pointer-to types, so treat values as all integers
DataType.ARRAY_UB ->
dt.isUnsignedByteArray ->
array.map {
val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_B ->
dt.isSignedByteArray ->
array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
@@ -817,11 +875,11 @@ internal class ProgramAndVarsGen(
else
"-$$hexnum"
}
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0')
}
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
dt.isArray && dt.elementType().isSignedWord -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0)
@@ -49,8 +49,8 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
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
@@ -89,9 +89,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
// 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 IntegerDatatypesWithBoolean) {
if(variable.dt.isIntegerOrBool) {
if(zeropage.free.isEmpty()) {
break
} else {
@@ -124,6 +124,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
}
}
collect(st)
return vars.sortedBy { it.dt }
return vars.sortedBy { it.dt.base }
}
}
@@ -17,28 +17,28 @@ internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal
) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) {
in ByteDatatypesWithBoolean -> {
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
when {
expr.type.isByteOrBool -> {
if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if (expr.left.type.isWord && expr.right.type.isWord) {
require(expr.operator in ComparisonOperators)
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
}
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
if (expr.left.type.isFloat && expr.right.type.isFloat) {
require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign)
}
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
}
in WordDatatypes -> {
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
expr.type.isWord -> {
require(expr.left.type.isWord && expr.right.type.isWord) {
"both operands must be words"
}
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) {
expr.type.isFloat -> {
require(expr.left.type.isFloat && expr.right.type.isFloat) {
"both operands must be floats"
}
return assignFloatBinExpr(expr, assign)
@@ -52,7 +52,7 @@ internal class AnyExprAsmGen(
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -60,7 +60,7 @@ internal class AnyExprAsmGen(
"-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -75,7 +75,7 @@ internal class AnyExprAsmGen(
"&" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -83,7 +83,7 @@ internal class AnyExprAsmGen(
"|" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -91,7 +91,7 @@ internal class AnyExprAsmGen(
"^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -197,7 +197,7 @@ internal class AnyExprAsmGen(
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.forDt(BaseDataType.FLOAT))
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
}
@@ -44,7 +44,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
}
init {
if(register!=null && datatype !in NumericDatatypesWithBoolean)
if(register!=null && !datatype.isNumericOrBool)
throw AssemblyError("must be numeric type")
}
@@ -77,12 +77,20 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
when(registers) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers)
RegisterOrPair.Y -> {
val dt = DataType.forDt(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
RegisterOrPair.XY -> {
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
RegisterOrPair.FAC2 -> {
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), scope, pos, register = registers)
}
RegisterOrPair.R0,
RegisterOrPair.R1,
RegisterOrPair.R2,
@@ -98,7 +106,10 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.R12,
RegisterOrPair.R13,
RegisterOrPair.R14,
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
RegisterOrPair.R15 -> {
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
}
}
@@ -151,7 +162,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
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 AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.forDt(BaseDataType.BOOL), boolean = bv)
return when(value) {
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
@@ -163,7 +174,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
if(value.type.isUnsignedWord && varName.lowercase().startsWith("cx16.r")) {
val regStr = varName.lowercase().substring(5)
val reg = RegisterOrPair.valueOf(regStr.uppercase())
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
@@ -172,7 +183,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
}
}
is PtMemoryByte -> {
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value)
}
is PtArrayIndexer -> {
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
@@ -202,9 +213,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
if(target.datatype!=datatype) {
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
if(target.datatype.isByte && datatype.isByte) {
return withAdjustedDt(target.datatype)
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
} else if(target.datatype.isWord && datatype.isWord) {
return withAdjustedDt(target.datatype)
}
}
@@ -220,8 +231,9 @@ internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
val position: Position) {
init {
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
require(!source.datatype.isUndefined) { "must not be placeholder/undefined datatype at $position" }
if(!source.datatype.isArray && !target.datatype.isArray)
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+18 -9
View File
@@ -4,16 +4,25 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5
else -> 2
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements != null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
BaseDataType.FLOAT -> numElements*5
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> 5 * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
+53 -19
View File
@@ -8,6 +8,7 @@ import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.code.target.C64Target
import prog8.codegen.cpu6502.AsmGen6502
import java.nio.file.Files
@@ -26,7 +27,8 @@ class TestCodegen: FunSpec({
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu
)
}
@@ -43,52 +45,84 @@ class TestCodegen: FunSpec({
// xx += cx16.r0
// }
//}
val codegen = AsmGen6502(prefixSymbols = false)
val codegen = AsmGen6502(prefixSymbols = false, 0)
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.forDt(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
PtNumber(BaseDataType.UBYTE, 0.0, Position.DUMMY),
null,
Position.DUMMY
))
sub.add(PtVariable(
"particleX",
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"particleDX",
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"xx",
DataType.forDt(BaseDataType.WORD),
ZeropageWish.DONTCARE,
0u,
PtNumber(BaseDataType.WORD, 1.0, Position.DUMMY),
null,
Position.DUMMY
))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
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))
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
}
it.add(targetIdx)
}
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target)
assign.add(value)
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
sub.add(cxregAssign)
block.add(sub)
@@ -96,7 +130,7 @@ class TestCodegen: FunSpec({
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
program.add(cx16block)
val options = getTestOptions()
-46
View File
@@ -1,46 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
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:2.0.0"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!
+27
View File
@@ -0,0 +1,27 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":intermediate"))
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:2.0.0")
}
sourceSets {
main {
java {
srcDir(file("${project.projectDir}/src"))
}
resources {
srcDir(file("${project.projectDir}/res"))
}
}
}
// note: there are no unit tests in this module!
-66
View File
@@ -1,66 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
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: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'
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}
+48
View File
@@ -0,0 +1,48 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
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:2.0.0")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
test {
java {
srcDir("${project.projectDir}/test")
}
}
}
tasks.test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn("cleanTest")
// Show test results.
testLogging {
events("skipped", "failed")
}
}
@@ -1,7 +1,7 @@
package prog8.codegen.intermediate
import prog8.code.StRomSub
import prog8.code.StRomSubParameter
import prog8.code.StExtSub
import prog8.code.StExtSubParameter
import prog8.code.ast.*
import prog8.code.core.*
import prog8.intermediate.*
@@ -14,7 +14,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val values = assignment.value as? PtFunctionCall
?: throw AssemblyError("only function calls can return multiple values in a multi-assign")
val sub = codeGen.symbolTable.lookup(values.name) as? StRomSub
val sub = codeGen.symbolTable.lookup(values.name) as? StExtSub
?: throw AssemblyError("only asmsubs can return multiple values")
val result = mutableListOf<IRCodeChunkBase>()
@@ -46,7 +46,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
}
private fun assignCpuRegister(returns: StRomSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
private fun assignCpuRegister(returns: StExtSubParameter, regNum: Int, target: PtAssignTarget): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val loadCpuRegInstr = when(returns.register.registerOrPair) {
RegisterOrPair.A -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum)
@@ -83,7 +83,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val symbol = target.identifier?.name
val array = target.array
val value = augAssign.value
val signed = target.type in SignedDatatypes
val signed = target.type.isSigned
val chunks = when (augAssign.operator) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
@@ -182,7 +182,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun inplacePrefixArray(operator: String, array: PtArrayIndexer): IRCodeChunks {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
val vmDt = irType(array.type)
val constIndex = array.index.asConstInteger()
@@ -278,7 +278,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
}
"not" -> {
// TODO: in boolean branch, is 'not' handled ok like this?
val register = codeGen.registers.nextFree()
if(constIndex!=null) {
result += IRCodeChunk(null, null).also {
@@ -337,7 +336,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
val opcode = if(assignment.value.type.isSigned) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null)
}
}
@@ -358,9 +357,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
else if(targetArray!=null) {
val variable = targetArray.variable.name
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type, null)
val fixedIndex = constIntValue(targetArray.index)
val fixedIndex = targetArray.index.asConstInteger()
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
if(zero) {
if(fixedIndex!=null) {
@@ -466,10 +465,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
expressionEval.translateExpression(offsetTypecast!!.value)
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
@@ -499,9 +498,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
}
val mult: PtExpression = PtBinaryExpression("*", DataType.UBYTE, array.position)
val mult: PtExpression = PtBinaryExpression("*", DataType.forDt(BaseDataType.UBYTE), array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
@@ -512,7 +511,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -552,7 +551,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -612,7 +611,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -652,7 +651,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -717,7 +716,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val factor = constFactorRight.number
result += codeGen.divideByConstFloatInplace(constAddress, symbol, factor)
} else {
@@ -738,7 +737,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
addInstr(result, ins, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val factor = constFactorRight.number.toInt()
result += codeGen.divideByConstInplace(vmDt, constAddress, symbol, factor, signed)
} else {
@@ -764,7 +763,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
return operatorMultiplyInplaceSplitArray(array, operand)
@@ -802,7 +801,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val factor = constFactorRight.number.toInt()
result += codeGen.multiplyByConstInplace(vmDt, constAddress, symbol, factor)
} else {
@@ -820,7 +819,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorMinusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
return operatorMinusInplaceSplitArray(array, operand)
@@ -884,7 +883,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun operatorMultiplyInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
return null // TODO("inplace split word array *")
return null // TODO("inplace split word array *")
}
private fun operatorMinusInplaceSplitArray(array: PtArrayIndexer, operand: PtExpression): IRCodeChunks? {
@@ -916,7 +915,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
return operatorPlusInplaceSplitArray(array, operand)
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val elementDt = irType(array.type)
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
@@ -1056,7 +1055,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -1370,10 +1369,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun createInplaceArrayComparison(array: PtArrayIndexer, value: PtExpression, comparisonOperator: String): IRCodeChunks? {
if(array.type==DataType.FLOAT)
return null // TODO("optimized in-place compare on float arrays")) // TODO?
if(array.type.isFloat)
return null // TODO("optimized in-place compare on float arrays"))
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
TODO("inplace compare for split word array")
@@ -1390,10 +1389,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
it += when(comparisonOperator) {
"==" -> IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"!=" -> IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"<" -> return null // TODO("array <0 inplace")) // TODO?
"<=" -> return null // TODO("array <=0 inplace")) // TODO?
">" -> return null // TODO("array >0 inplace")) // TODO?
">=" -> return null // TODO("array >=0 inplace")) // TODO?
"<" -> return null // TODO("array <0 inplace"))
"<=" -> return null // TODO("array <=0 inplace"))
">" -> return null // TODO("array >0 inplace"))
">=" -> return null // TODO("array >=0 inplace"))
else -> throw AssemblyError("invalid operator")
}
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
@@ -1413,10 +1412,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
it += when(comparisonOperator) {
"==" -> IRInstruction(Opcode.SZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"!=" -> IRInstruction(Opcode.SNZ, vmDt, reg1=cmpResultReg, reg2=valueReg)
"<" -> return null // TODO("array <0 inplace")) // TODO?
"<=" -> return null // TODO("array <=0 inplace")) // TODO?
">" -> return null // TODO("array >0 inplace")) // TODO?
">=" -> return null // TODO("array >=0 inplace")) // TODO?
"<" -> return null // TODO("array <0 inplace"))
"<=" -> return null // TODO("array <=0 inplace"))
">" -> return null // TODO("array >0 inplace"))
">=" -> return null // TODO("array >=0 inplace"))
else -> throw AssemblyError("invalid operator")
}
it += IRInstruction(Opcode.STOREX, vmDt, reg1=valueReg, reg2=indexTr.resultReg, labelSymbol = array.variable.name)
@@ -1,7 +1,8 @@
package prog8.codegen.intermediate
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
import prog8.intermediate.*
@@ -19,6 +20,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"callfar" -> funcCallfar(call)
"callfar2" -> funcCallfar2(call)
"call" -> funcCall(call)
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
"msb" -> funcMsb(call)
"lsb" -> funcLsb(call)
"memory" -> funcMemory(call)
@@ -39,77 +42,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"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 statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
"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)
"prog8_lib_arraycopy" -> funcArrayCopy(call)
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
}
}
private fun funcArrayCopy(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val source = call.args[0] as PtIdentifier
val target = call.args[1] as PtIdentifier
val sourceLength = codeGen.symbolTable.getLength(source.name)!!
val targetLength = codeGen.symbolTable.getLength(target.name)!!
require(sourceLength==targetLength)
val result = mutableListOf<IRCodeChunkBase>()
val fromReg = codeGen.registers.nextFree()
val toReg = codeGen.registers.nextFree()
val countReg = codeGen.registers.nextFree()
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
// split words -> split words, copy lsb and msb arrays separately
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
}
else if(source.type in SplitWordArrayTypes) {
// split -> normal words
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
val fromRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromRegMsb, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_SPLITW_TO_NORMAL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to fromRegMsb, IRDataType.WORD to toReg, IRDataType.BYTE to countReg), returns = null)
}
else if(target.type in SplitWordArrayTypes) {
// normal -> split words
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
val toRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toRegMsb, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_NORMAL_TO_SPLITW, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to toRegMsb, IRDataType.BYTE to countReg), returns = null)
}
else {
// normal array to array copy (various element types)
val eltsize = codeGen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength * eltsize)
}
result += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val valueTr = exprGen.translateExpression(call.args[0])
@@ -229,13 +170,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>()
if(sourceDt==DataType.UWORD)
if(sourceDt.isUnsignedWord)
return ExpressionCodeResult.EMPTY
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
when (sourceDt) {
DataType.BYTE -> {
when (sourceDt.base) {
BaseDataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -246,7 +187,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
DataType.WORD -> {
BaseDataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -257,7 +198,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
val resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
@@ -289,8 +230,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
val dt = call.args[0].type
when(dt) {
DataType.UBYTE -> {
when(dt.base) {
BaseDataType.UBYTE -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -298,7 +239,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -306,7 +247,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg)
val resultFpReg = codeGen.registers.nextFreeFloat()
result += IRCodeChunk(null, null).also {
@@ -356,11 +297,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
)
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
} else {
val syscall = when(call.type) {
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE
DataType.BYTE -> IMSyscall.CLAMP_BYTE
DataType.UWORD -> IMSyscall.CLAMP_UWORD
DataType.WORD -> IMSyscall.CLAMP_WORD
val syscall = when(call.type.base) {
BaseDataType.UBYTE -> IMSyscall.CLAMP_UBYTE
BaseDataType.BYTE -> IMSyscall.CLAMP_BYTE
BaseDataType.UWORD -> IMSyscall.CLAMP_UWORD
BaseDataType.WORD -> IMSyscall.CLAMP_WORD
else -> throw AssemblyError("invalid dt")
}
result += codeGen.makeSyscall(syscall, listOf(
@@ -380,7 +321,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val comparisonOpcode = if(call.type.isSigned) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
@@ -399,7 +340,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val comparisonOpcode = if(call.type.isSigned) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
@@ -606,8 +547,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
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)
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
@@ -692,7 +633,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
else {
val eltSize = codeGen.program.memsizer.memorySize(target.type)
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
val constIndex = target.index.asConstInteger()
if(isConstZeroValue) {
if(constIndex!=null) {
@@ -1,7 +1,7 @@
package prog8.codegen.intermediate
import prog8.code.StNode
import prog8.code.StRomSub
import prog8.code.StExtSub
import prog8.code.StSub
import prog8.code.ast.*
import prog8.code.core.*
@@ -53,7 +53,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
is PtIdentifier -> {
val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) {
if (expr.type.isPassByValue) {
val vmDt = irType(expr.type)
if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat()
@@ -67,9 +67,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
} else {
// for strings and arrays etc., load the *address* of the value instead
val vmDt = if(expr.type==DataType.UNDEFINED) IRDataType.WORD else irType(expr.type)
// for arrays this could mean a split word array, in which case we take the address of the _lsb array which comes first
val vmDt = if(expr.type.isUndefined) IRDataType.WORD else irType(expr.type)
val resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = expr.name)
val labelsymbol = if(expr.type.isSplitWordArray) expr.name+"_lsb" else expr.name
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = labelsymbol)
ExpressionCodeResult(code, vmDt, resultRegister, -1)
}
}
@@ -79,13 +81,48 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtPrefix -> translate(expr)
is PtArrayIndexer -> translate(expr)
is PtBinaryExpression -> translate(expr)
is PtIfExpression -> translate(expr)
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
is PtFunctionCall -> translate(expr)
is PtContainmentCheck -> translate(expr)
is PtRange,
is PtArray,
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
else -> throw AssemblyError("weird expression")
}
}
private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
if((ifExpr.condition as? PtPrefix)?.operator=="not")
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
val condTr = translateExpression(ifExpr.condition)
val trueTr = translateExpression(ifExpr.truevalue)
val falseTr = translateExpression(ifExpr.falsevalue)
val irDt = irType(ifExpr.type)
val result = mutableListOf<IRCodeChunkBase>()
val falseLabel = codeGen.createLabelName()
val endLabel = codeGen.createLabelName()
addToResult(result, condTr, condTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=condTr.resultReg, immediate = 0), null)
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = falseLabel), null)
if (irDt != IRDataType.FLOAT) {
addToResult(result, trueTr, trueTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
result += IRCodeChunk(falseLabel, null)
addToResult(result, falseTr, trueTr.resultReg, -1)
result += IRCodeChunk(endLabel, null)
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
} else {
addToResult(result, trueTr, -1, trueTr.resultFpReg)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
result += IRCodeChunk(falseLabel, null)
addToResult(result, falseTr, -1, trueTr.resultFpReg)
result += IRCodeChunk(endLabel, null)
return ExpressionCodeResult(result, irDt, -1, trueTr.resultFpReg)
}
}
@@ -96,13 +133,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val resultRegister = codeGen.registers.nextFree()
if(expr.isFromArrayElement) {
require(expr.identifier.type !in SplitWordArrayTypes)
require(!expr.identifier.type.isSplitWordArray)
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
val indexTr2 = translateExpression(expr.arrayIndexExpr!!)
addToResult(result, indexTr2, indexTr2.resultReg, -1)
val indexWordReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr2.resultReg), null)
if(expr.identifier.type == DataType.UWORD) {
if(expr.identifier.type.isUnsignedWord) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
@@ -119,7 +156,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
} else {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
if(expr.isMsbForSplitArray) {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol+"_msb"), null)
} else if(expr.identifier.type.isSplitWordArray) {
// the _lsb split array comes first in memory
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol+"_lsb"), null)
} else
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
}
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
}
@@ -149,10 +192,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
translateExpression(offsetTypecast!!.value)
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
@@ -176,10 +219,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(it is PtBool) it.asInt()
else (it as PtNumber).number.toInt()
}
when(elementDt) {
in IntegerDatatypesWithBoolean -> {
if (elementDt in ByteDatatypesWithBoolean) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
if (elementDt in WordDatatypes) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
when {
elementDt.isIntegerOrBool -> {
if (elementDt.isByteOrBool) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
if (elementDt.isWord) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
val gottemLabel = codeGen.createLabelName()
val endLabel = codeGen.createLabelName()
val elementTr = translateExpression(check.needle)
@@ -194,17 +237,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, elementTr.resultReg, immediate = 1), gottemLabel)
result += IRCodeChunk(endLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.FLOAT -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
elementDt.isFloat -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
else -> throw AssemblyError("weird dt $elementDt")
}
}
val haystackVar = check.haystackHeapVar!!
when(haystackVar.type) {
DataType.STR -> {
when {
haystackVar.type.isString -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
@@ -214,7 +256,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
haystackVar.type.isByteArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
@@ -227,7 +269,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
haystackVar.type.isWordArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
@@ -240,7 +282,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_F -> {
haystackVar.type.isFloatArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, -1, elementTr.resultFpReg)
@@ -259,7 +301,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
val vmDt = irType(arrayIx.type)
val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name
@@ -350,18 +392,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, tr.resultReg, tr.resultFpReg)
var actualResultReg2 = -1
var actualResultFpReg2 = -1
when(cast.type) {
DataType.BOOL -> {
when (cast.value.type) {
in ByteDatatypes -> {
val valueDt = cast.value.type
when(cast.type.base) {
BaseDataType.BOOL -> {
when {
valueDt.isByte -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
in WordDatatypes -> {
valueDt.isWord -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
DataType.FLOAT -> {
valueDt.isFloat -> {
actualResultReg2 = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg)
@@ -371,87 +414,87 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else -> throw AssemblyError("weird cast value type")
}
}
DataType.UBYTE -> {
when(cast.value.type) {
DataType.BOOL, DataType.BYTE, DataType.UWORD, DataType.WORD -> {
BaseDataType.UBYTE -> {
when(valueDt.base) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg // just keep the LSB as it is
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.BYTE -> {
when(cast.value.type) {
DataType.BOOL, DataType.UBYTE, DataType.UWORD, DataType.WORD -> {
BaseDataType.BYTE -> {
when(valueDt.base) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg // just keep the LSB as it is
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.UWORD -> {
when(cast.value.type) {
DataType.BYTE -> {
BaseDataType.UWORD -> {
when(valueDt.base) {
BaseDataType.BYTE -> {
// byte -> uword: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
}
DataType.BOOL, DataType.UBYTE -> {
BaseDataType.BOOL, BaseDataType.UBYTE -> {
// ubyte -> uword: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
}
DataType.WORD -> {
BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.WORD -> {
when(cast.value.type) {
DataType.BYTE -> {
BaseDataType.WORD -> {
when(valueDt.base) {
BaseDataType.BYTE -> {
// byte -> word: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
DataType.BOOL, DataType.UBYTE -> {
BaseDataType.BOOL, BaseDataType.UBYTE -> {
// byte -> word: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
actualResultReg2 = tr.resultReg
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultFpReg2 = codeGen.registers.nextFreeFloat()
when(cast.value.type) {
DataType.BOOL, DataType.UBYTE -> {
when(valueDt.base) {
BaseDataType.BOOL, BaseDataType.UBYTE -> {
addInstr(result, IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
DataType.BYTE -> {
BaseDataType.BYTE -> {
addInstr(result, IRInstruction(Opcode.FFROMSB, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
addInstr(result, IRInstruction(Opcode.FFROMUW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
DataType.WORD -> {
BaseDataType.WORD -> {
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
else -> throw AssemblyError("weird cast value type")
@@ -465,7 +508,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
val signed = binExpr.left.type.isSigned
return when(binExpr.operator) {
"+" -> operatorPlus(binExpr, vmDt)
"-" -> operatorMinus(binExpr, vmDt)
@@ -492,7 +535,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw")) {
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
return translateStackFunctions(fcall, callTarget)
}
@@ -527,12 +570,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = irType(parameter.type)
val tr = translateExpression(arg)
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
result += tr.chunks
if(parameter.register==null) {
val tr = translateExpression(arg)
result += tr.chunks
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
} else {
require(parameter.register in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
val regname = parameter.register!!.asScopedNameVirtualReg(parameter.type).joinToString(".")
val assign = PtAssignment(fcall.position)
val target = PtAssignTarget(true, fcall.position)
target.add(PtIdentifier(regname, parameter.type, fcall.position))
assign.add(target)
assign.add(arg)
result += codeGen.translateNode(assign)
}
}
// return value (always singular for normal Subs)
val returnRegSpec = if(fcall.void) null else {
@@ -547,12 +601,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fcallArgs = FunctionCallArgs(argRegisters, if(returnRegSpec==null) emptyList() else listOf(returnRegSpec))), null)
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
else if(fcall.type.isFloat)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
}
is StRomSub -> {
is StExtSub -> {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
// assign the arguments
@@ -572,6 +626,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=tr.resultReg), null)
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=tr.resultReg), null)
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=tr.resultReg), null)
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> TODO("floating point register parameters not supported")
in Cx16VirtualRegisters -> {
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1=tr.resultReg, labelSymbol = "cx16.${parameter.register.registerOrPair.toString().lowercase()}"), null)
}
@@ -585,7 +640,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
if(callTarget.returns.size>1)
return callRomSubWithMultipleReturnValues(callTarget, fcall, argRegisters, result)
return callExtSubWithMultipleReturnValues(callTarget, fcall, argRegisters, result)
// return a single value (or nothing)
val returnRegSpec = if(fcall.void) null else {
@@ -607,8 +662,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val call =
if(callTarget.address==null)
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
else
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
else {
val address = callTarget.address!!
if(address.constbank==null && address.varbank==null) {
IRInstruction(
Opcode.CALL,
address = address.address.toInt(),
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
}
else {
TODO("callfar into another bank is not implemented for the selected compilation target")
}
}
addInstr(result, call, null)
var finalReturnRegister = returnRegSpec?.registerNum ?: -1
@@ -649,7 +714,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
else if(fcall.type.isFloat)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, finalReturnRegister)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
@@ -687,27 +752,49 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.WORD, reg1=popReg), null)
return ExpressionCodeResult(chunk, IRDataType.WORD, popReg, -1)
}
"floats.push" -> {
// push float
val tr = translateExpression(fcall.args.single())
chunk += tr.chunks
addInstr(chunk, IRInstruction(Opcode.PUSH, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
return ExpressionCodeResult(chunk, IRDataType.FLOAT, -1, -1)
}
"floats.pop" -> {
// pop float
val popReg = codeGen.registers.nextFreeFloat()
addInstr(chunk, IRInstruction(Opcode.POP, IRDataType.FLOAT, fpReg1 = popReg), null)
return ExpressionCodeResult(chunk, IRDataType.FLOAT, -1, resultFpReg = popReg)
}
else -> throw AssemblyError("unknown stack subroutine called")
}
}
private fun callRomSubWithMultipleReturnValues(
callTarget: StRomSub,
private fun callExtSubWithMultipleReturnValues(
callTarget: StExtSub,
fcall: PtFunctionCall,
argRegisters: MutableList<FunctionCallArgs.ArgumentSpec>,
result: MutableList<IRCodeChunkBase>
): ExpressionCodeResult {
// return multiple values
val returnRegisters = callTarget.returns.map {
val regnum = if(it.type==DataType.FLOAT) codeGen.registers.nextFreeFloat() else codeGen.registers.nextFree()
val regnum = if(it.type.isFloat) codeGen.registers.nextFreeFloat() else codeGen.registers.nextFree()
FunctionCallArgs.RegSpec(irType(it.type), regnum, it.register)
}
// create the call
val call =
if(callTarget.address==null)
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegisters))
else
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegisters))
else {
val address = callTarget.address!!
if(address.constbank==null && address.varbank==null) {
IRInstruction(
Opcode.CALL,
address = address.address.toInt(),
fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)
)
}
else TODO("extsub with banked address got called ${callTarget.name}")
}
addInstr(result, call, null)
val resultRegs = returnRegisters.filter{it.dt!=IRDataType.FLOAT}.map{it.registerNum}
val resultFpRegs = returnRegisters.filter{it.dt==IRDataType.FLOAT}.map{it.registerNum}
@@ -739,7 +826,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultReg, reg2 = resultRegister, reg3 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1)
} else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) {
if(binExpr.left.type.isString || binExpr.right.type.isString) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
@@ -782,7 +869,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultRegister, reg2 = resultRegister, reg3 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1)
} else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) {
if(binExpr.left.type.isString || binExpr.right.type.isString) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
@@ -823,10 +910,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += IRCodeChunk(label, null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) {
if(binExpr.left.type.isString || binExpr.right.type.isString) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
return if(constValue(binExpr.right)==0.0) {
return if(binExpr.right.asConstValue()==0.0) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
val opcode = if (notEquals) Opcode.SNZ else Opcode.SZ
@@ -995,7 +1082,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = binExpr.right as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg)
val factor = constFactorRight.number
@@ -1014,7 +1101,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
}
} else {
return if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
return if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorRight.number.toInt()
@@ -1072,13 +1159,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
}
} else {
return if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
return if(constFactorLeft!=null && !constFactorLeft.type.isFloat) {
val tr = translateExpression(binExpr.right)
addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorLeft.number.toInt()
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorRight.number.toInt()
@@ -23,6 +23,7 @@ class IRCodeGen(
makeAllNodenamesScoped(program)
moveAllNestedSubroutinesToBlockScope(program)
verifyNameScoping(program, symbolTable)
changeGlobalVarInits(symbolTable)
val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable)
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
@@ -54,12 +55,43 @@ class IRCodeGen(
return irProg
}
private fun changeGlobalVarInits(symbolTable: SymbolTable) {
// Normally, block level (global) variables that have a numeric initialization value
// are initialized via an assignment statement.
val initsToRemove = mutableListOf<Pair<PtBlock, PtAssignment>>()
symbolTable.allVariables.forEach { variable ->
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
val block = variable.parent.astNode as PtBlock
val initialization = (block.children.firstOrNull {
it is PtAssignment && it.target.identifier?.name==variable.scopedName
} as PtAssignment?)
val initValue = initialization?.value
when(initValue){
is PtBool -> {
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
initsToRemove += block to initialization
}
is PtNumber -> {
variable.setOnetimeInitNumeric(initValue.number)
initsToRemove += block to initialization
}
is PtArray, is PtString -> throw AssemblyError("array or string initialization values should already be part of the vardecl, not a separate assignment")
else -> {}
}
}
}
for((block, assign) in initsToRemove) {
block.children.remove(assign)
}
}
private fun verifyNameScoping(program: PtProgram, symbolTable: SymbolTable) {
fun verifyPtNode(node: PtNode) {
when (node) {
is PtBuiltinFunctionCall -> require('.' !in node.name) { "builtin function call name should not be scoped: ${node.name}" }
is PtFunctionCall -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtIdentifier -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtAsmSub -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtBlock -> require('.' !in node.name) { "block name should not be scoped: ${node.name}" }
is PtConstant -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
@@ -69,8 +101,7 @@ class IRCodeGen(
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
else -> { /* node has no name */
}
else -> { /* node has no name or is ok to have no dots in the name */ }
}
node.children.forEach { verifyPtNode(it) }
}
@@ -116,12 +147,11 @@ class IRCodeGen(
}
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.label, first.assembly, first.isIR, first.next)
is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.label, first.data, first.next)
else -> throw AssemblyError("invalid chunk")
}
sub.chunks.removeAt(0)
sub.chunks.add(0, replacement)
} else if(first.label != sub.label) {
val next = if(first is IRCodeChunk) first else null
val next = first as? IRCodeChunk
sub.chunks.add(0, IRCodeChunk(sub.label, next))
}
}
@@ -201,6 +231,7 @@ class IRCodeGen(
chunk += IRInstruction(Opcode.BREAKPOINT)
listOf(chunk)
}
is PtAlign -> TODO("ir support for inline %align")
is PtConditionalBranch -> translate(node)
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, null))
is PtIncludeBinary -> listOf(IRInlineBinaryChunk(null, readBinaryData(node), null))
@@ -221,6 +252,7 @@ class IRCodeGen(
is PtBool,
is PtArray,
is PtBlock,
is PtDefer -> throw AssemblyError("should have been transformed")
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
else -> TODO("missing codegen for $node")
@@ -245,7 +277,8 @@ class IRCodeGen(
val goto = branch.trueScope.children.firstOrNull() as? PtJump
if (goto is PtJump) {
// special case the form: if_cc goto <place> (with optional else)
val address = goto.address?.toInt()
val address = goto.target.asConstInteger()
val label = (goto.target as? PtIdentifier)?.name
if(address!=null) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
@@ -258,8 +291,7 @@ class IRCodeGen(
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
}
addInstr(result, branchIns, null)
} else {
val label = goto.identifier!!.name
} else if(label!=null && !isIndirectJump(goto)) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
@@ -271,6 +303,8 @@ class IRCodeGen(
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
}
addInstr(result, branchIns, null)
} else {
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
if(branch.falseScope.children.isNotEmpty())
result += translateNode(branch.falseScope)
@@ -330,9 +364,6 @@ class IRCodeGen(
is IRInlineBinaryChunk -> {
IRInlineBinaryChunk(label, first.data, first.next)
}
else -> {
throw AssemblyError("invalid chunk")
}
}
return listOf(labeledFirstChunk) + chunks.drop(1)
}
@@ -403,8 +434,8 @@ class IRCodeGen(
val tmpReg = registers.nextFree()
val loopLabel = createLabelName()
val endLabel = createLabelName()
when (iterable.type) {
DataType.STR -> {
when {
iterable.type.isString -> {
// iterate over a zero-terminated string
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
@@ -419,10 +450,10 @@ class IRCodeGen(
result += jumpChunk
result += IRCodeChunk(endLabel, null)
}
in SplitWordArrayTypes -> {
iterable.type.isSplitWordArray -> {
// iterate over lsb/msb split word array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
if(elementDt !in WordDatatypes)
val elementDt = iterable.type.elementType()
if(!elementDt.isWord)
throw AssemblyError("weird dt")
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
@@ -446,13 +477,14 @@ class IRCodeGen(
}
else -> {
// iterate over regular array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
val element = iterable.type.sub!!
val elementDt = element.dt
val elementSize = program.memsizer.memorySize(element)
val lengthBytes = iterableLength!! * elementSize
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
it += IRInstruction(Opcode.LOADX, irType(DataType.forDt(elementDt)), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
it += IRInstruction(Opcode.STOREM, irType(DataType.forDt(elementDt)), reg1=tmpReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
@@ -519,7 +551,7 @@ class IRCodeGen(
addToResult(result, fromTr, fromTr.resultReg, -1)
val labelAfterFor = createLabelName()
val precheckInstruction = if(loopvarDt in SignedDatatypes) {
val precheckInstruction = if(loopvarDt.isSigned) {
if(step>0)
IRInstruction(Opcode.BGTSR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
else
@@ -587,7 +619,7 @@ class IRCodeGen(
val chunk2 = addConstMem(loopvarDtIr, null, loopvarSymbol, iterable.step)
if(loopvarDtIr==IRDataType.BYTE && iterable.step==-1 && iterable.last==0) {
// downto 0 optimization (byte)
if(loopvarDt==DataType.BYTE || iterable.first<=127) {
if(loopvarDt.isSignedByte || iterable.first<=127) {
chunk2 += IRInstruction(Opcode.BSTPOS, labelSymbol = loopLabel)
} else {
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
@@ -948,7 +980,7 @@ class IRCodeGen(
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump): MutableList<IRCodeChunkBase> {
val condition = ifElse.condition as? PtBinaryExpression
if(condition==null || condition.left.type!=DataType.FLOAT) {
if(condition==null || !condition.left.type.isFloat) {
return if(isIndirectJump(goto))
ifWithOnlyIndirectJump_IntegerCond(ifElse, goto)
else
@@ -995,7 +1027,12 @@ class IRCodeGen(
it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel)
}
}
it += IRInstruction(Opcode.JUMPI, labelSymbol = goto.identifier!!.name)
// evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(goto.target)
for(i in tr.chunks.flatMap { it.instructions }) {
it += i
}
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
} else {
// normal jump, directly to target with branch opcode
when(condition.operator) {
@@ -1015,10 +1052,12 @@ class IRCodeGen(
">=" -> Opcode.BGES
else -> throw AssemblyError("weird operator")
}
it += if (goto.address != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
it += if (goto.target.asConstInteger() != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.target.asConstInteger())
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
}
@@ -1029,11 +1068,15 @@ class IRCodeGen(
}
private fun branchInstr(goto: PtJump, branchOpcode: Opcode): IRInstruction {
return if (goto.address != null)
IRInstruction(branchOpcode, address = goto.address?.toInt())
return if (goto.target.asConstInteger() != null)
IRInstruction(branchOpcode, address = goto.target.asConstInteger())
else {
require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"}
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
val identifier = goto.target as? PtIdentifier
if(identifier!=null && !isIndirectJump(goto))
IRInstruction(branchOpcode, labelSymbol = identifier.name)
else
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
@@ -1041,7 +1084,6 @@ class IRCodeGen(
// indirect jump to target so the if has to jump past it instead
val result = mutableListOf<IRCodeChunkBase>()
val afterIfLabel = createLabelName()
val gotoSymbol = goto.identifier!!.name
fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) {
if(condition.operator in LogicalOperators) {
@@ -1053,7 +1095,7 @@ class IRCodeGen(
val leftTr = expressionEval.translateExpression(condition.left)
val irDt = leftTr.dt
val signed = condition.left.type in SignedDatatypes
val signed = condition.left.type.isSigned
addToResult(result, leftTr, leftTr.resultReg, -1)
val number = (condition.right as? PtNumber)?.number?.toInt()
if(number!=null) {
@@ -1141,7 +1183,7 @@ class IRCodeGen(
when(val cond = ifElse.condition) {
is PtTypeCast -> {
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
require(cond.type.isBool && cond.value.type.isNumeric)
val tr = expressionEval.translateExpression(cond)
result += tr.chunks
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel), null)
@@ -1163,7 +1205,10 @@ class IRCodeGen(
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
}
addInstr(result, IRInstruction(Opcode.JUMPI, labelSymbol = gotoSymbol), null)
// indirect jump to some computed address
val tr = expressionEval.translateExpression(goto.target)
result += tr.chunks
addInstr(result, IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg), null)
result += IRCodeChunk(afterIfLabel, null)
return result
}
@@ -1183,7 +1228,7 @@ class IRCodeGen(
val leftTr = expressionEval.translateExpression(condition.left)
val irDt = leftTr.dt
val signed = condition.left.type in SignedDatatypes
val signed = condition.left.type.isSigned
addToResult(result, leftTr, leftTr.resultReg, -1)
val number = (condition.right as? PtNumber)?.number?.toInt()
if(number!=null) {
@@ -1205,10 +1250,12 @@ class IRCodeGen(
">=" -> if(signed) Opcode.BGES else Opcode.BGE
else -> throw AssemblyError("invalid comparison operator")
}
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.target.asConstInteger()), null)
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
else
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
} else {
@@ -1262,17 +1309,19 @@ class IRCodeGen(
it += branchInstr(goto, opcode)
}
} else {
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.target.asConstInteger()), null)
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
else
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
}
when(val cond = ifElse.condition) {
is PtTypeCast -> {
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
require(cond.type.isBool && cond.value.type.isNumeric)
val tr = expressionEval.translateExpression(cond)
result += tr.chunks
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
@@ -1295,8 +1344,11 @@ class IRCodeGen(
}
private fun translateIfElse(ifElse: PtIfElse): IRCodeChunks {
if((ifElse.condition as? PtPrefix)?.operator=="not" && ifElse.hasElse())
throw AssemblyError("not prefix in ifelse should have been replaced by swapped if-else blocks")
val condition = ifElse.condition as? PtBinaryExpression
if(condition==null || condition.left.type != DataType.FLOAT) {
if(condition==null || !condition.left.type.isFloat) {
return ifWithElse_IntegerCond(ifElse)
}
@@ -1360,12 +1412,14 @@ class IRCodeGen(
private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>()
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) {
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_"))
throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND")
val tr = expressionEval.translateExpression(condition)
if(addCmpiZero)
tr.chunks.last().instructions.add(IRInstruction(Opcode.CMPI, tr.dt, reg1 = tr.resultReg, immediate = 0))
result += tr.chunks
if(ifElse.hasElse()) {
val elseLabel = createLabelName()
@@ -1385,9 +1439,9 @@ class IRCodeGen(
fun translateBinExpr(condition: PtBinaryExpression) {
if(condition.operator in LogicalOperators)
return translateSimple(condition, Opcode.BSTEQ)
return translateSimple(condition, Opcode.BSTEQ, false)
val signed = condition.left.type in SignedDatatypes
val signed = condition.left.type.isSigned
val elseBranchFirstReg: Int
val elseBranchSecondReg: Int
val number = (condition.right as? PtNumber)?.number?.toInt()
@@ -1522,20 +1576,24 @@ class IRCodeGen(
}
when(val cond=ifElse.condition) {
// TODO investigate; maybe do all conditions require a CMPI at the end? Some here still have false, but do they work correctly in all cases?
is PtBool -> {
// normally this will be optimized away, but not with -noopt
translateSimple(cond, Opcode.BSTEQ)
translateSimple(cond, Opcode.BSTEQ, false)
}
is PtTypeCast -> {
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
translateSimple(cond, Opcode.BSTEQ)
require(cond.type.isBool && cond.value.type.isNumeric)
translateSimple(cond, Opcode.BSTEQ, false)
}
is PtIdentifier, is PtArrayIndexer, is PtBuiltinFunctionCall, is PtFunctionCall, is PtContainmentCheck -> {
translateSimple(cond, Opcode.BSTEQ)
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
translateSimple(cond, Opcode.BSTEQ, false)
}
is PtBuiltinFunctionCall, is PtFunctionCall -> {
translateSimple(cond, Opcode.BSTEQ, true)
}
is PtPrefix -> {
require(cond.operator=="not")
translateSimple(cond.value, Opcode.BSTNE)
translateSimple(cond.value, Opcode.BSTNE, true)
}
is PtBinaryExpression -> {
translateBinExpr(cond)
@@ -1546,29 +1604,41 @@ class IRCodeGen(
}
private fun translate(repeat: PtRepeatLoop): IRCodeChunks {
when (constIntValue(repeat.count)) {
when (repeat.count.asConstInteger()) {
0 -> return emptyList()
1 -> return translateGroup(repeat.children)
256 -> {
// 256 iterations can still be done with just a byte counter if you set it to zero as starting value.
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
repeat.children[0] = PtNumber(BaseDataType.UBYTE, 0.0, repeat.count.position)
}
}
val repeatLabel = createLabelName()
val skipRepeatLabel = createLabelName()
val irDt = irType(repeat.count.type)
val constRepeats = repeat.count.asConstInteger()
val result = mutableListOf<IRCodeChunkBase>()
val countTr = expressionEval.translateExpression(repeat.count)
addToResult(result, countTr, countTr.resultReg, -1)
if(constIntValue(repeat.count)==null) {
// check if the counter is already zero
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipRepeatLabel), null)
}
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg) // sets status bits
it += IRInstruction(Opcode.BSTNE, labelSymbol = repeatLabel)
if(constRepeats==65536) {
// make use of the word wrap around to count to 65536
val resultRegister = registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, immediate = 0), null)
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.DEC, IRDataType.WORD, reg1 = resultRegister) // sets status bits
it += IRInstruction(Opcode.BSTNE, labelSymbol = repeatLabel)
}
} else {
val irDt = irType(repeat.count.type)
val countTr = expressionEval.translateExpression(repeat.count)
addToResult(result, countTr, countTr.resultReg, -1)
if (repeat.count.asConstValue() == null) {
// check if the counter is already zero
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipRepeatLabel), null)
}
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg) // sets status bits
it += IRInstruction(Opcode.BSTNE, labelSymbol = repeatLabel)
}
}
result += IRCodeChunk(skipRepeatLabel, null)
return result
@@ -1577,26 +1647,35 @@ class IRCodeGen(
private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, null)
chunk += if(jump.address!=null) {
IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
if(jump.target.asConstInteger()!=null) {
chunk += IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger())
result += chunk
return result
} else {
if (jump.identifier != null) {
if(isIndirectJump(jump)) {
IRInstruction(Opcode.JUMPI, labelSymbol = jump.identifier!!.name)
} else {
IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
}
} else
throw AssemblyError("weird jump")
val identifier = jump.target as? PtIdentifier
if (identifier != null && !isIndirectJump(jump)) {
// jump to label
chunk += IRInstruction(Opcode.JUMP, labelSymbol = identifier.name)
result += chunk
return result
}
// evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(jump.target)
result += tr.chunks
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.JUMPI, reg1=tr.resultReg)
}
return result
}
result += chunk
return result
}
private fun isIndirectJump(jump: PtJump): Boolean {
if(jump.identifier==null)
if(jump.target.asConstInteger()!=null)
return false
val symbol = symbolTable.lookup(jump.identifier!!.name)
val identifier = jump.target as? PtIdentifier
if(identifier==null)
return true
val symbol = symbolTable.lookup(identifier.name)
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
}
@@ -1612,15 +1691,23 @@ class IRCodeGen(
if(value==null) {
addInstr(result, IRInstruction(Opcode.RETURN), null)
} else {
if(value.type==DataType.FLOAT) {
val tr = expressionEval.translateExpression(value)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
if(value.type.isFloat) {
if(value is PtNumber) {
addInstr(result, IRInstruction(Opcode.RETURNI, IRDataType.FLOAT, immediateFp = value.number), null)
} else {
val tr = expressionEval.translateExpression(value)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
}
}
else {
val tr = expressionEval.translateExpression(value)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type) , reg1=tr.resultReg), null)
if(value.asConstInteger()!=null) {
addInstr(result, IRInstruction(Opcode.RETURNI, irType(value.type), immediate = value.asConstInteger()), null)
} else {
val tr = expressionEval.translateExpression(value)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type), reg1 = tr.resultReg), null)
}
}
}
return result
@@ -1633,14 +1720,14 @@ class IRCodeGen(
block.options.forceOutput,
block.options.noSymbolPrefixing,
block.options.veraFxMuls,
block.options.ignoreUnused,
translate(block.options.alignment)
block.options.ignoreUnused
), block.position)
for (child in block.children) {
when(child) {
is PtNop -> { /* nothing */ }
is PtAssignment, is PtAugmentedAssign -> { /* global variable initialization is done elsewhere */ }
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
is PtAlign -> TODO("ir support for inline %align")
is PtSub -> {
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
for (subchild in child.children) {
@@ -1650,9 +1737,9 @@ class IRCodeGen(
}
is PtAsmSub -> {
if(child.address!=null) {
// romsub. No codegen needed: calls to this are jumping straight to the address.
// extmsub. No codegen needed: calls to this are jumping straight to the address.
require(child.children.isEmpty()) {
"romsub should be empty at ${child.position}"
"extsub should be empty at ${child.position}"
}
} else {
// regular asmsub
@@ -1665,7 +1752,7 @@ class IRCodeGen(
)
irBlock += IRAsmSubroutine(
child.name,
child.address,
null,
child.clobbers,
child.parameters.map { IRAsmSubroutine.IRAsmParam(it.first, it.second.type) }, // note: the name of the asmsub param is not used here anymore
child.returns.map { IRAsmSubroutine.IRAsmParam(it.first, it.second)},
@@ -1689,27 +1776,30 @@ class IRCodeGen(
return irBlock
}
private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map {
val flattenedName = it.definingISub()!!.name + "." + it.name
if(symbolTable.lookup(flattenedName)==null)
TODO("fix missing lookup for: $flattenedName parameter")
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
IRSubroutine.IRParam(flattenedName, orig.dt)
}
private fun translate(alignment: PtBlock.BlockAlignment): IRBlock.BlockAlignment {
return when(alignment) {
PtBlock.BlockAlignment.NONE -> IRBlock.BlockAlignment.NONE
PtBlock.BlockAlignment.WORD -> IRBlock.BlockAlignment.WORD
PtBlock.BlockAlignment.PAGE -> IRBlock.BlockAlignment.PAGE
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
val result = mutableListOf<IRSubroutine.IRParam>()
parameters.forEach {
if(it.register==null) {
val flattenedName = it.definingISub()!!.name + "." + it.name
if (symbolTable.lookup(flattenedName) == null)
TODO("fix missing lookup for: $flattenedName parameter")
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
result += IRSubroutine.IRParam(flattenedName, orig.dt)
} else {
val reg = it.register
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
val regname = it.register!!.asScopedNameVirtualReg(it.type).joinToString(".")
val targetVar = symbolTable.lookup(regname) as StMemVar
result += IRSubroutine.IRParam(regname, targetVar.dt)
}
}
return result
}
private var labelSequenceNumber = 0
internal fun createLabelName(): String {
labelSequenceNumber++
return "label_gen_$labelSequenceNumber"
return "${PtLabel.GENERATED_LABEL_PREFIX}$labelSequenceNumber"
}
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
@@ -64,7 +64,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
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) {
@@ -84,7 +84,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
/*
Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1.
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label ->
should not have been generated! ERROR.
@@ -195,10 +195,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
chunks += if(label!=null)
IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
else
chunks += candidate
candidate
}
}
is IRInlineBinaryChunk -> {
@@ -206,10 +206,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
chunks += if(label!=null)
IRInlineBinaryChunk(label, candidate.data, candidate.next)
else
chunks += candidate
candidate
}
}
}
@@ -299,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)
@@ -449,7 +449,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.forEach { (idx, ins) ->
if(ins.opcode==Opcode.STOREM) {
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.
+18 -9
View File
@@ -2,16 +2,25 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5
else -> 2
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray || dt.isSplitWordArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
BaseDataType.FLOAT -> numElements*5
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> 5 * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
@@ -22,7 +22,8 @@ class TestIRPeepholeOpt: FunSpec({
floats = false,
noSysInit = true,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu
)
val prog = IRProgram("test", IRSymbolTable(), options, target)
prog.addBlock(block)
+179 -93
View File
@@ -4,6 +4,7 @@ import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.code.target.VMTarget
import prog8.codegen.vm.VmAssemblyProgram
import prog8.codegen.vm.VmCodeGen
@@ -23,7 +24,8 @@ class TestVmCodeGen: FunSpec({
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu
)
}
@@ -44,48 +46,84 @@ class TestVmCodeGen: 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.forDt(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
PtNumber(BaseDataType.UBYTE, 0.0, Position.DUMMY),
null,
Position.DUMMY
))
sub.add(PtVariable(
"particleX",
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"particleDX",
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"xx",
DataType.forDt(BaseDataType.WORD),
ZeropageWish.DONTCARE,
0u,
PtNumber(BaseDataType.WORD, 1.0, Position.DUMMY),
null,
Position.DUMMY
))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
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))
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX",
DataType.arrayFor(BaseDataType.UBYTE),
Position.DUMMY))
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
}
it.add(targetIdx)
}
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX",
DataType.arrayFor(BaseDataType.UBYTE),
Position.DUMMY))
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target)
assign.add(value)
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
sub.add(cxregAssign)
block.add(sub)
@@ -93,7 +131,7 @@ class TestVmCodeGen: FunSpec({
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
program.add(cx16block)
val options = getTestOptions()
@@ -123,35 +161,43 @@ class TestVmCodeGen: 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("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"f1",
DataType.forDt(BaseDataType.FLOAT),
ZeropageWish.DONTCARE,
0u,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -186,35 +232,43 @@ class TestVmCodeGen: 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("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"f1",
DataType.forDt(BaseDataType.FLOAT),
ZeropageWish.DONTCARE,
0u,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -245,21 +299,29 @@ class TestVmCodeGen: 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("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"f1",
DataType.forDt(BaseDataType.FLOAT),
ZeropageWish.DONTCARE,
0u,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@@ -292,35 +354,43 @@ class TestVmCodeGen: 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("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"sb1",
DataType.forDt(BaseDataType.BYTE),
ZeropageWish.DONTCARE,
0u,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -355,35 +425,43 @@ class TestVmCodeGen: 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("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"sb1",
DataType.forDt(BaseDataType.BYTE),
ZeropageWish.DONTCARE,
0u,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -414,21 +492,29 @@ class TestVmCodeGen: 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("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"ub1",
DataType.forDt(BaseDataType.BYTE),
ZeropageWish.DONTCARE,
0u,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@@ -442,9 +528,9 @@ class TestVmCodeGen: FunSpec({
irChunks.size shouldBe 1
}
test("romsub allowed in ir-codegen") {
test("extsub allowed in ir-codegen") {
//main {
// romsub $5000 = routine()
// extsub $5000 = routine()
//
// sub start() {
// routine()
@@ -453,10 +539,10 @@ class TestVmCodeGen: FunSpec({
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(romsub)
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(extsub)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
val call = PtFunctionCall("main.routine", true, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY)
sub.add(call)
block.add(sub)
program.add(block)
-44
View File
@@ -1,44 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!
+26
View File
@@ -0,0 +1,26 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":compilerAst"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
}
// note: there are no unit tests in this module!
@@ -5,10 +5,10 @@ import prog8.ast.base.ExpressionError
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.NumericLiteral
import prog8.code.core.DataType
import prog8.code.core.IntegerDatatypes
import prog8.code.core.IntegerDatatypesWithBoolean
import prog8.code.core.BaseDataType
import prog8.code.core.Position
import prog8.code.core.isInteger
import prog8.code.core.isIntegerOrBool
import kotlin.math.*
@@ -44,10 +44,10 @@ class ConstExprEvaluator {
}
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
if(!left.type.isInteger || !amount.type.isInteger)
throw ExpressionError("cannot compute $left >> $amount", left.position)
val result =
if(left.type== DataType.UBYTE || left.type== DataType.UWORD)
if(left.type == BaseDataType.UBYTE || left.type == BaseDataType.UWORD)
left.number.toInt().ushr(amount.number.toInt())
else
left.number.toInt().shr(amount.number.toInt())
@@ -55,7 +55,7 @@ class ConstExprEvaluator {
}
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
if(!left.type.isInteger || !amount.type.isInteger)
throw ExpressionError("cannot compute $left << $amount", left.position)
val result = left.number.toInt().shl(amount.number.toInt())
// when(left.type) {
@@ -70,33 +70,34 @@ class ConstExprEvaluator {
}
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type==DataType.LONG) {
if(right.type in IntegerDatatypes) {
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left ^ $right", left.position)
}
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type==DataType.LONG) {
if(right.type in IntegerDatatypes) {
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
}
}
@@ -104,16 +105,16 @@ class ConstExprEvaluator {
}
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type== DataType.LONG) {
if(right.type in IntegerDatatypes) {
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
}
}
@@ -131,15 +132,15 @@ class ConstExprEvaluator {
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot add $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() + right.number, left.position)
return when {
left.type.isInteger -> when {
right.type.isInteger -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() + right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number.toInt() + right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number + right.number, left.position)
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> NumericLiteral(BaseDataType.FLOAT, left.number + right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number + right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@@ -148,15 +149,15 @@ class ConstExprEvaluator {
private fun minus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot subtract $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() - right.number, left.position)
return when {
left.type.isInteger -> when {
right.type.isInteger -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() - right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number.toInt() - right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number - right.number, left.position)
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> NumericLiteral(BaseDataType.FLOAT, left.number - right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number - right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@@ -165,15 +166,15 @@ class ConstExprEvaluator {
private fun multiply(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot multiply ${left.type} and ${right.type}"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() * right.number, left.position)
return when {
left.type.isInteger -> when {
right.type.isInteger -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() * right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number.toInt() * right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number * right.number, left.position)
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> NumericLiteral(BaseDataType.FLOAT, left.number * right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number * right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@@ -185,27 +186,27 @@ class ConstExprEvaluator {
private fun divide(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot divide $left by $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
return when {
left.type.isInteger -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
val result: Int = left.number.toInt() / right.number.toInt()
NumericLiteral.optimalInteger(left.type, right.type, result, left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number.toInt() / right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number.toInt() / right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number / right.number.toInt(), left.position)
NumericLiteral(BaseDataType.FLOAT, left.number / right.number.toInt(), left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number / right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number / right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
@@ -215,26 +216,26 @@ class ConstExprEvaluator {
private fun remainder(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute remainder of $left by $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
return when {
left.type.isInteger -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteral.optimalNumeric(left.type, right.type, left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number.toInt() % right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number.toInt() % right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number % right.number.toInt(), left.position)
NumericLiteral(BaseDataType.FLOAT, left.number % right.number.toInt(), left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number % right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number % right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
@@ -252,7 +253,7 @@ class ConstExprEvaluator {
return when(call.target.nameInSource[0]) {
"math" -> evalMath(call, constArgs)
"floats" -> evalFloats(call, constArgs)
"string" -> evalString(call, constArgs)
"strings" -> evalString(call, constArgs)
else -> null
}
}
@@ -290,50 +291,50 @@ class ConstExprEvaluator {
return if(result==null)
null
else
NumericLiteral(DataType.FLOAT, result, func.position)
NumericLiteral(BaseDataType.FLOAT, result, func.position)
}
private fun evalMath(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
return when(func.target.nameInSource[1]) {
"sin8u" -> {
val value = truncate(128.0 + 127.5 * sin(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"cos8u" -> {
val value = truncate(128.0 + 127.5 * cos(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"sin8" -> {
val value = truncate(127.0 * sin(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"cos8" -> {
val value = truncate(127.0 * cos(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"sinr8u" -> {
val value = truncate(128.0 + 127.5 * sin(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"cosr8u" -> {
val value = truncate(128.0 + 127.5 * cos(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"sinr8" -> {
val value = truncate(127.0 * sin(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"cosr8" -> {
val value = truncate(127.0 * cos(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"log2" -> {
val value = truncate(log2(args.single().number))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"log2w" -> {
val value = truncate(log2(args.single().number))
NumericLiteral(DataType.UWORD, value, func.position)
NumericLiteral(BaseDataType.UWORD, value, func.position)
}
"atan2" -> {
val x1f = args[0].number
@@ -343,19 +344,19 @@ class ConstExprEvaluator {
var radians = atan2(y2f-y1f, x2f-x1f)
if(radians<0)
radians+=2*PI
NumericLiteral(DataType.UWORD, floor(radians/2.0/PI*256.0), func.position)
NumericLiteral(BaseDataType.UWORD, floor(radians/2.0/PI*256.0), func.position)
}
"diff" -> {
val n1 = args[0].number
val n2 = args[1].number
val value = if(n1>n2) n1-n2 else n2-n1
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"diffw" -> {
val n1 = args[0].number
val n2 = args[1].number
val value = if(n1>n2) n1-n2 else n2-n1
NumericLiteral(DataType.UWORD, value, func.position)
NumericLiteral(BaseDataType.UWORD, value, func.position)
}
else -> null
}
@@ -2,13 +2,14 @@ package prog8.optimizer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType
import prog8.code.core.BaseDataType
import prog8.code.core.IErrorReporter
import kotlin.math.floor
@@ -34,17 +35,18 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
if(numLiteral.type==DataType.LONG) {
if(numLiteral.type==BaseDataType.LONG) {
// see if LONG values may be reduced to something smaller
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
if(smaller.type!=DataType.LONG)
if(smaller.type!=BaseDataType.LONG) {
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
}
}
if(parent is Assignment) {
val iDt = parent.target.inferType(program)
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED), true)
if(iDt.isKnown && !iDt.isBool && !(iDt issimpletype numLiteral.type)) {
val casted = numLiteral.cast(iDt.getOrUndef().base, true)
if(casted.isValid) {
return listOf(IAstModification.ReplaceNode(numLiteral, casted.valueOrZero(), parent))
}
@@ -87,7 +89,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program)
if(expr.left.inferType(program) istype DataType.STR) {
if(expr.left.inferType(program).isStringLy) {
if(expr.operator=="+" && expr.left is StringLiteral && expr.right is StringLiteral) {
// concatenate two strings.
val leftString = expr.left as StringLiteral
@@ -112,6 +114,28 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}
}
if(expr.left.inferType(program).isArray) {
if (expr.operator=="*" && rightconst!=null) {
if (expr.left is ArrayLiteral) {
// concatenate array literal.
val part = expr.left as ArrayLiteral
if(part.value.isEmpty())
errors.warn("resulting array has length zero", part.position)
val tmp = mutableListOf<Expression>()
repeat(rightconst.number.toInt()) {
part.value.forEach { tmp += it.copy() }
}
val newArray = ArrayLiteral(part.type, tmp.toTypedArray(), part.position)
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
}
else {
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program)
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
}
}
}
if(expr.operator=="==" && rightconst!=null) {
val leftExpr = expr.left as? BinaryExpression
// only do this shuffling when the LHS is not a constant itself (otherwise problematic nested replacements)
@@ -140,7 +164,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}
}
if(expr.inferType(program) istype DataType.FLOAT) {
if(expr.inferType(program) issimpletype BaseDataType.FLOAT) {
val subExpr: BinaryExpression? = when {
leftconst != null -> expr.right as? BinaryExpression
rightconst != null -> expr.left as? BinaryExpression
@@ -211,7 +235,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val newExpr = BinaryExpression(leftBinExpr.left, "*", constants, expr.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
} else if (leftBinExpr.operator=="/") {
if(expr.inferType(program).istype(DataType.FLOAT)) {
if(expr.inferType(program) issimpletype BaseDataType.FLOAT) {
// (X / C2) * rightConst --> X * (rightConst/C2) only valid for floating point
val constants = BinaryExpression(rightconst, "/", c2, c2.position)
val newExpr = BinaryExpression(leftBinExpr.left, "*", constants, expr.position)
@@ -289,7 +313,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
} else {
val arrayDt = array.guessDatatype(program)
if (arrayDt.isKnown) {
val newArray = array.cast(arrayDt.getOr(DataType.UNDEFINED))
val newArray = array.cast(arrayDt.getOrUndef())
if (newArray != null && newArray != array)
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
}
@@ -298,6 +322,31 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return noModifications
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(parent is VarDecl && parent.parent is Block) {
// only block level (global) initializers are considered here, because they're run just once at program startup
val const = arrayIndexedExpression.constValue(program)
if (const != null)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, const, parent))
val constIndex = arrayIndexedExpression.indexer.constIndex()
if (constIndex != null) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
if(arrayVar!=null) {
val array =arrayVar.value as? ArrayLiteral
if(array!=null) {
val value = array.value[constIndex].constValue(program)
if(value!=null) {
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
}
}
}
}
}
return noModifications
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val constvalue = functionCallExpr.constValue(program)
return if(constvalue!=null)
@@ -311,16 +360,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}
}
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
val constvalue = bfc.constValue(program)
return if(constvalue!=null)
listOf(IAstModification.ReplaceNode(bfc, constvalue, parent))
else
noModifications
}
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: BaseDataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
val fromCast = rangeFrom.cast(targetDt, true)
val toCast = rangeTo.cast(targetDt, true)
if(!fromCast.isValid || !toCast.isValid)
@@ -349,35 +390,37 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
val stepLiteral = iterableRange.step as? NumericLiteral
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
require(loopvar.datatype.sub == null)
val loopvarSimpleDt = loopvar.datatype.base
when(loopvarSimpleDt) {
BaseDataType.UBYTE -> {
if(rangeFrom.type != BaseDataType.UBYTE) {
// attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.BYTE -> {
if(rangeFrom.type!= DataType.BYTE) {
BaseDataType.BYTE -> {
if(rangeFrom.type != BaseDataType.BYTE) {
// attempt to translate the iterable into byte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.UWORD -> {
if(rangeFrom.type!= DataType.UWORD) {
BaseDataType.UWORD -> {
if(rangeFrom.type != BaseDataType.UWORD) {
// attempt to translate the iterable into uword values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.WORD -> {
if(rangeFrom.type!= DataType.WORD) {
BaseDataType.WORD -> {
if(rangeFrom.type != BaseDataType.WORD) {
// attempt to translate the iterable into word values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
@@ -392,13 +435,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val numval = decl.value as? NumericLiteral
if(decl.type== VarDeclType.CONST && numval!=null) {
val valueDt = numval.inferType(program)
if(valueDt istype DataType.LONG) {
if(valueDt issimpletype BaseDataType.LONG) {
return noModifications // this is handled in the numericalvalue case
}
if(valueDt isnot decl.datatype) {
val cast = numval.cast(decl.datatype, true)
if (cast.isValid)
if(!(valueDt istype decl.datatype)) {
val cast = numval.cast(decl.datatype.base, true)
if (cast.isValid) {
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
}
}
}
return noModifications
@@ -419,6 +463,18 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val address = subroutine.asmAddress?.address
if(address!=null) {
val constAddress = address.constValue(program)
if(constAddress!=null) {
subroutine.asmAddress!!.address = constAddress
constAddress.parent = subroutine
}
}
return noModifications
}
private class ShuffleOperands(val expr: BinaryExpression,
val exprOperator: String?,
val subExpr: BinaryExpression,
@@ -527,7 +583,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return IAstModification.ReplaceNode(expr, change, expr.parent)
}
}
else if(expr.operator=="*" && subExpr.operator=="/" && subExpr.inferType(program).istype(DataType.FLOAT)) {
else if(expr.operator=="*" && subExpr.operator=="/" && subExpr.inferType(program) issimpletype BaseDataType.FLOAT) {
// division optimizations only valid for floats
if(leftIsConst) {
val change = if(subleftIsConst) {
@@ -31,16 +31,10 @@ class VarConstantValueTypeAdjuster(
try {
val declConstValue = decl.value?.constValue(program)
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
&& declConstValue.type != decl.datatype) {
// avoid silent float roundings
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
&& declConstValue.type != decl.datatype.base) {
if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) {
// avoid silent float roundings
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
} else if(decl.datatype!=DataType.BOOL) {
// cast the numeric literal to the appropriate datatype of the variable if it's not boolean
declConstValue.linkParents(decl)
val cast = declConstValue.cast(decl.datatype, true)
if (cast.isValid)
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
}
}
} catch (x: UndefinedSymbolError) {
@@ -49,7 +43,7 @@ class VarConstantValueTypeAdjuster(
// replace variables by constants, if possible
if(options.optimize) {
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || decl.datatype !in NumericDatatypes)
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || !decl.datatype.isNumeric)
return noModifications
if (decl.value != null && decl.value!!.constValue(program) == null)
return noModifications
@@ -76,7 +70,7 @@ class VarConstantValueTypeAdjuster(
if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position)
decl.value = null
return listOf(
IAstModification.ReplaceNode(decl, const, parent)
@@ -94,9 +88,9 @@ class VarConstantValueTypeAdjuster(
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
// variable only has a single write, and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
@@ -155,9 +149,9 @@ class VarConstantValueTypeAdjuster(
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val replaceFunc = if(t1.isBytes) {
if(t1.istype(DataType.BYTE)) "clamp__byte" else "clamp__ubyte"
if(t1 issimpletype BaseDataType.BYTE) "clamp__byte" else "clamp__ubyte"
} else if(t1.isInteger) {
if(t1.istype(DataType.WORD)) "clamp__word" else "clamp__uword"
if(t1 issimpletype BaseDataType.WORD) "clamp__word" else "clamp__uword"
} else {
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
return noModifications
@@ -174,12 +168,12 @@ class VarConstantValueTypeAdjuster(
val funcName = func[0]
val replaceFunc: String
if(t1.isBytes && t2.isBytes) {
replaceFunc = if(t1.istype(DataType.BYTE) || t2.istype(DataType.BYTE))
replaceFunc = if(t1 issimpletype BaseDataType.BYTE || t2 issimpletype BaseDataType.BYTE)
"${funcName}__byte"
else
"${funcName}__ubyte"
} else if(t1.isInteger && t2.isInteger) {
replaceFunc = if(t1.istype(DataType.WORD) || t2.istype(DataType.WORD))
replaceFunc = if(t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD)
"${funcName}__word"
else
"${funcName}__uword"
@@ -199,11 +193,11 @@ class VarConstantValueTypeAdjuster(
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.BYTE -> "abs__byte"
DataType.WORD -> "abs__word"
DataType.FLOAT -> "abs__float"
DataType.UBYTE, DataType.UWORD -> {
val replaceFunc = when {
dt.isSignedByte -> "abs__byte"
dt.isSignedWord -> "abs__word"
dt.isFloat -> "abs__float"
dt.isUnsignedByte || dt.isUnsignedWord -> {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
else -> {
@@ -220,10 +214,10 @@ class VarConstantValueTypeAdjuster(
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "sqrt__ubyte"
DataType.UWORD -> "sqrt__uword"
DataType.FLOAT -> "sqrt__float"
val replaceFunc = when {
dt.isUnsignedByte -> "sqrt__ubyte"
dt.isUnsignedWord -> "sqrt__uword"
dt.isFloat -> "sqrt__float"
else -> {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications
@@ -249,9 +243,9 @@ class VarConstantValueTypeAdjuster(
val t1 = argTypes.single()
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "divmod__ubyte"
DataType.UWORD -> "divmod__uword"
val replaceFunc = when {
dt.isUnsignedByte -> "divmod__ubyte"
dt.isUnsignedWord -> "divmod__uword"
else -> {
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
return noModifications
@@ -268,11 +262,9 @@ class VarConstantValueTypeAdjuster(
// Replace all constant identifiers with their actual value,
// and the array var initializer values and sizes.
// This is needed because further constant optimizations depend on those.
internal class ConstantIdentifierReplacer(
private val program: Program,
private val options: CompilationOptions,
private val errors: IErrorReporter
) : AstWalker() {
@@ -287,7 +279,7 @@ internal class ConstantIdentifierReplacer(
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
// replace identifiers that refer to const value, with the value itself
// if it's a simple type and if it's not a left hand side variable
if(identifier.parent is AssignTarget)
if(identifier.parent is AssignTarget || identifier.parent is Alias)
return noModifications
var forloop = identifier.parent as? ForLoop
if(forloop==null)
@@ -302,7 +294,7 @@ internal class ConstantIdentifierReplacer(
try {
val cval = identifier.constValue(program) ?: return noModifications
val arrayIdx = identifier.parent as? ArrayIndexedExpression
if(arrayIdx!=null && cval.type in NumericDatatypes) {
if(arrayIdx!=null && cval.type.isNumeric) {
// special case when the identifier is used as a pointer var
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
@@ -316,8 +308,8 @@ internal class ConstantIdentifierReplacer(
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
}
}
when (cval.type) {
in NumericDatatypesWithBoolean -> {
when {
cval.type.isNumericOrBool -> {
if(parent is AddressOf)
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
return listOf(
@@ -328,7 +320,7 @@ internal class ConstantIdentifierReplacer(
)
)
}
in PassByReferenceDatatypes -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
else -> return noModifications
}
} catch (x: UndefinedSymbolError) {
@@ -369,16 +361,16 @@ internal class ConstantIdentifierReplacer(
}
}
when(decl.datatype) {
DataType.FLOAT -> {
when {
decl.datatype.isFloat -> {
// vardecl: for scalar float vars, promote constant integer initialization values to floats
val litval = decl.value as? NumericLiteral
if (litval!=null && litval.type in IntegerDatatypesWithBoolean) {
val newValue = NumericLiteral(DataType.FLOAT, litval.number, litval.position)
if (litval!=null && litval.type.isIntegerOrBool) {
val newValue = NumericLiteral(BaseDataType.FLOAT, litval.number, litval.position)
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
}
}
in ArrayDatatypes -> {
decl.datatype.isArray -> {
val replacedArrayInitializer = createConstArrayInitializerValue(decl)
if(replacedArrayInitializer!=null)
return listOf(IAstModification.ReplaceNode(decl.value!!, replacedArrayInitializer, decl))
@@ -398,9 +390,9 @@ internal class ConstantIdentifierReplacer(
if(range!=null) {
val targetDatatype = assignment.target.inferType(program)
if(targetDatatype.isArray) {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOr(DataType.UNDEFINED),
ZeropageWish.DONTCARE, null, "dummy", emptyList(),
assignment.value, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOrUndef(),
ZeropageWish.DONTCARE, SplitWish.NOSPLIT, null, "dummy", emptyList(),
assignment.value, false, 0u, false, Position.DUMMY)
val replaceValue = createConstArrayInitializerValue(decl)
if(replaceValue!=null) {
return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment))
@@ -421,104 +413,43 @@ internal class ConstantIdentifierReplacer(
return null
}
// convert the initializer range expression from a range or int, to an actual array.
when(decl.datatype) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> {
val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) {
val constRange = rangeExpr.toConstantIntegerRange()
if(constRange?.isEmpty()==true) {
if(constRange.first>constRange.last && constRange.step>=0)
errors.err("descending range with positive step", decl.value?.position!!)
else if(constRange.first<constRange.last && constRange.step<=0)
errors.err("ascending range with negative step", decl.value?.position!!)
}
val declArraySize = decl.arraysize?.constIndex()
if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
if(constRange!=null) {
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
return if(eltType in ByteDatatypes) {
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
} else {
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
}
}
val numericLv = decl.value as? NumericLiteral
if(numericLv!=null && numericLv.type== DataType.FLOAT)
errors.err("arraysize requires only integers here", numericLv.position)
val size = decl.arraysize?.constIndex() ?: return null
if (rangeExpr==null && numericLv!=null) {
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
val fillvalue = numericLv.number.toInt()
when(decl.datatype){
DataType.ARRAY_UB -> {
if(fillvalue !in 0..255)
errors.err("ubyte value overflow", numericLv.position)
}
DataType.ARRAY_B -> {
if(fillvalue !in -128..127)
errors.err("byte value overflow", numericLv.position)
}
DataType.ARRAY_UW -> {
if(fillvalue !in 0..65535)
errors.err("uword value overflow", numericLv.position)
}
DataType.ARRAY_W -> {
if(fillvalue !in -32768..32767)
errors.err("word value overflow", numericLv.position)
}
else -> {}
}
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
return ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
}
}
DataType.ARRAY_F -> {
val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) {
// convert the initializer range expression to an actual array of floats
val declArraySize = decl.arraysize?.constIndex()
if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
val constRange = rangeExpr.toConstantIntegerRange()
if(constRange!=null) {
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F),
constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
val rangeExpr = decl.value as? RangeExpression ?: return null
// convert the initializer range expression from a range, to an actual array literal.
val declArraySize = decl.arraysize?.constIndex()
val constRange = rangeExpr.toConstantIntegerRange()
if(constRange?.isEmpty()==true) {
if(constRange.first>constRange.last && constRange.step>=0)
errors.err("descending range with positive step", decl.value?.position!!)
else if(constRange.first<constRange.last && constRange.step<=0)
errors.err("ascending range with negative step", decl.value?.position!!)
}
val dt = decl.datatype
when {
dt.isUnsignedByteArray || dt.isSignedByteArray || dt.isUnsignedWordArray || dt.isSignedWordArray -> {
if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
if(constRange!=null) {
val rangeType = rangeExpr.inferType(program).getOr(DataType.forDt(BaseDataType.UBYTE))
return if(rangeType.isByte) {
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
} else {
require(rangeType.sub!=null)
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(rangeType.sub!!.dt, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
}
val numericLv = decl.value as? NumericLiteral
val size = decl.arraysize?.constIndex() ?: return null
if(rangeExpr==null && numericLv!=null) {
// arraysize initializer is a single int, and we know the array size.
val fillvalue = numericLv.number
if (fillvalue < options.compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > options.compTarget.machine.FLOAT_MAX_POSITIVE)
errors.err("float value overflow", numericLv.position)
else {
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
}
}
}
DataType.ARRAY_BOOL -> {
val numericLv = decl.value as? NumericLiteral
val size = decl.arraysize?.constIndex() ?: return null
if(numericLv!=null) {
// arraysize initializer is a single value, and we know the array size.
if(numericLv.type!=DataType.BOOL) {
errors.err("initializer value is not a boolean", numericLv.position)
return null
}
val array = Array(size) {numericLv.number}.map { NumericLiteral(DataType.BOOL, it, numericLv.position) }.toTypedArray<Expression>()
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
dt.isFloatArray -> {
if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
if(constRange!=null) {
return ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.FLOAT, false)),
constRange.map { NumericLiteral(BaseDataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
}
else -> return null
@@ -30,11 +30,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val subTypecast = typecast.expression as? TypecastExpression
if (subTypecast != null) {
// remove the sub-typecast if its datatype is larger than the outer typecast
if(subTypecast.type largerThan typecast.type) {
if(subTypecast.type.largerSizeThan(typecast.type)) {
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
}
} else {
if (typecast.expression.inferType(program) istype typecast.type) {
if (typecast.expression.inferType(program) issimpletype typecast.type) {
// remove duplicate cast
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
}
@@ -67,19 +67,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val booleanCondition = ifElse.condition as? BinaryExpression
if(booleanCondition!=null && booleanCondition.operator=="&") {
val rightNum = booleanCondition.right as? NumericLiteral
if (rightNum!=null && rightNum.type==DataType.UWORD) {
if (rightNum!=null && rightNum.type==BaseDataType.UWORD) {
if ((rightNum.number.toInt() and 0x00ff) == 0) {
// if WORD & $xx00 -> if msb(WORD) & $xx
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
return listOf(
IAstModification.ReplaceNode(booleanCondition.left, msb, booleanCondition),
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
}
else if ((rightNum.number.toInt() and 0xff00) == 0) {
// if WORD & $00ff -> if lsb(WORD) & $ff
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, rightNum.number, booleanCondition.right.position)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, rightNum.number, booleanCondition.right.position)
return listOf(
IAstModification.ReplaceNode(booleanCondition.left, lsb, booleanCondition),
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
@@ -130,12 +130,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
))
}
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
val leftDt = leftIDt.getOrUndef()
val rightDt = rightIDt.getOrUndef()
if (expr.operator == "+" || expr.operator == "-"
&& leftVal == null && rightVal == null
&& leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
&& leftDt.isNumeric && rightDt.isNumeric) {
val leftBinExpr = expr.left as? BinaryExpression
val rightBinExpr = expr.right as? BinaryExpression
if (leftBinExpr?.operator == "*") {
@@ -145,7 +145,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val x = expr.right
val y = determineY(x, leftBinExpr)
if (y != null) {
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt, 1.0, y.position), y.position)
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt.base, 1.0, y.position), y.position)
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
}
@@ -155,7 +155,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val x = expr.right
val y = determineY(x, leftBinExpr)
if (y != null) {
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt, 1.0, y.position), y.position)
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt.base, 1.0, y.position), y.position)
val replacement = BinaryExpression(x, "*", yMinus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
}
@@ -176,7 +176,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
// X <= Y-1 ---> X<Y , X >= Y+1 ---> X>Y
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
if(leftDt.isInteger && rightDt.isInteger) {
val rightExpr = expr.right as? BinaryExpression
if(rightExpr!=null && rightExpr.right.constValue(program)?.number==1.0) {
if (expr.operator == "<=" && rightExpr.operator == "-") {
@@ -189,24 +189,24 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
if(leftDt!=DataType.FLOAT && expr.operator == ">=" && rightVal?.number == 1.0) {
if(!leftDt.isFloat && expr.operator == ">=" && rightVal?.number == 1.0) {
// for integers: x >= 1 --> x > 0
expr.operator = ">"
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
}
// for signed integers: X <= -1 => X<0 , X > -1 => X>=0
if(leftDt in SignedDatatypes && leftDt!=DataType.FLOAT && rightVal?.number==-1.0) {
if(leftDt.isSigned && !leftDt.isFloat && rightVal?.number==-1.0) {
if(expr.operator=="<=") {
expr.operator = "<"
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt, 0.0, expr.right.position), expr))
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt.base, 0.0, expr.right.position), expr))
} else if(expr.operator==">") {
expr.operator = ">="
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt, 0.0, expr.right.position), expr))
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt.base, 0.0, expr.right.position), expr))
}
}
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
if (leftDt.isUnsignedByte || leftDt.isUnsignedWord) {
if(expr.operator == ">=" && rightVal?.number == 0.0) {
// unsigned >= 0 --> true
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(true, expr.position), parent))
@@ -217,13 +217,13 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
if(leftDt!=DataType.FLOAT && expr.operator == "<" && rightVal?.number == 1.0) {
if(!leftDt.isFloat && expr.operator == "<" && rightVal?.number == 1.0) {
// for integers: x < 1 --> x <= 0
expr.operator = "<="
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
}
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
if (leftDt.isUnsignedByte || leftDt.isUnsignedWord) {
if(expr.operator == "<" && rightVal?.number == 0.0) {
// unsigned < 0 --> false
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
@@ -236,47 +236,41 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// optimize boolean constant comparisons
if(expr.operator=="==") {
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
if(rightDt.isBool && leftDt.isBool) {
val rightConstBool = rightVal?.asBooleanValue
if(rightConstBool!=null) {
return if(rightConstBool)
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
else
listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
if(rightConstBool==true) {
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
}
}
if (rightVal?.number == 1.0) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
else if (rightVal?.number == 0.0) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
}
if (expr.operator=="!=") {
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
if(rightDt.isBool && leftDt.isBool) {
val rightConstBool = rightVal?.asBooleanValue
if(rightConstBool!=null) {
return if (rightConstBool)
listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
else
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
if(rightConstBool==false) {
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
}
}
if (rightVal?.number == 1.0) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
else if (rightVal?.number == 0.0) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
@@ -328,10 +322,10 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
when {
leftVal?.number==0.0 -> expr.right
rightVal?.number==0.0 -> expr.left
rightIDt.isBytes && rightVal?.number==255.0 -> NumericLiteral(DataType.UBYTE, 255.0, rightVal.position)
rightIDt.isWords && rightVal?.number==65535.0 -> NumericLiteral(DataType.UWORD, 65535.0, rightVal.position)
leftIDt.isBytes && leftVal?.number==255.0 -> NumericLiteral(DataType.UBYTE, 255.0, leftVal.position)
leftIDt.isWords && leftVal?.number==65535.0 -> NumericLiteral(DataType.UWORD, 65535.0, leftVal.position)
rightIDt.isBytes && rightVal?.number==255.0 -> NumericLiteral(BaseDataType.UBYTE, 255.0, rightVal.position)
rightIDt.isWords && rightVal?.number==65535.0 -> NumericLiteral(BaseDataType.UWORD, 65535.0, rightVal.position)
leftIDt.isBytes && leftVal?.number==255.0 -> NumericLiteral(BaseDataType.UBYTE, 255.0, leftVal.position)
leftIDt.isWords && leftVal?.number==65535.0 -> NumericLiteral(BaseDataType.UWORD, 65535.0, leftVal.position)
else -> null
}
}
@@ -367,7 +361,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else -> null
}
if(rightVal!=null && leftDt==DataType.BOOL) {
if(rightVal!=null && leftDt.isBool) {
// boolean compare against a number -> just keep the boolean, no compare
if(expr.operator=="==" || expr.operator=="!=") {
val test = if (expr.operator == "==") rightVal.asBooleanValue else !rightVal.asBooleanValue
@@ -384,30 +378,44 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if (rightVal!=null && (expr.operator == "==" || expr.operator == "!=")) {
val bitwise = expr.left as? BinaryExpression
if(bitwise!=null && bitwise.operator=="&" && bitwise.inferType(program).isWords) {
val andNum = (bitwise.right as? NumericLiteral)?.number?.toInt()
if (andNum!=null) {
if ((andNum and 0x00ff) == 0) {
// (WORD & $xx00)==y -> (msb(WORD) & $xx)==y
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, (andNum shr 8).toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() shr 8).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, msb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
if (bitwise!=null && bitwise.operator=="&") {
if(rightVal.number in powersOfTwoFloat && bitwise.right.constValue(program) == rightVal) {
if(expr.operator=="==") {
// x & const == const -> x & const != 0 (if const has only a single bit set!)
val notZero = BinaryExpression(bitwise, "!=", NumericLiteral(rightDt.base, 0.0, rightVal.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, notZero, parent))
} else {
// x & const != const -> x & const == 0 (if const has only a single bit set!)
val equalsZero = BinaryExpression(bitwise, "==", NumericLiteral(rightDt.base, 0.0, rightVal.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, equalsZero, parent))
}
else if((andNum and 0xff00) == 0) {
// (WORD & $00xx)==y -> (lsb(WORD) & $xx)==y
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, andNum.toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() and 255).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, lsb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
}
if (bitwise.inferType(program).isWords) {
val andNum = (bitwise.right as? NumericLiteral)?.number?.toInt()
if (andNum!=null) {
if ((andNum and 0x00ff) == 0) {
// (WORD & $xx00)==y -> (msb(WORD) & $xx)==y
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, (andNum shr 8).toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(BaseDataType.UBYTE, (rightVal.number.toInt() shr 8).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, msb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
} else if((andNum and 0xff00) == 0) {
// (WORD & $00xx)==y -> (lsb(WORD) & $xx)==y
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, andNum.toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(BaseDataType.UBYTE, (rightVal.number.toInt() and 255).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, lsb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
}
}
}
}
@@ -416,21 +424,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return noModifications
}
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator=="not") {
// not X <compare> Y -> X <invertedcompare> Y
val binExpr = expr.expression as? BinaryExpression
if(binExpr!=null) {
val invertedOperator = invertedComparisonOperator(binExpr.operator)
if(invertedOperator!=null) {
val inverted = BinaryExpression(binExpr.left, invertedOperator, binExpr.right, binExpr.position)
return listOf(IAstModification.ReplaceNode(expr, inverted, parent))
}
}
}
return noModifications
}
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
// NOTE: only when the terms are not function calls!!!
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
@@ -501,13 +494,13 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val arg = functionCallExpr.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
if (valueDt istype DataType.UBYTE) {
if (valueDt issimpletype BaseDataType.UBYTE) {
// useless lsb() of ubyte value
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
}
else if (valueDt istype DataType.BYTE) {
else if (valueDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
} else {
@@ -518,13 +511,13 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program)
if (argDt istype DataType.UBYTE) {
if (argDt issimpletype BaseDataType.UBYTE) {
// useless lsb() of byte value
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg, parent))
}
else if (argDt istype DataType.BYTE) {
else if (argDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
@@ -535,11 +528,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val arg = functionCallExpr.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
if (valueDt issimpletype BaseDataType.BYTE || valueDt issimpletype BaseDataType.UBYTE) {
// useless msb() of byte value that was typecasted to word, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCallExpr,
NumericLiteral(valueDt.getOr(DataType.UBYTE), 0.0, arg.expression.position),
NumericLiteral(valueDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.expression.position),
parent))
}
} else {
@@ -550,11 +543,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program)
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
if (argDt issimpletype BaseDataType.BYTE || argDt issimpletype BaseDataType.UBYTE) {
// useless msb() of byte value, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCallExpr,
NumericLiteral(argDt.getOr(DataType.UBYTE), 0.0, arg.position),
NumericLiteral(argDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.position),
parent))
}
}
@@ -563,11 +556,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
// just cast the lsb to uword
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
else if(functionCallExpr.target.nameInSource == listOf("string", "contains")) {
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
if(target?.value is StringLiteral) {
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
@@ -672,7 +665,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val idt = expr.inferType(program)
if(!idt.isKnown)
throw FatalAstException("unknown dt")
return NumericLiteral(idt.getOr(DataType.UNDEFINED), 0.0, expr.position)
return NumericLiteral(idt.getOrUndef().base, 0.0, expr.position)
} else if (cv in powersOfTwoFloat) {
expr.operator = "&"
expr.right = NumericLiteral.optimalInteger(cv!!.toInt()-1, expr.position)
@@ -696,7 +689,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val leftIDt = expr.left.inferType(program)
if (!leftIDt.isKnown)
return null
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
val leftDt = leftIDt.getOrUndef()
when (cv) {
0.0 -> return null // fall through to regular float division to properly deal with division by zero
-1.0 -> {
@@ -712,36 +705,36 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
256.0 -> {
when(leftDt) {
DataType.UBYTE -> return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
DataType.BYTE -> return null // is either 0 or -1 we cannot tell here
DataType.UWORD, DataType.WORD -> {
when(leftDt.base) {
BaseDataType.UBYTE -> return NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)
BaseDataType.BYTE -> return null // is either 0 or -1 we cannot tell here
BaseDataType.UWORD, BaseDataType.WORD -> {
// just use: msb(value) as type
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return if(leftDt==DataType.WORD)
TypecastExpression(msb, DataType.BYTE, true, expr.position)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return if(leftDt.isSignedWord)
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
else
TypecastExpression(msb, DataType.UWORD, true, expr.position)
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
}
else -> return null
}
}
in powersOfTwoFloat -> {
val numshifts = powersOfTwoFloat.indexOf(cv)
if (leftDt in IntegerDatatypes) {
if (leftDt.isInteger) {
// division by a power of two => shift right (signed and unsigned)
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
}
}
}
if (leftDt == DataType.UBYTE) {
if (leftDt.isUnsignedByte) {
if (abs(rightConst.number) >= 256.0) {
return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
return NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)
}
} else if (leftDt == DataType.UWORD) {
} else if (leftDt.isUnsignedWord) {
if (abs(rightConst.number) >= 65536.0) {
return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
return NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)
}
}
}
@@ -814,22 +807,22 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val targetIDt = expr.left.inferType(program)
if(!targetIDt.isKnown)
throw FatalAstException("unknown dt")
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE, DataType.BYTE -> {
when (val targetDt = targetIDt.getOrUndef().base) {
BaseDataType.UBYTE, BaseDataType.BYTE -> {
if (amount >= 8) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position)
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
if (amount >= 16) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position)
}
else if(amount==8) {
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(DataType.UBYTE, 0.0, expr.position)), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
@@ -838,23 +831,23 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
}
}
DataType.WORD -> {
BaseDataType.WORD -> {
if (amount >= 16) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position)
}
else if(amount==8) {
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(DataType.UBYTE, 0.0, expr.position)), expr.position)
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
}
}
else -> {
@@ -874,20 +867,20 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val idt = expr.left.inferType(program)
if(!idt.isKnown)
throw FatalAstException("unknown dt")
when (idt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> {
when (idt.getOrUndef().base) {
BaseDataType.UBYTE -> {
if (amount >= 8) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral.optimalInteger(0, expr.position)
}
}
DataType.BYTE -> {
BaseDataType.BYTE -> {
if (amount > 8) {
expr.right = NumericLiteral.optimalInteger(8, expr.right.position)
return null
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
if (amount >= 16) {
errors.err("useless to shift by more than 15 bits", expr.position)
return null
@@ -895,15 +888,15 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount==8) {
// shift right by 8 bits is just a byte operation: msb(X) as uword
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
}
}
DataType.WORD -> {
BaseDataType.WORD -> {
if (amount >= 16) {
errors.err("useless to shift by more than 15 bits", expr.position)
return null
@@ -911,12 +904,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount == 8) {
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
}
else if(amount > 8) {
// same as above but with residual shifts. Take care to do signed shift.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
}
}
@@ -12,7 +12,7 @@ fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
if(errors.noErrors()) {
valuetypefixer.applyModifications()
val replacer = ConstantIdentifierReplacer(this, options, errors)
val replacer = ConstantIdentifierReplacer(this, errors)
replacer.visit(this)
if (errors.noErrors()) {
replacer.applyModifications()
Loaded 100 of 396 files, more files were not shown because too many files have changed in this diff. Show more