Compare commits

...

1293 Commits

Author SHA1 Message Date
837e88d61d first setup of LSP languageserver 2025-06-01 17:47:50 +02:00
399cf5118d we will get a 11.4 version first before structs will land 2025-06-01 17:47:06 +02:00
a87f2640d3 fixed signed byte comparisons in case of overflowing values 2025-06-01 14:01:25 +02:00
a90ef274d7 fix word*128 codegen.
added cx16/landscape.p8 example that draws procedurally generated landscapes.
found bug in signed byte comparisons with overflow.
2025-05-31 05:27:19 +02:00
341778ba67 added -timings flag 2025-05-30 12:38:16 +02:00
ec50b5a007 homebrew info 2025-05-30 03:43:03 +02:00
31d84c8921 doc 2025-05-29 13:35:20 +02:00
34bedbeef1 optimize byte modulus (%) routine with repeated subtraction instead of using full division 2025-05-29 13:26:04 +02:00
3b1b0985c1 make sizeof(float) work, so you don't have to use sys.SIZEOF_FLOAT anymore etc.
define sys.SIZEOF_FLOAT in terms of sizeof(float)
2025-05-29 12:38:03 +02:00
368387e1a7 allow floats to be (explicitly) cast to integers 2025-05-26 21:39:48 +02:00
9da430ffeb vm: more complete V-flag handling. somd doc and todo updates. 2025-05-23 18:58:14 +02:00
cc063124cf add joystick control to cx16 fileselector.
fix fileselector Basic exasmple.
fixed too aggressive asm peephole optimization that destroyed %jumptable in libraries for example.
2025-05-23 17:50:11 +02:00
3b37b89951 added cx16.joysticks_detect() and cx16.joysticks_getall() 2025-05-23 02:26:21 +02:00
844b537d1e cobramk3 example now draws with new monogfx doublebuffering 2025-05-22 23:29:49 +02:00
caf1d4a22a fix monogfx INVERT draw mode 2025-05-22 21:29:23 +02:00
d8e244df99 fix monogfx example 2025-05-22 00:37:20 +02:00
548e421e27 added doublebuffering to monogfx (in both lores and hires mode) 2025-05-22 00:10:03 +02:00
322fa7ea69 slightly optimize monogfx plot() 2025-05-21 01:16:43 +02:00
cf7bea0985 cleanup RTS insertion and ast postprocessing before assembly generation 2025-05-21 00:19:50 +02:00
25d7f8808f IR: added signed multiplication opcodes 2025-05-20 21:36:05 +02:00
acc630972a make keyboardhandler example restartable 2025-05-15 23:07:54 +02:00
6a33be3fd8 IR: allow returning boolean in Pc/Pv cpu status register (Pz and Pn are not yet possible) 2025-05-15 22:56:45 +02:00
f5fc4e345c fix build error on case-insensitive filesystems 2025-05-15 21:11:34 +02:00
67231af623 fix forloop codegen over non-split word arrays of length >= 64 elements 2025-05-13 23:32:26 +02:00
e31ef6f06f IR: fix temp register type in for x in array 2025-05-13 22:23:04 +02:00
09d188106a different temp var mechanism for certain array expression, remove old tmpvar mechanism. 2025-05-13 21:12:10 +02:00
d8e2116481 different temp var mechanism for for loops, and pokef() 2025-05-13 21:06:33 +02:00
435dfbb932 optimize: rewrite suitable when into on..goto 2025-05-13 01:12:58 +02:00
ba93966474 optimize codegen: shortcut redundant jumps in when statement 2025-05-13 00:35:22 +02:00
ea8d17cdb2 optimized the cx16 multi-irq dispatcher used in cx16.enable_irq_handlers() 2025-05-12 23:26:54 +02:00
082265fb25 todo 2025-05-12 00:24:57 +02:00
9e557ce8ac add keyword 'on' to IDEA syntax 2025-05-11 23:32:54 +02:00
e5d9af75de remove double bra/jmp 2025-05-11 23:01:13 +02:00
31c1bf8bc5 added on..goto/call statement 2025-05-11 21:37:44 +02:00
37d4055036 translate newline '\n' to char code 13 in various encodings such as ISO (used to be 10)
This means that when printed, such newlines will now properly go to the next line in these encodings too (ISO variants, KATAKANA).
2025-05-11 19:45:24 +02:00
78b1076110 some more software links 2025-05-11 19:39:20 +02:00
0a3c748e41 fix code gen bug for certain goto array[idx] 2025-05-11 17:18:20 +02:00
ebf79ef9e2 release 11.3.2 2025-05-11 12:30:36 +02:00
99b9370178 fix various bugs around word-indexing combined with address-of: &buffer[2000] 2025-05-10 16:22:05 +02:00
d634061cd9 Merge pull request #169 from Frosty-J/crc
Specify X16 CRC type
2025-05-08 23:47:17 +02:00
d59d8ff1fe Specify X16 CRC type 2025-05-08 05:57:30 +01:00
53e442d509 fix regression in 11.3.1: allow bitwise operation between different types as long as they're the same size. 2025-05-07 23:06:45 +02:00
f7cbfdff06 skip this test for now 2025-05-07 21:14:52 +02:00
b28ee0819f revert behavior change of @dirty variables, instead document the initialization behavior correctly in the docs
they get zeroed at program startup (like other uninitialized BSS variables), just not on entry in the subroutine.
2025-05-07 21:07:12 +02:00
522958e0e9 @dirty variables now actually end up in the uninitialized BSS_NOCLEAR section 2025-05-06 01:41:34 +02:00
ccc6b56e35 added link to prog8reu library 2025-05-02 19:38:59 +02:00
7eb079050c fix strings.hash() on 6502 CPU's. NOTE: now takes string pointer in AY instead of R0 on all platforms 2025-05-01 22:47:43 +02:00
2fdd5543b2 release 11.3.1 2025-04-30 22:40:54 +02:00
d04164c0a6 fix const evaluation of bitwise logical expressions (&, |, ^, <<, >>) of signed operands 2025-04-30 22:27:31 +02:00
b047731f82 removed some redundant arguments 2025-04-25 23:27:06 +02:00
4d91f92a2e clearer error message 2025-04-25 19:38:45 +02:00
98505d27b1 IR: fix invalid removal of label chunks and include asmsubs when looking for labels 2025-04-24 23:30:51 +02:00
cd63a58ad9 IR: get rid of problematic PREPARECALL pseudo instruction 2025-04-24 22:32:40 +02:00
170f8dd092 IR: new CALLFAR instructions to support callfar into another bank 2025-04-24 21:17:48 +02:00
619dcb6a84 fix error in IR register typing 2025-04-24 20:50:19 +02:00
99ae8ea52e code cleanup 2025-04-24 18:51:03 +02:00
dc031c30eb add -version option to just print the compiler version and then exit 2025-04-24 18:13:35 +02:00
1e702439b7 avoid JDK 21+/Kotlin method conflict of removeLast()/removeFirst()
background: see https://www.reddit.com/r/androiddev/comments/1gspjrs/dont_use_kotlins_removefirst_and_removelast_when/
https://youtrack.jetbrains.com/issue/KT-71375/Prevent-Kotlins-removeFirst-and-removeLast-from-causing-crashes-on-Android-14-and-below-after-upgrading-to-Android-API-Level-35

it's about Android but the problem also occurs on desktop JDKs for example when running a Java21 compiled prog8 with Java17
2025-04-24 18:11:42 +02:00
8debc42381 slightly stricter assignment type compatibility checking 2025-04-24 15:05:35 +02:00
532d719089 more optimal math.randrange() routines 2025-04-24 04:04:14 +02:00
b40860aca4 get rid of RTS after JMP 2025-04-23 17:08:16 +02:00
2cbe6b5f7f info message when more optimal goto array[idx] is possible 2025-04-23 16:59:18 +02:00
d2cc7ccdfa remove redundant variable=0 initializations (BSS clear takes care of them) 2025-04-23 14:45:38 +02:00
2cb183c6d8 fix regression for goto array[idx] on 6502 cpu which doesn't have jmp (ptr,x) 2025-04-23 02:56:10 +02:00
84026b105f smaller code for goto nosplitptrarray[index] 2025-04-22 20:01:16 +02:00
a4d0589f10 fix errors like parse error still printed in color when -plaintext is set 2025-04-22 19:26:11 +02:00
e375f6afce fix diskio.f_read() returning 1 less than the actual size read.
fix diskio.f_read_all() more robust error status end of loop checking.
2025-04-21 05:02:16 +02:00
5a7bc04816 update docs about library jump table 2025-04-19 13:49:03 +02:00
bd1894580e allow floating point value as part of a multi-value return 2025-04-18 22:45:05 +02:00
9e694c0337 doc 2025-04-18 21:11:27 +02:00
c82586db28 print copyright message better 2025-04-17 23:09:28 +02:00
dd2d466350 code cleanups 2025-04-17 22:58:02 +02:00
830da8de0a memorymapped vars in ZP are now treated as ZP-variables by prog8 itself too 2025-04-17 22:19:01 +02:00
4e5ee333c8 preparing release 11.3 2025-04-17 21:16:52 +02:00
9df899eb63 document romable option and that strings+initialized arrays become read-only 2025-04-17 21:05:46 +02:00
ca7491a702 cx16: sys.enable_irq_handlers() and associated functions are now romable 2025-04-17 00:25:25 +02:00
1a07129865 c64: graphics.horizontal_line() is romable 2025-04-17 00:02:17 +02:00
4fbd67ff99 txt.setcc() is romable 2025-04-16 23:44:50 +02:00
5bc6c50f42 txt.setclr() and getclr() are romable 2025-04-16 23:38:16 +02:00
063de3801d txt.getchr() is romable 2025-04-16 23:32:18 +02:00
ae65266a4a txt.setchr() is romable 2025-04-16 23:27:23 +02:00
8ed2401e0b cx16: txt.scroll_left(), right, up and down are now romable 2025-04-16 23:11:27 +02:00
d2e8ee8269 cx16: txt.fill_screen, txt.clear_screenchars, clear_screencolors are now romable 2025-04-16 22:59:36 +02:00
1f996e3b8b sorting.gnomesort_ub(), compression.decode_rle() and decode_rle_srcfunc() are now romable 2025-04-16 22:47:34 +02:00
7108b74105 string.rfind() is now romable and now works correctly on empty strings.
added some comments to strings.pattern_match
2025-04-16 21:55:55 +02:00
801fe1b604 c64: callfar() is now romable 2025-04-16 21:13:59 +02:00
fb44c87597 make diskio.f_read() ROM-compatible 2025-04-15 00:23:23 +02:00
6b9cdbd482 remove unused arraycopy routines 2025-04-14 22:10:54 +02:00
0ab98033b5 add rom-compatible random number routines 2025-04-14 22:04:11 +02:00
14a2b96609 scanned libraries for self-modifying code/inline vars (romable problem) 2025-04-14 21:15:32 +02:00
f829b689db update zsmkit example to zsmkit release 2.6 2025-04-14 19:28:12 +02:00
dfda8b7ed5 remove problematic rewriting of X=value-X 2025-04-14 02:35:37 +02:00
4388466451 romable comments 2025-04-10 22:21:27 +02:00
5c2f509a52 also hide emulator process output when using -quiet 2025-04-10 21:26:48 +02:00
59582f5210 added -quiet flag to suppres all compiler and assembler messages 2025-04-10 21:16:26 +02:00
e2a8bdbdfb romable comments 2025-04-09 22:35:23 +02:00
0916b943da sys.exit(), exit2(), exit3() now romable 2025-04-09 22:13:52 +02:00
9c7ebc883c fixed: memsetw() invalid asm, c128: sys.restore_irq() 2025-04-09 21:29:48 +02:00
0ee42b9aa0 output_type is now part of custom target config and atari again defaults to .xex 2025-04-09 20:43:44 +02:00
37b3868ca3 symboldump now also includes aliased symbols (like palette.set_default) 2025-04-08 21:40:28 +02:00
a6835ce3f0 fix signed word value range check error, fix rol2() on array element 2025-04-08 21:05:50 +02:00
69c96ad99b cx16: added cx16.push_rambank/rombank and cx16.pop_rambank/rombank for easy temporary bank switching 2025-04-06 15:33:35 +02:00
b72877d59d cx16: added routines to get and set the default palette (new rom 49+ extapi) 2025-04-06 15:14:04 +02:00
05eb15d4f7 add cx16.memory_decompress_from_func() 2025-04-06 13:48:08 +02:00
f1fec37c79 rename examples/vm to examples/virtual 2025-04-04 20:02:45 +02:00
73f6880ff8 fix irq related crash by no longer zeroing out stored vector 2025-04-02 22:22:21 +02:00
8a53742f31 fix notreached error 2025-04-01 23:12:08 +02:00
9be40e85ff Improve romability of low-level libs (#160)
* Improve romability of low-level libs

* Improve ROMability of targeted syslibs

Previous commit just did that for multitarget libs, now it's also syslibs of built-in targets that get this treatment too.
2025-04-01 23:00:14 +02:00
61079c1eb7 errormessage for assignment to str/arrays in ROM 2025-03-31 23:57:04 +02:00
1075ee8fc3 errormessage for non romable extsub bank declaration 2025-03-31 23:05:58 +02:00
a28b265197 fix diskio merge issue on c64 and c128. Fixes #167 2025-03-31 22:06:49 +02:00
20e534c468 fix memtop limit when using -varshigh option 2025-03-27 23:31:05 +01:00
da7aa5dc49 forloop temp index variables no longer inline when romable 2025-03-26 22:38:50 +01:00
8f2a43ca0a temp vars for for loops indices in romable mode, are no longer allocated from the precious Zeropage 2025-03-26 22:12:22 +01:00
d0909d7810 added diskio.loadlib() convenience function to load library blobs 2025-03-24 22:35:34 +01:00
1641999d20 upgrade to kotlin 2.1.20 2025-03-22 23:06:44 +01:00
e16452037c no more self modifying code for loops over non-const word range 2025-03-22 22:51:30 +01:00
344d79684a get rid of more modifying code in forloops (byte ranges with step 1/-1)
fix invalid stack corrupting loops
2025-03-22 20:45:28 +01:00
573a1d9b7b refactor for loop over byte ranges 2025-03-22 16:07:46 +01:00
25ab57580c for loop over string no longer requires self-modifying code 2025-03-22 13:38:49 +01:00
a332e0e3d1 remove self-modifying code from @(ptr)++ and @(ptr)--
romable errors for string and array variables turned into read-only warnings

remove self-modifying code from @(ptr)++ and @(ptr)--
2025-03-21 23:54:19 +01:00
376f1cb139 remove self-modifying code from rol(ptr) and ror(ptr) 2025-03-21 23:16:40 +01:00
90f80558d7 add more 'modified' comments to self-modifying code lines 2025-03-21 17:59:49 +01:00
e281994898 intellij syntax def file works for all jetbrains ides 2025-03-21 01:32:24 +01:00
29fac122e1 fix: ignore EOF when looking for error status after reading first byte of newly-opened file (#166)
* fix: ignore EOF when looking for error status after reading first byte of newly-opened file

* fix: replace hard-coded $40 and $BF with STATUS_EOF and ~STATUS_EOF
2025-03-19 23:00:55 +01:00
1dc412eb90 simplify 2025-03-18 23:50:51 +01:00
3770a4fe0c easier datatype notation by just using the type objects directly 2025-03-18 23:33:04 +01:00
79cda544c8 allow integer range as when choice value 2025-03-17 22:26:27 +01:00
f04b97d890 support symlinks in paths (triggered by symlinked tmp on mac os) 2025-03-15 14:33:33 +01:00
3e9b4ccc45 getting rid of needless absolute pathings 2025-03-14 20:39:18 +01:00
2c3d838dd8 get rid of deprecated usage of thread id to create temporary test output file names 2025-03-14 01:23:01 +01:00
7668a3c660 use kotest tempdir instead of hardcoded output directory 2025-03-14 01:18:33 +01:00
5dd45b714a psg module: the envelope handler no longer writes to vera PSG voices that haven't been enabled 2025-03-12 23:07:13 +01:00
8b08895d0f also add strings.ncompare() to virtual lib 2025-03-09 14:33:31 +01:00
8f8d99e3ed updated symboldumps 2025-03-07 23:58:06 +01:00
23474360ec fix ncompare calls 2025-03-07 23:56:13 +01:00
81c255c450 Add strings.ncompare (and backing strncmp_mem) to compare up to n characters of a string. (#164)
* Add strings.ncompare (and backing strncmp_mem) to compare up to n characters of a string.

* Document strings.ncompare.
2025-03-07 23:53:56 +01:00
ef23d52ed7 better error if module name clash occurs caused by case-insensitive names 2025-03-07 21:35:35 +01:00
220ab773aa fix asmgen error when assigning address of split word array without explicit adressof syntax 2025-03-06 23:20:42 +01:00
e3e5bff7bb add missing sprites benchmark module 2025-03-06 19:39:55 +01:00
7b9a841b2a fix C64 graphics.plot() it was broken since 10.5 2025-03-06 00:42:17 +01:00
40423911ef added footgun warning when calling labels as subroutine 2025-03-04 22:14:21 +01:00
582a70b046 fix calling label as subroutine (JSR label) 2025-03-04 21:53:53 +01:00
5b63590ebf fix symbol prefixing bug triggered by certain usage of %option no_symbol_prefixing 2025-03-03 22:26:19 +01:00
2b6510dc19 improve error message for non-numeric when choice values 2025-03-03 19:48:45 +01:00
9d49589d73 fix codegen for @(ptr-offset)=value writing bogus values 2025-03-02 23:40:14 +01:00
125b66c929 fix crash in asmgen for boolean comparison with false, when not using optimizations 2025-03-01 23:57:55 +01:00
5255f1c052 print offending source position in more of the TODO errors to help diagnosing 2025-03-01 23:16:34 +01:00
a6ba05d60c compile time evaluation of constant rangecheck 2025-03-01 13:45:11 +01:00
41e963b04b memory() name argument should be string literal, nice error message 2025-03-01 12:38:42 +01:00
6ff75bef29 fix verafx register address typo VERA_FX_POLY_FILL_H 2025-02-27 21:59:04 +01:00
72c16d0d32 avoid doing useless jsr for reboot/poweroff calls 2025-02-26 20:42:38 +01:00
94653e5c8c possible workaround for SMC issue that could make sys.reset_system() and sys.poweroff_system() not work properly 2025-02-24 23:07:51 +01:00
3e2b2a698d Separate simple Ast and Symboltable from codeCore into new simpleAst module. VirtualMachine and Intermediate do not need them, just codeCore. 2025-02-24 22:06:52 +01:00
ae04f5aee8 %option romable now disables floating point support
because if it's your code that is running in the rom bank you can't have the floating point routines available there
2025-02-23 10:21:55 +01:00
5c56267662 Also romable warning for inline variables. Added TODO: Romable in library files where applicable 2025-02-21 21:58:31 +01:00
e55ce5504e added %option romable to enable romable mode, but only generate a bunch of warnings for problematic codegeneration atm 2025-02-20 23:40:44 +01:00
fb1e89d9ef update docs about call convention for multi-value results (first is in A or AY, then R15...R0)
added sprites+coroutines+defer part to benchmark program
2025-02-19 22:10:12 +01:00
bc550a4549 fix optimized multi-value call result assignment 2025-02-19 20:19:20 +01:00
ebdea9cf76 optimized call convention for multi-value return and assign on regular asmsubs (6502) 2025-02-19 20:19:20 +01:00
09ec508f82 avoid unnecessary word extension when assigning a register byte to a byte target 2025-02-19 20:19:20 +01:00
d06e9ea7f6 allow comparisons against constant values with different type 2025-02-19 20:19:20 +01:00
a36bdc54fd Add Notepad++ syntax highlighting file for dark mode (#161) 2025-02-19 18:33:31 +01:00
0814ea9711 symboldumps 2025-02-13 13:10:07 +01:00
daefe839d8 version 11.1 2025-02-13 12:31:12 +01:00
e6088dd315 optimize byte comparisons against small word constants 2025-02-13 12:02:11 +01:00
fc03d6f332 changed -sourcelines option to -nosourcelines (default is now to include them) 2025-02-12 21:27:46 +01:00
2aeb7a838e finalize extracting neo and atari compiler targets into configuration files instead 2025-02-12 14:01:04 +01:00
99ff5dd078 extracting neo and atari compiler targets into configuration files instead 2025-02-12 13:58:34 +01:00
49982b49b6 extracting neo and atari compiler targets into configuration files instead 2025-02-12 13:58:34 +01:00
fd39c22616 Update FUNDING.yml
added paypal sponsor link
2025-02-12 13:48:49 +01:00
9e79722a7f fix rangeexpression value type casting 2025-02-11 22:23:07 +01:00
17334a1c58 fix 6502 codegen error for mixed case register params and normal params 2025-02-10 22:06:11 +01:00
c7f0ff11ac fix crash when initializing string variable with a non-string value 2025-02-10 02:54:53 +01:00
cd2cc89e6a fix type errors in Range containment check 2025-02-10 02:27:34 +01:00
069143092d fix Golden ram issues 2025-02-09 15:57:14 +01:00
efd41260f2 added %jmptable 2025-02-09 15:02:59 +01:00
8d2410622c make 64tass less strict about implied register addressing modes on instructions like "asl" 2025-02-05 22:33:50 +01:00
60554389b3 for fileselector library save/restore the prog8 temporary ZP locations 2025-02-04 00:17:55 +01:00
a940dc7d43 improve dumpvars output for zeropage variables 2025-02-03 22:36:58 +01:00
06ca68a625 add drivenumber to fileselector config 2025-02-02 23:09:27 +01:00
5b58e5b158 fix unit tests 2025-02-02 21:52:33 +01:00
74dd8fe80b %output library header generation depends on compiler target
fileselector example tweaks
2025-02-02 20:51:45 +01:00
75ddcda5f3 simplify DirectiveArg 2025-02-02 04:35:20 +01:00
216825b98a cx16: made fileselector example into a loadable library 2025-02-02 04:13:03 +01:00
a96defab86 diskio: added several diskio routines to list only the files or dir entries on the disk
uses CBM DOS filtering $:*=c and $:*=p
2025-02-02 02:44:23 +01:00
0864b0a1b7 cx16: added several diskio routines to list only the files or dir entries on the disk
uses CBM DOS filtering $:*=d and $:*=p
2025-02-01 22:41:34 +01:00
8b158d9240 fix 64tass ascii encoding option for atari and neo6502 2025-02-01 21:47:01 +01:00
f335251c2b added ability to specify additional assembler options in custom target configurations 2025-02-01 16:09:43 +01:00
67bc0b6931 Added txt.cls() to prog8_builtins.vim (#159) 2025-02-01 14:21:40 +01:00
e646dd1ed1 Add an encoding for the C64 OS custom character set. Use c64os: (#158) 2025-01-31 23:41:55 +01:00
2b7947f9b0 fix address check issue when using custom launcher 2025-01-31 23:34:12 +01:00
ec0cfb4b3f doc fix 2025-01-30 23:55:14 +01:00
9cdf53019c some tweaks to the configurable targets 2025-01-30 23:52:44 +01:00
1a04a3eb3a added ability to configure custom ASM launcher code in target configuration file 2025-01-30 22:10:42 +01:00
105d3995e0 some docs about the configurable targets, update kotlin to 2.1.10 2025-01-29 21:14:40 +01:00
8ce3204f93 program start label is back for library outputs 2025-01-28 19:28:34 +01:00
d0f15f1285 missing pic 2025-01-27 23:40:32 +01:00
66d6f67120 usage 2025-01-27 23:36:39 +01:00
a106c88054 unit test for %output library, and docs. 2025-01-27 23:26:21 +01:00
ee784e1ccc fix indication for when imported modules are library modules or not.
This fixes a bug where syslib and such gets optimized away when it is loaded from an alternative library location using the configurable target library path property setting.
2025-01-26 21:19:29 +01:00
bb75be0b44 library now includes the 2 byte PRG header
fixed some assorted things
2025-01-26 19:48:44 +01:00
2478aea316 add %output library 2025-01-24 23:25:57 +01:00
1e17df5296 optimize word+byte*2 expression to word+byte+byte (more efficient in 6502 codegen) 2025-01-24 21:30:02 +01:00
8583a96519 optimized gnomesorts 2025-01-24 00:40:11 +01:00
d0c184c7de remove needless 0 initializations of multi decl's
fix outdated text
2025-01-23 22:42:58 +01:00
0191acb2b3 fix IR codegen for the RETURN 4,5,6,7
added cx16.EXTAPI_memory_decompress_from_func for cx16
2025-01-22 23:31:51 +01:00
277a1a32b2 fix crash when declaring a string array with wrong variable type str vs str[] 2025-01-21 03:12:12 +01:00
7a13f57ab0 enforce variable init values are only strings or arrays 2025-01-20 01:25:17 +01:00
0c882836d9 support multi-value variable initialization: ubyte a,b,c = multi() 2025-01-18 22:08:31 +01:00
228be5cd04 callgraph no longer forgets some identifier occurrences 2025-01-18 21:18:08 +01:00
08cd2fd6e8 fix check for assigning to a constant, for multi-value assigns. 2025-01-18 20:38:02 +01:00
bc7b086f0f fix a configurable compilation target, add working example 2025-01-17 22:58:51 +01:00
e8f3af6981 adding a configurable compilation target 2025-01-17 00:56:44 +01:00
f9c7c7dab7 fix subroutine calling convention for @Rx parameters: don't pass them via cpu registers 2025-01-14 23:02:17 +01:00
09a17743ad merge IMachineDefinition into ICompilationTarget 2025-01-13 21:45:38 +01:00
4f096a7511 added sprites.getxy()
fix compiler crash on return xxx,yyy  when symbol is not defined
2025-01-10 22:26:38 +01:00
2ab2130000 renamed "intermediate AST" to "simplified AST" 2025-01-10 20:33:44 +01:00
66558f7638 IR support for multi-value returns in normal subroutines, documentation. 2025-01-09 22:39:08 +01:00
a6f9ed07e7 6502 codegen for multi-assigns 2025-01-09 00:38:47 +01:00
7268a8736f working on 6502 codegen for multi-assigns 2025-01-07 22:13:13 +01:00
8f6b5676d7 working on codegen for multi-value returns 2025-01-07 20:35:49 +01:00
ca9422bbe9 be able to parse multiple return values (sub + return) 2025-01-07 01:51:54 +01:00
35d9412559 better error message 2025-01-07 00:53:48 +01:00
f071c07dd9 IR: only put align on lsb array of split word array. tag split word arrays with split. (but this isn't actually used yet) 2025-01-06 02:50:54 +01:00
e5ff3c1ff3 fix compiler crash when using strings in if-expression. Remove harmless info message. 2025-01-06 01:34:42 +01:00
f0e8ff0326 get rid of some empty lines in the generated asm 2025-01-05 19:34:05 +01:00
3b5cda85ff fix register clobber on @() 2025-01-05 16:34:51 +01:00
420793f9e2 mkword() avoids unneeded push/pop for simple arguments 2025-01-05 13:41:40 +01:00
cf1dbaf0d8 no longer clear msb on asmsub argument byte @R0
avoid unneeded push/pop for 2 byte arguments to a functioncall
2025-01-05 13:06:39 +01:00
d187cef6b7 optimize x=min(x,100) and some other simple min() and max() cases 2025-01-05 11:59:00 +01:00
3b3616afda optimized pointer access @(pointer - constantoffset) 2025-01-05 06:31:34 +01:00
0ffebc25d0 tweak pointer access, fix and optimize reading memory from a label as pointer 2025-01-05 05:44:51 +01:00
478e2b4ebd abs() return type changed to ubyte/uword 2025-01-04 23:16:51 +01:00
a56ae7539a optimized codegen for word*128 (word << 7): no longer do 7 shifts 2025-01-04 23:08:21 +01:00
407773bda2 IR: don't lose inline asm chunks, fix carry behavior for multi-shifts 2025-01-04 22:12:54 +01:00
823eaa8918 when using @requirezp in a subroutine that is unused (and is pruned), don't give an error there when using %zeropage dontuse
give warning when a pointer var is declared as @nozp  (very inefficient)
2025-01-04 16:39:09 +01:00
a2be42c5ca txt.get_cursor() now returns the column and row as 2 values, no longer requires 2 pointer arguments 2025-01-04 04:06:27 +01:00
a76b8d66ff cx16: added syslib.get_charset()
updated fileselector
2025-01-04 01:02:43 +01:00
b7f47d354f IR: implemented << and >> for split word arrays 2025-01-03 22:18:02 +01:00
5d33c93af9 IR: implemented rol(), rol2(), ror(), ror2() for split word arrays 2025-01-03 20:01:50 +01:00
4db6859f3f IR: strict register pool type assignment, 1 unique type per register + verification during IR writing 2025-01-03 04:18:16 +01:00
45fe1bb16e reduce error clutter for undefined symbols 2025-01-03 01:55:44 +01:00
b014facbd3 cx16 fileselector improvements 2025-01-03 01:35:20 +01:00
3b4b37f16b optimize codegen for x += array[index] (and others) 2025-01-02 01:11:25 +01:00
68d5983a14 optimize monogfx.plot() to use a *40 lookup table in lores mode. Speeds up a lot of other routines too (line etc) 2025-01-01 20:25:58 +01:00
f2cfcfdf31 cx16: monogfx can fill with stipple mode again.
cx16: charset switching enabled again on program exit.
cx16: fileselector example: can now also show directories
2025-01-01 18:27:25 +01:00
10b9162dc5 improving fileselector 2024-12-30 21:34:03 +01:00
c84cc8f8c9 word arrays for sorting should be @nosplit 2024-12-30 00:00:31 +01:00
78c71bbf0e adding file selector example 2024-12-30 00:00:31 +01:00
37c2c1bf0b support &, &< and &> on array elements from split word arrays, not just on the array as a whole 2024-12-30 00:00:31 +01:00
c8996418da ir: tweak register pool, prepare to register the types there uniquely 2024-12-30 00:00:31 +01:00
76b29aa629 fixed register reuse and types on syscall interface 2024-12-30 00:00:31 +01:00
ee521793f8 ir: enforce single reg type 2024-12-30 00:00:31 +01:00
f42e12bc13 ir: fix more register typing errors 2024-12-30 00:00:31 +01:00
427451a23f ir: remove remaining SL* opcodes 2024-12-30 00:00:31 +01:00
af7930d494 ir: remove SL* opcodes 2024-12-30 00:00:31 +01:00
e2882d37bf ir: remove a bunch of strange in-place assignment operators, and problematic opcodes 2024-12-30 00:00:31 +01:00
942d3ee640 ir: improve register type detection 2024-12-30 00:00:31 +01:00
7b4a82b91a IR: report register usage and types in code blocks 2024-12-30 00:00:31 +01:00
056c0a24d9 better way of doing BIT instructions 2024-12-30 00:00:31 +01:00
827df04b32 IR: BIT instruction added 2024-12-30 00:00:31 +01:00
e174b31344 IR: fix up missing indirect goto codegen 2024-12-30 00:00:31 +01:00
49959af752 IR: support %align in code chunks, and load/store FAC0/FAC1 2024-12-30 00:00:31 +01:00
c86c0912f8 latest symboldumps 2024-12-29 20:07:45 +01:00
268b0c9365 fix memory top boundary address on c64 and c128 2024-12-29 17:01:22 +01:00
099fe280ba improved diskio.diskname() error detection 2024-12-29 10:29:31 +01:00
f786f60e9c cmb.PLOT: fixed order of return registers (Y then X, column then row) - same as argument order 2024-12-29 07:42:13 +01:00
f40e1eb1f2 fixed clipping sample 2024-12-29 00:56:12 +01:00
8b9da65357 Added supervisor to coroutines.run(). update symboldumps for 11.0.1. 2024-12-28 04:44:45 +01:00
2cbbe0d48a remove syscall from docs, it doesn't exist anymore 2024-12-27 22:35:56 +01:00
b6e1fb3ba8 emphasize that str[] is also split by default 2024-12-27 17:00:39 +01:00
bdccffbb8e stricter type checking in multivalue assigns, avoids possible invalid output due to missing type cast 2024-12-26 22:20:08 +01:00
5a85474712 pet32: fixed txt.plot() mixing up row and column 2024-12-26 19:37:39 +01:00
f50899c6fa coroutines: make yield() return a configured uword so that a task subroutine can get reused for multiple different things 2024-12-26 18:55:32 +01:00
4daa909f32 fix path normalization problems,
allow ~ in srcdirs compiler flag
2024-12-26 17:42:20 +01:00
4555edf369 update zsmkit to fix zsm_clearisr routine 2024-12-26 13:06:41 +01:00
529ea5bf58 added coroutines library and multitasking example. Added sys.push_returnaddress(). 2024-12-26 00:57:39 +01:00
fe011de934 fix the missing cases in certain expressions that need the address of a split word array 2024-12-25 16:55:07 +01:00
0653d430a7 fix compiler crash related to uword array parameters type checking 2024-12-25 12:12:20 +01:00
a587f6e9a0 make imported module cache case-insensitive
this avoids crashes when using case-insensitive filesystems and mistyping the exact path case
2024-12-25 11:51:24 +01:00
3850e1dbb5 Merge branch 'better-ir' 2024-12-24 21:23:57 +01:00
91cde072e0 added txt.t256c() on the cx16 to turn 256 color tile mode on or off 2024-12-24 12:10:02 +01:00
2ca4aed566 IR: prefix immediate values with '#' for human readability reasons (no technical reason) 2024-12-24 09:35:10 +01:00
5071da6784 retain constants in IR
some IR related cleanups
2024-12-24 00:30:08 +01:00
4c1e2f3110 refactor package nesting of ast exception classes 2024-12-23 18:14:46 +01:00
2727a4dcb3 tweak DataType class and memsizer related to subtypes/elementtypes 2024-12-23 17:28:25 +01:00
126d4c69e6 fix cx16images.py script for new pillow library version 2024-12-23 16:31:15 +01:00
7657edcb7d latest symboldumps 2024-12-22 09:24:45 +01:00
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
c0ae35b3a3 tweaks, bump version 11.0 2024-12-22 06:34:17 +01:00
c3dc74788a added diskio.get_loadaddress()
added compression.decode_tscrunch_inplace()
2024-12-22 03:17:16 +01:00
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
1f49e8fe75 in diskio.f_readline make sure AY result isn't clobbered 2024-12-21 06:25:56 +01:00
d70cfbb661 added sorting module and sortingbench example 2024-12-21 06:18:35 +01:00
5482ac0302 simplify grammar of @tags, also improving their error message 2024-12-21 01:44:58 +01:00
131d5ceb4f avoid re-reading all source files when sourcelines are requested in the asm 2024-12-21 00:06:18 +01:00
512ddd1694 cleanups 2024-12-20 22:59:20 +01:00
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
d586846bc5 use simpler set_screen_mode() 2024-12-19 22:17:09 +01:00
ef4efcb112 cleanup 2024-12-19 21:06:51 +01:00
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
3804fba0f1 moved jdk version config back to main gradle build file, version consistency 2024-12-19 13:39:27 +01:00
f93b7e3303 changed IR JUMPI instruction to support more indirect jump cases 2024-12-19 04:29:16 +01:00
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
7c79cdbd2f fix symbol prefixing on goto with expression
added coroutines example
2024-12-17 16:16:38 +01:00
8ea032ed66 fix compiler crash on certain split array values 2024-12-17 12:31:47 +01:00
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
02f3f5d0f5 @split is back to force splitting of word arrays 2024-12-16 14:51:32 +01:00
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
8644a4ae91 more split array fixes 2024-12-15 22:54:06 +01:00
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
80d88b3c61 fix many split array issues 2024-12-15 17:08:07 +01:00
d2827a7431 fix ast printer for arrays containing label addresses 2024-12-15 13:53:24 +01:00
28c721fa7d add a split-array version for word containment check 2024-12-15 13:45:47 +01:00
8f799567cf make word arrays split by default (w.i.p.) 2024-12-15 08:12:34 +01:00
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
cc59069876 allow goto to take any expression, not only an integer or an identifier (part 1) 2024-12-14 01:01:32 +01:00
697d54e10a fix asmgen for call $3000 2024-12-13 22:33:26 +01:00
1679ca79b4 can now use boolean params mapped to Rx register 2024-12-13 20:47:23 +01:00
124ec77b58 update zsmkit to version 2.4, including the new on_deck routines 2024-12-13 20:15:36 +01:00
3675d7961b boolean variables can now also be memory-mapped (including boolean arrays) 2024-12-11 18:25:27 +01:00
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
b7afda781a Optimize 6502 bitwise operations on word values where only the msb or lsb is touched 2024-12-10 21:42:42 +01:00
535ec13072 improved codegen for testing for single bits: x & mask == mask 2024-12-09 04:05:00 +01:00
26d0a174db optimize codegen for while loops with empty body 2024-12-09 03:21:20 +01:00
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
2e303041c1 fix crash when using undefined variable in for loop 2024-12-06 21:50:22 +01:00
96bed8f57f tweaks 2024-12-06 00:37:16 +01:00
86d4a4309f cleanups 2024-12-05 21:56:00 +01:00
1a1ab0dac6 changed the data type system to composite types 2024-12-05 21:48:51 +01:00
ba8c3d14f7 diskio docs, remove super harmless warning message 2024-12-05 20:51:44 +01:00
617ea15c3a fix failing optimization of 'not' in if statements 2024-12-04 19:03:24 +01:00
ef192a5778 easier notation for builtin function signatures by using varargs 2024-12-04 01:57:02 +01:00
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
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
1ebfff7c7b add -plaintext and -ignorefootguns options 2024-12-03 19:12:30 +01:00
8341f9c066 diskio.status(): remove unreliable device not present error detection 2024-12-02 23:33:33 +01:00
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
8fa14a10e2 Optimize diskio.f_read for size=1, also improve ST check 2024-12-02 21:25:38 +01:00
55dbd095ed fix IR codegen missing a CMPI after if not condition
fix IR codegen for containmentcheck
2024-12-02 03:06:06 +01:00
31ad8bdd8d remove bankof(), documented msw() and lsw() 2024-12-01 21:24:26 +01:00
181f3e9eb1 remove the unary/prefix operators ^ and << again 2024-12-01 20:50:33 +01:00
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
58f696d00a document the @R0 - @R15 register support for normal subroutine parameters 2024-11-30 20:46:31 +01:00
f603c543d3 restructure documentation to get rid of redundant syntax chapter 2024-11-30 20:26:06 +01:00
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
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
310e8f15cd update to latest zsmkit lib v2.2 2024-11-29 18:37:06 +01:00
da03941582 fix build 2024-11-29 01:01:59 +01:00
dcbb36a3bd update gradle wrapper version 2024-11-29 00:54:21 +01:00
53558f5c1d add zmskit example for zsmkit v2 2024-11-29 00:04:57 +01:00
189399d5f8 update to kotlin 2.1.0 2024-11-28 03:49:07 +01:00
5406a992f5 improved buffers library, added to docs 2024-11-28 03:30:32 +01:00
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
2eed75f602 call convention for @Rx parameters, also use cpu registers if possible, like normal parameters 2024-11-25 22:22:24 +01:00
d58f9f56c4 tests for register args for normal subs
some warnings demoted into infos
2024-11-24 19:21:45 +01:00
2e35f3c3a3 code check cleanups 2024-11-24 16:14:22 +01:00
5c6bd9c091 register params support for normal subroutines 2024-11-24 15:56:54 +01:00
857d2eefca added floats.interpolate(), math.interpolate(), and LERP example 2024-11-24 10:00:21 +01:00
90f1e7fd6a ast printing fixes, added alias to syntax files 2024-11-24 07:28:33 +01:00
18e37accf9 improve detection of register re-use in parameters 2024-11-24 05:27:43 +01:00
cc53d698bf added msw() and lsw() builtin functions (experimental) 2024-11-24 03:53:37 +01:00
cb86206698 added unary ^ and << operators (experimental) (gets bank and address of a long integer) 2024-11-24 03:07:18 +01:00
d77b1944fb rename bnk() to bankof() 2024-11-24 00:53:09 +01:00
a58cb43c4a fixed weird error messages when attempting to create variable with type long 2024-11-23 21:35:57 +01:00
88574c87c4 convert vtui and zsmkit to new extsub address expression capability 2024-11-23 21:21:52 +01:00
3a7a7091c0 update some docs 2024-11-23 21:01:18 +01:00
906b137a7c renamed 'string' module to 'strings' for consistency 2024-11-23 15:51:38 +01:00
42e2c5f605 fix some deprecated code in tests
silence redundant error about unused txt block
2024-11-23 15:48:18 +01:00
cc13a51493 fix import order problem related to %option merge 2024-11-23 12:15:15 +01:00
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
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
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
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
844c97930f fix Antlr grammar build and convert final build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
5c09dc10ae convert build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
9fd9e9ab5f change block sort order so that blocks with address are now sorted last 2024-11-20 23:23:26 +01:00
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
ae0cadb383 added bnk() builtin function 2024-11-20 23:23:21 +01:00
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
a874aec6a1 implementing const long 2024-11-20 23:22:56 +01:00
ea1daa97d3 remove the 'addmissingrts' compiler option 2024-11-20 23:22:56 +01:00
fb0d9b46b0 remove 'romsub' as a recognised alternative for 'extsub' 2024-11-20 23:22:56 +01:00
9da70bdf05 simplify ReturnConvention a little 2024-11-20 23:22:56 +01:00
d640cfbe13 removed BuiltinFunctionCallStatement redundant ast node type 2024-11-20 23:22:56 +01:00
51a05ec4b7 removed BuiltinFunctionCall redundant ast node type 2024-11-20 23:22:56 +01:00
1f5706bbeb version 10.5.1 2024-11-20 22:54:26 +01:00
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
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
d78ce77536 improve vm error message when referencing a block name 2024-11-19 20:57:58 +01:00
b4fb43bc80 fix the if not check in ir codegen 2024-11-18 22:33:47 +01:00
e0e01f794e fix dt compiler crash 2024-11-17 17:39:36 +01:00
08865dbb4e todo 2024-11-16 02:10:57 +01:00
b9ad7e0e55 forgot to mention floats 2024-11-15 23:37:08 +01:00
07158a6f1a improve manual about subroutine call convention 2024-11-15 22:59:47 +01:00
957c42bc1d tweak 2024-11-15 02:52:21 +01:00
f784da2da6 fix asm optimization regression caused by wrong label prefix comparison 2024-11-14 21:24:46 +01:00
c080fbe59a target machine config tweak and fix possible compiler crash on wrong type name 2024-11-13 21:04:46 +01:00
d70b8303b1 added sprites.reset() to remove sprites from the screen 2024-11-13 20:26:04 +01:00
1d38c3582a progstart() added to complement progend() 2024-11-13 19:29:50 +01:00
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
3b4a5e27f7 renamed gfx_hires4 module to just gfx_hires
to be consistent with gfx_lores
2024-11-12 17:48:35 +01:00
648d9fc269 todo 2024-11-12 00:57:10 +01:00
54fccec7d7 now also support using defer inside if statements 2024-11-12 00:11:19 +01:00
4f9693055e fix compiler crash when extsub has both FAC1 and FAC2 float parameters 2024-11-11 20:48:25 +01:00
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
bf98ceca2c make repeat support 65536 iterations 2024-11-11 01:58:27 +01:00
573cecb087 make memtop adjust automatically when you use %address larger than the default memtop setting. 2024-11-10 23:44:10 +01:00
1b528491c2 make %memtop exclusive i.e. the first address NOT to use (like kernal MEMTOP) 2024-11-10 23:35:25 +01:00
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
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
84f5ffa426 fix generated labels prefix and filtering in the vice symbol dump file 2024-11-10 15:34:35 +01:00
25d2b42283 textelite now with sysinit, so it runs on the c128 as well (needs banking) 2024-11-09 16:03:16 +01:00
300d1a871c c128 banks out basic, added banks() and getbanks() 2024-11-09 15:44:08 +01:00
2fcb83a39f version 10.5 2024-11-09 14:04:04 +01:00
3ba1d00a7c add unit test for @dirty variables 2024-11-09 13:31:54 +01:00
64164c1c72 changed @initonce to @dirty and meaning is now: not initialized at all. 2024-11-08 22:05:31 +01:00
3ee6058524 todo 2024-11-08 19:57:38 +01:00
93a0a41e73 Merge branch 'initonce-var-tag'
# Conflicts:
#	examples/test.p8
2024-11-08 19:32:30 +01:00
e7ab7b6d7a neo skeletons added in docs 2024-11-08 19:19:11 +01:00
7d4dc3c063 update 2024-11-08 19:04:51 +01:00
a50400b7d1 initial neo6502 target 2024-11-08 19:04:49 +01:00
f89f1a84d0 @initonce variable tag to skip variable reinitialization 2024-11-08 19:03:48 +01:00
688dce6145 floats: added AYINT2 as a safe wrapper for AYINT. Internal float to word cast now also uses that. 2024-11-08 18:52:48 +01:00
b88f550c5b todo 2024-11-07 00:48:13 +01:00
9864abd393 romsub keyword is now extsub 2024-11-06 22:14:53 +01:00
c702c4a6df internal rename of romsub to extsub 2024-11-06 21:42:16 +01:00
77e376f6bf romsub @bank now also accepts a variable so the bank can be dynamic 2024-11-06 00:02:36 +01:00
491e5dbcfb move the program startup and cleanup machinery to the front of the program to keep it in system ram 2024-11-05 22:12:25 +01:00
a5c7393561 tweaking program startup and cleanup stuff 2024-11-05 21:12:27 +01:00
7fd3e9bb7d also provide a X16-style JSRFAR implementation for the C64. Enable callfar() and callfar2() on the C64 and C128. 2024-11-05 19:26:58 +01:00
459e9f8f3b jsrfar stuff 2024-11-05 01:06:06 +01:00
5b1143bcb3 C64: add support for calling romsub with bank ('jsrfar') 2024-11-04 23:26:21 +01:00
fddd390d31 on the C64, if not using floats, disable basic ROM in startup to gain another 8Kb of RAM
MEMTOP is adjusted to $d000. This gives us 50 Kb of contiguous program RAM space. ($0801-$CFFF)
2024-11-04 22:11:44 +01:00
e514eeba17 added c64.banks() and c64.getbanks() and c64 banking example 2024-11-04 20:14:30 +01:00
c11a52b278 added cx16 banking example 2024-11-03 21:52:04 +01:00
85e87dfe2e consolidate @rombank and @rambank into just @bank 2024-11-03 21:15:11 +01:00
cb47e2c149 documented the romsub bank additions 2024-11-03 20:39:44 +01:00
0fc9aa6b2d cx16: romsubs of the audio routines now have the rom bank tag.
cx16: removed 'audio' module again, no longer needed to have these stubs
2024-11-03 18:35:10 +01:00
155896c4c7 added @rombank and @rambank bank number tags on romsubs
on cx16 and c128 targets the compiler then automatically inserts a CALLFAR instead of a regular JSR to automatically do the bank switching.
2024-11-03 18:19:31 +01:00
178e60bba0 fix ast source gen for romsub 2024-11-03 15:04:53 +01:00
9f84aa5fb2 fix double %option merge problem where it deleted all of the blocks 2024-11-03 13:36:14 +01:00
66fc109ce5 correct program name in help 2024-11-02 22:16:57 +01:00
a231872821 tip for using aliases for the virtual registers r0-r15 2024-11-02 22:09:20 +01:00
7cfb33a448 tweak & fix if expression with word condition 2024-11-02 22:01:57 +01:00
3b798097b9 added memtop to machine definition and asm source code check
added %memtop directive
2024-11-02 00:59:07 +01:00
6fb05bdefc replaced deprecated cx16 ZSOUND example by new ZSMKIT examples 2024-11-01 23:17:23 +01:00
64ea72ed4d tweak plot 2024-11-01 21:56:27 +01:00
89425088ce taking address of a split word array is no longer a fatal error but a warning and the array is turned back into a normal word array. 2024-11-01 20:18:31 +01:00
925b9d845d fix split array possible compiler loop (due to wrong datatype replacement) 2024-11-01 19:18:03 +01:00
ad074076c2 remove last references to gfx2 module 2024-11-01 18:41:36 +01:00
a2194c43a6 fix benchmark 2024-11-01 03:50:13 +01:00
4b23b1dc86 don't always import math automatically anymore 2024-11-01 03:39:52 +01:00
9005c7994a added Linear Interpolation (LERP) functions: math.lerp(), floats.lerp(), floats.lerp_fast() 2024-11-01 02:05:48 +01:00
4a47e15b1c fix IR if expression sometimes lacking a cmpi after calculation of the condition value
VM/IR: add a returni immediate value return instruction to replace certain returnr's
2024-11-01 01:04:16 +01:00
09cbdf410a added diskio.exists(), made f_close_w() idempotent like f_close() already was 2024-10-31 21:25:22 +01:00
df6a43c7f0 gfx_lores now has drawmode_eor() (used by Paint, for example) 2024-10-31 01:28:29 +01:00
4ce130dc8b split up cx16.gfx2 module into gfx_lores and gfx_hires4 modules 2024-10-30 22:21:07 +01:00
94d76aa82c cx16.vaddr(), vaddr_clone(), vaddr_autoincr(), vaddr_autodecr() now all reset vera's ADDRSEL back to 0 even if the configured port was 1 2024-10-30 21:40:58 +01:00
73609636c5 gfx_lores.set_screen_mode() is now gfx_lores.graphics_mode()
adding all missing routines from gfx2 to gfx_lores
2024-10-30 21:39:37 +01:00
66b06d6c40 added gfx2.safe_vertical_line, gfx2.safe_rect, gfx2.safe_fillrect for completeness 2024-10-30 19:03:40 +01:00
eeeb8d81f4 merge now also allows monkeypatching if signature is 100% identical 2024-10-30 01:15:56 +01:00
6f727aff88 fix beanshell compile with jdk11 2024-10-29 23:42:37 +01:00
518e5a30c2 slight parser rule tweak 2024-10-29 23:18:17 +01:00
bbba4b3d60 new block merge semantics and implementation 2024-10-29 22:57:54 +01:00
967adb9a87 Merge branch 'beanshell' 2024-10-29 20:55:09 +01:00
040a6c62de added a beanshell interpreter experiment 2024-10-29 20:52:41 +01:00
483d193ced vm: implemented reading/writing files in diskio 2024-10-29 02:34:53 +01:00
62458216c9 first skeleton of LSP language server 2024-10-28 21:42:20 +01:00
76b05cb5fd fix chained aliasing 2024-10-28 18:35:23 +01:00
570b574b93 added sys.memcmp 2024-10-28 00:41:26 +01:00
a82f211f9a added alias statement 2024-10-28 00:36:10 +01:00
504c80cddf fix parser rule for identifiers (void is a keyword, not an identifier) 2024-10-27 15:57:27 +01:00
4b4af9b527 no longer silently add RTS to asmsubs that don't have one 2024-10-27 13:49:00 +01:00
28b383f888 docs and syntax for @alignxxx and %align 2024-10-27 00:47:52 +02:00
40ce7725a1 cleanup c64 sprite examples 2024-10-26 21:36:11 +02:00
1f2d46628e remove %option align_xxx (block level alignment, as we now have better alternatives) 2024-10-26 21:18:34 +02:00
c9535049c8 %align directive and @align64 2024-10-26 20:58:35 +02:00
9317cf8a35 sorting aligned vars to shrink prg size 2024-10-26 18:33:51 +02:00
1cd754f05d adding @alignword/page on individual variables 2024-10-26 17:00:38 +02:00
97b8cb748d more ifexpression codegen tweaks 2024-10-25 22:52:26 +02:00
84d9040b57 make BIT test also work on signed byte variables. Fixed an address-of optimization error. 2024-10-23 22:34:18 +02:00
fdd18c615c more ifexpresssion codegen tweaks 2024-10-23 21:04:55 +02:00
c14f6cfc2b more optimal if expression code 2024-10-22 23:49:24 +02:00
326eab3dd1 unit test for defer, describe defer and if expression in docs 2024-10-22 22:19:49 +02:00
6da1f7eb4c don't remove essential subroutines even though they seem unused 2024-10-22 21:17:02 +02:00
1e82483152 ast printer correctly prints ifexpression 2024-10-22 21:14:55 +02:00
6e2fd41a8b ast printer correctly prints unroll and continue 2024-10-22 21:14:26 +02:00
9927af1095 about var inits 2024-10-22 01:08:42 +02:00
7585b6ef6f fix issues with calling the defer handler 2024-10-21 19:49:38 +02:00
a6159702da defers are now only registered/called when flow of control actually reached the defer statement
a defer statement sets its corresponding bit in a bitmask that is shifted in the defer handler routine to see what defer blocks to call.
2024-10-21 00:55:51 +02:00
0247fb0d84 some ast2 var tweaks 2024-10-21 00:20:54 +02:00
6de760885f fix defer push/pop typecasting issues 2024-10-19 21:45:49 +02:00
9851d14fb9 added if expression: ubyte a = if b>0 44 else 55
it doesn't generate the best code yet, like regular ifs do.
2024-10-19 15:34:04 +02:00
d5fc69d3e4 fix instruction index error in optimizer 2024-10-19 14:45:53 +02:00
a40d120f2a more defer sanity checks 2024-10-18 22:32:49 +02:00
fcdd9414d9 fix defer interfering with return value, fix prefix expression error when operand is functioncall that doesn't return a value. 2024-10-18 21:43:09 +02:00
272a1001a8 fix bad optimization of floats.pop/push call 2024-10-18 21:04:18 +02:00
2a52241f1c defer is now done *after* calculating a return value 2024-10-18 20:56:27 +02:00
d8f1822c12 fixes 2024-10-18 20:32:46 +02:00
ce7d094adb Zig-like "defer" to clean up stuff when leaving the scope of the current routine. 2024-10-18 01:30:20 +02:00
a0cf1889a3 omit more redundant 0-initializations ("stz's") 2024-10-17 22:51:39 +02:00
38ef394e15 IR codegen: global vars with numeric initialization value are now also put into the VARIABLESWITHINIT section rather than requiring explicit code instructions to initialize them in INITGLOBALS.
Note that something similar, such as putting those variables inline in the program initialized with their value and all, cannot be done for the 6502 codegen: the program needs a mechanism to reset ALL variables when it runs a second time.
2024-10-16 22:15:51 +02:00
abbf7c7cb0 compiler name change: prog8c (was p8compile)
fat jar file also changed:  prog8c-X.Y-all.jar    (was: prog8compiler-X.Y-all.jar)
2024-10-16 18:36:19 +02:00
ca5f7ae32f global (block-level) variables that get initialized with an array index expression now get a constant value as well if possible. This reduces the number of instructions in the init globals code block 2024-10-16 02:14:19 +02:00
cbc4b75e50 IR now contains "bool" as a type instead of already erasing it into "ubyte". (boolean literals still are simply just 1 and 0 values) 2024-10-16 01:03:35 +02:00
65ddcf91d0 remove unused syscalls 2024-10-15 18:24:55 +02:00
5280e1b449 err msgs 2024-10-13 21:33:13 +02:00
b6ffb81909 Merge branch 'next-version' 2024-10-13 21:20:10 +02:00
e9edffa9f0 remove support for array-to-array assignments (other than initialization of variable declaration)
Just use an explicit sys.memcopy(src, dest, sizeof(dest))  or assign array members individually.
2024-10-13 20:02:43 +02:00
0dd1c17ff4 avoid possible crash 2024-10-13 17:51:14 +02:00
aef211e5f3 stricter array literal element type handling (number,bool,address-of).
More consistent implicit address-of handling if array literals contain by-ref identifiers (such as subroutine names)
2024-10-13 17:46:41 +02:00
66829203d8 New [x]*42 syntax to create array literals with repeated values (like "abc"*10 already exists for strings)
Should be used in place of array initializer expressions that contain only a single numeric value to initialize the whole array with. That isn't supported anymore.
2024-10-13 05:16:08 +02:00
7a0eaf3148 Remove array initialization by single value.
New compiler and kotlin version.
2024-10-13 04:31:56 +02:00
fa5479ee5f fix ast printing of arrays with duplicate elements 2024-10-13 04:30:46 +02:00
03412cacba added examples/cx16/balloonflight.p8 2024-10-13 00:51:07 +02:00
01a38a0b11 Merge branch 'monogfx_fill_optimization' 2024-10-12 17:29:38 +02:00
f43c14bd78 doc 2024-10-12 17:29:28 +02:00
fb23452383 optimize monogfx.fill() 2024-10-12 17:18:00 +02:00
ab7dde1450 todo 2024-10-12 13:04:19 +02:00
8d9bc2f5ff fixing all sorts of things about assigning arrays to arrays 2024-10-12 12:33:46 +02:00
7651ccc84e fix a type error 2024-10-11 00:50:05 +02:00
1a6b95b388 house cleaning 2024-10-10 20:46:18 +02:00
78ec1e7512 version 2024-10-09 22:21:04 +02:00
7e38d26c33 added several color fade functions to the palette module (cx16) 2024-10-09 21:48:04 +02:00
ed09dd4e9e improve automatic type conversions for return values, fixes #155 2024-10-09 20:04:05 +02:00
5731b79554 don't allow problematic string and array assignments anymore, improve error messages.
In certain cases you will need to use string.copy() explicitly to overwrite strings with new strings.
2024-10-09 00:51:05 +02:00
eaa22a9d13 added callfar2() builtin function that allows to set A,X,Y and Carry arguments. 2024-10-08 21:36:04 +02:00
b2bdfe8482 fix ir rndseed() 2024-10-08 20:40:00 +02:00
fea531be9a add sys.sizeof_bool, _ubyte, _uword constants 2024-10-07 20:45:13 +02:00
7c69d38588 scan all asmsubs to see if another subroutine is referenced. Fixes #153 2024-10-07 20:39:49 +02:00
a088ee56b0 function inlining can no longer get into an infinite loop. Fixes #154 2024-10-07 19:58:04 +02:00
ae669af904 add sys.sizeof_byte, _word, _float constants
because Antlr doesn't allow the grammar to contain a sizeof(typename) rule to override the sizeof(identifier) rule
2024-10-07 19:17:37 +02:00
d1ddf05e38 check that block address leaves room for program startup logic 2024-10-03 22:30:06 +02:00
51279a98b3 attempt to fix forloop range datatype issues 2024-10-03 21:12:31 +02:00
bf33a4f82d small refactor to prepare for better range dt adjustment 2024-10-02 23:28:33 +02:00
fff0d741c3 improved parsing of "not in" operator, and [] array signature (allow space) 2024-10-02 19:06:20 +02:00
e83d0ee820 fix crash in msb() when assigning to word again. Fix wrong register in lsb() and msb() in certain situations. 2024-10-02 02:40:16 +02:00
09f3eecf56 changed cx16/rotating-stars example to starszoom instead. 2024-10-02 01:36:54 +02:00
2bd4326ff6 added cx16/rotating-stars example 2024-10-01 23:43:50 +02:00
c13168b60c various improvements:
fix verafx.available().
added gfx_lores.plot().
faster gfx_lores.clear_screen().
added a new Sublime Text 4 syntax highlighting file.
2024-10-01 22:18:03 +02:00
ea3871d0c4 comment about builtin function call ast node type 2024-10-01 02:14:31 +02:00
70a2b11271 New example program: draw a fractal tree (#152)
* demo of prog8 recursion to draw a fractal tree

* feat: comments

* fix: comment formatting

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

* fix: remove duplicate definition

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

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

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

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

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

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

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

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

* fix: wording

* fix: wording
2024-03-26 22:00:55 +01:00
993be6394e unit tests multi-assigns 2024-03-25 23:20:03 +01:00
9a27505315 6502 codegen for multi-assigns 2024-03-25 22:17:31 +01:00
2e37f5dee3 IR: support for multi-returnvalue function calls (asmsubs)
note: the VM can't execute these though as it has no CPU hardware registers
2024-03-23 00:30:17 +01:00
03e486c082 multi assign 2024-03-22 21:51:25 +01:00
edc83305a4 allow multiple targets in AssignTarget 2024-03-22 21:51:08 +01:00
66e7c51064 IR: fix some things related to asmsubs 2024-03-22 21:49:01 +01:00
60244aaf16 64tass version... 2024-03-21 21:40:18 +01:00
443391c700 another way to hash? 2024-03-21 21:30:34 +01:00
47dbafacd4 correct version 2024-03-21 20:15:54 +01:00
5b6811d073 not separate 2024-03-21 20:07:44 +01:00
7516116bb7 last attempt for hash build step 2024-03-21 20:04:07 +01:00
e6014ea4dd version 10.3.1 2024-03-21 19:32:16 +01:00
362abfe284 ci 2024-03-20 22:46:18 +01:00
ad4880997a no operand swap on logical expressions with shortcircuit evaluation (and,or are no longer associative!) 2024-03-20 22:34:48 +01:00
592becc126 allow %breakpoint also in if/else blocks and other anonymous scopes 2024-03-19 23:31:21 +01:00
c38765301e gfx2 screenmode tweak 2024-03-19 01:21:56 +01:00
5f27426f59 only on release event 2024-03-17 23:35:53 +01:00
d924f8bff8 another attempt to get the hashes working 2024-03-17 23:31:04 +01:00
d14c61b160 added string.findstr(). string.find() returns 255 if not found.
also fix string assignment bug for sub args.
2024-03-17 23:18:33 +01:00
fe2b67998c vm: fix load_raw, fix rng bug in textelite (carry flag shifting...) 2024-03-17 16:46:26 +01:00
04df3c9f7f vm: implemented in-place array multiplication better 2024-03-17 13:39:05 +01:00
de3d0b40dc fixed vm problem with branching instructions in global init chunk 2024-03-17 13:22:17 +01:00
4db4a5f1b2 vm: txt.width() and height() now return the actual console terminal width and height if possible. 2024-03-16 22:40:08 +01:00
5a0524ff4d various fixes 2024-03-16 18:48:06 +01:00
5b7801eea1 added crc verifications to diskspeed 2024-03-16 16:26:39 +01:00
fbe231793b optimized and added "streaming" crc32 and crc16 routines to math module. Return value is put in different register now! r14+r15 instead of r0+r1! 2024-03-16 01:07:03 +01:00
6a9269111e some changes in SMC routines for the cx16:
sys.poweroff_system() moved to cx16
sys.set_leds_brightness() moved to cx16 and changed to set_led_brightness, you can only change the activity led brightness.
2024-03-15 23:00:14 +01:00
a94cfd34f5 don't apply absorption law on functioncall operands 2024-03-15 01:04:27 +01:00
28eae5a0fd updated diskspeed example to deal with increased I/O speeds 2024-03-15 00:37:13 +01:00
1818738fc8 fixed potential bug in cx16.kbdbuf_clear() is and it is now cbm.kbdbuf_clear() and is available on all cbm like targets 2024-03-14 22:12:29 +01:00
7e1e7a0780 fix conv.str_ub and conv.str_b for missing tens digits 2024-03-13 23:03:25 +01:00
1fc79ff6dd implement the missing in-place array operators for split word arrays and numeric operand 2024-03-13 21:16:49 +01:00
3535c1acda fix broken boolean != comparison optimization 2024-03-13 20:23:42 +01:00
33c8caac8f get rid of containment expression restriction 2024-03-12 23:39:54 +01:00
51d708bbdd fix monogfx issue 2024-03-12 23:27:15 +01:00
a5a918df84 update docs about boolean type 2024-03-12 18:54:56 +01:00
820541e427 fixed and optimized pointervar indexed in-place operations 2024-03-11 23:27:48 +01:00
e63a8f0c01 fix vm textio prefix type 2024-03-11 22:22:30 +01:00
c11a9b8709 fix callgraph issue when module gets removed by optimizations 2024-03-11 20:34:22 +01:00
80f39e8097 Merge branch 'booleans'
# Conflicts:
#	compiler/res/prog8lib/cx16/monogfx.p8
#	compiler/res/prog8lib/virtual/monogfx.p8
#	compiler/src/prog8/compiler/astprocessing/BoolRemover.kt
#	compiler/test/TestTypecasts.kt
#	docs/source/todo.rst
#	examples/cx16/highresbitmap.p8
#	examples/test.p8
#	httpCompilerService/src/prog8/http/TestHttp.kt
2024-03-11 01:00:48 +01:00
2a8b65e29c test str to uword change in function params 2024-03-10 23:48:58 +01:00
4bdf50145e recognise \t character (TAB) in string literals (note: only valid in iso encoding) 2024-03-10 13:20:09 +01:00
3a9919a377 implemented a couple more diskio routines for the VM target 2024-03-09 17:36:39 +01:00
eef8ae00b8 replace str return type by uword 2024-03-09 15:38:46 +01:00
ed15fac691 improve IR error message 2024-03-09 13:38:25 +01:00
f739e679e4 added sys.exit2 and sys.exit3 to set more result registers at program exit
todo
2024-03-09 03:30:05 +01:00
fc0fae8caf tweak the redundant beq asm optimizer a bit more 2024-03-09 00:05:06 +01:00
f46896fd74 attempt to no longer have BuildVersion.kt in git, blocking easy pulls
(cherry picked from commit bfcf07c1a2)
2024-03-08 23:36:35 +01:00
52649a8e4f conv routines now return the string buffer address. 2024-03-08 02:12:46 +01:00
bdfb01f6a0 VM: implemented a few core routines in diskio (load/save)
textelite can now load and save your progress like it already could in the real version
2024-03-06 23:21:01 +01:00
1137e57393 update VTUI lib 2024-03-06 21:39:30 +01:00
267ea13e8c clearer error msg 2024-03-06 20:10:10 +01:00
04f7b772a3 lib updates, removed unused and obsolete http and dbus modules 2024-03-05 23:42:35 +01:00
42c7569791 doc 2024-03-05 22:58:27 +01:00
6d29b00a80 fix monogfx horizontal line and text draw in invert mode 2024-03-05 22:38:34 +01:00
9f1bd2d7d6 asmoptimizer that removes redundant branches on boolean in A 2024-03-05 20:37:15 +01:00
9826d7c494 optimize certain boolean comparisons more 2024-03-05 03:09:53 +01:00
c6bf57b390 non strict bools should also replace not byte with byte==0 2024-03-04 23:25:34 +01:00
bfcf07c1a2 attempt to no longer have BuildVersion.kt in git, blocking easy pulls 2024-03-04 20:45:59 +01:00
4d7e96d423 add monogfx inverted (eor) draw mode 2024-03-03 23:28:37 +01:00
449461e412 tweak monogfx stipple plot 2024-03-03 21:47:42 +01:00
607275ec66 tweak 2024-03-03 19:52:46 +01:00
e55cde2a81 more nonstrictbool conversions 2024-03-03 19:43:48 +01:00
84afb374e6 nostrictbool array conversions 2024-03-03 17:48:52 +01:00
da1620807f fixed all todos in ifelse gen 2024-03-03 12:10:42 +01:00
f39ef8f565 optimize byte comparison assignment to use rol trick instead of branching 2024-03-03 00:01:14 +01:00
fe8b6e820c getting rid of problematic fallback (infinite recursion) 2024-03-02 23:19:55 +01:00
f29d24e96a fixup split words array comparisons 2024-03-02 23:19:55 +01:00
620ffe54ec asm optimizer: don't remove labels. remove redundant cmp/cpx/cpy instructions. 2024-03-02 23:19:53 +01:00
ceaa4cd07d array issue 2024-03-02 23:19:39 +01:00
af17f903ee fix that if not fcall() wasn't transformed to a conditional branch instruction 2024-03-02 23:19:39 +01:00
c532e28841 fix several remaining bool return values in library routines 2024-03-02 23:19:39 +01:00
dba0846866 optimize word >, word <= 2024-03-02 23:19:39 +01:00
bed629998a fix large code for some compares 2024-03-02 23:19:39 +01:00
bc2ede76bf tweak to byte compares 2024-03-02 23:19:39 +01:00
2a1fec2ed2 fix codegen error for comparisons 2024-03-02 23:19:39 +01:00
004048e5a7 fix IR codegen error for b=float>value 2024-03-02 23:19:39 +01:00
b941d6f1e4 new comparison tests 2024-03-02 23:19:39 +01:00
37b346740b fix 6502 casting uword and float to bool 2024-03-02 23:19:39 +01:00
f5e332daf7 remove redundant IR instructions like SNZ 2024-03-02 23:19:39 +01:00
fe9a9fc5cb new if tests 2024-03-02 23:19:39 +01:00
cc57477b99 IR: support for indirect jump after if 2024-03-02 23:19:39 +01:00
a1574a7187 added txt.print_bool, several fixes 2024-03-02 23:19:39 +01:00
a5110b1f96 improved De Morgan rewrite rules 2024-03-02 23:19:39 +01:00
006713fe13 optimize boolean to ubyte assignment (skip type cast) 2024-03-02 23:19:39 +01:00
7868e672e0 ifelse more 2024-03-02 23:19:39 +01:00
e1a133c2c0 ifelse more 2024-03-02 23:19:39 +01:00
c77cd0da39 ifelse more 2024-03-02 23:19:39 +01:00
577333f2c4 new ifelse codegen 2024-03-02 23:19:39 +01:00
7d8cdcbfea more bool fixes and optimizations in codegen 2024-03-02 23:19:39 +01:00
c5c4c6f111 start of new ifelse 2024-03-02 23:19:39 +01:00
73be754680 move in place assignment functions back to AssignmentGen 2024-03-02 23:19:39 +01:00
acd841dbb6 bool changes in 6502 assignment codegen 2024-03-02 23:19:37 +01:00
6b52ba9397 6502 asmgen 2024-03-02 23:19:20 +01:00
10d12f73d6 IR/VM: testing the boolean changes, added in-place and/or. 2024-03-02 23:19:20 +01:00
cd9119655c IR codegen 2024-03-02 23:19:20 +01:00
41afeccd51 compiler stuff 2024-03-02 23:19:20 +01:00
6b87cbb703 optimizers 2024-03-02 23:19:20 +01:00
32afcbfe42 compilerAst BOOL type changes 2024-03-02 23:19:20 +01:00
bc2b38daf4 added PtBool and other changes to intermediate Ast 2024-03-02 23:19:20 +01:00
f40b7b62bb updated unit tests and some basic changes for them 2024-03-02 23:19:20 +01:00
1ca3f64bf0 libraries: add ==0 or !=0 to expressions that depend on implicit conversion from byte to bool 2024-03-02 23:19:20 +01:00
92527b4c1d examples: add ==0 or !=0 to expressions that depend on implicit conversion from byte to bool 2024-03-02 23:19:18 +01:00
c48012c385 tweak ifelse > and <= order 2024-03-02 17:01:31 +01:00
a282b17286 added asm optimizer for <= byte (bcc+beq->bcs) 2024-03-02 15:34:54 +01:00
58d9463f16 consolidate word comparison codegen 2024-03-02 14:00:12 +01:00
047decd552 consolidate byte comparison codegen 2024-03-01 22:21:22 +01:00
82e0877e64 readme 2024-03-01 00:41:11 +01:00
040d75dafa VM now supports indirect jump instruction 2024-02-22 16:19:26 +01:00
4e1686f6e3 fix warnings in gradle build scripts 2024-02-22 10:34:51 +01:00
b5e691f367 IR: fix chunk reachability: via unchopped chunk label directly so that they don't get removed 2024-02-21 23:18:57 +01:00
325f55f22d doc refs duplicates fix 2024-02-21 21:29:17 +01:00
9724f2db7d readthedocs 2024-02-21 21:23:18 +01:00
5f20f321f0 readthedocs tweaks 2024-02-21 21:05:38 +01:00
d4b087ea3f added txt.bell() to make terminal beep/bell sound on most systems 2024-02-21 17:35:37 +01:00
8ff10724d1 fix some docs hyperlinks 2024-02-21 17:11:36 +01:00
1581381467 streamline 2024-02-20 23:35:04 +01:00
96b5a30f60 slightly less strict 2024-02-20 23:01:51 +01:00
0e17a0474a added type check to catch invalid comparisons, fix maze example 2024-02-20 22:53:15 +01:00
b27368175d get rid of problematic rewrite of certain for loops that were causing the end expression to be evaluated every loop iteration 2024-02-20 21:03:21 +01:00
aba36f7c92 update github actions 2024-02-20 01:07:39 +01:00
a3fa946300 fix typo: reaturn -> return (#126) 2024-02-18 22:25:26 +01:00
01bbc2234e fix absorption law optimization, add 2 additional optimizations 2024-02-14 23:12:54 +01:00
58e1864144 Mention AUR package in documentation (#125)
Additionally, small fixes it nano highlighting
2024-02-12 20:42:00 +01:00
88458f5355 faster array copy and fix for length 256 2024-02-11 23:57:38 +01:00
a4f697bae1 faster 2024-02-10 23:56:55 +01:00
8201408f16 fix element size calc when copying array 2024-02-10 22:58:44 +01:00
8b8caa1c2e added math.randrange() and math.randrangew() 2024-02-10 22:16:13 +01:00
4dc50cb551 fix unused subroutine removal not removing all unused subs 2024-02-10 21:26:53 +01:00
5522a305ab add -dumpvars option to dump all allocated variables (zp, normal ram, etc) 2024-02-10 18:42:31 +01:00
d7f72056fc rest of the array copying 2024-02-10 17:16:06 +01:00
64c9c9b7fe hash only on release? 2024-02-10 02:11:39 +01:00
98e1c843e4 hash only on release? 2024-02-10 02:07:37 +01:00
906d9d858c implementing the array copys 2024-02-10 01:40:36 +01:00
16c1309df1 try to add a hash to the artifact 2024-02-10 00:38:32 +01:00
6eacf1bddd added a few more IR syscalls for often used copy routines 2024-02-09 23:56:44 +01:00
6c8c8e11cc fix cpu register overwrites when using @(...) arguments to an asmsub 2024-02-09 19:55:35 +01:00
e941d2665a preparing for new array copy codegen 2024-02-09 18:22:41 +01:00
68669dbef0 fix & of pointervar indexing 2024-02-08 23:03:53 +01:00
6a48de9a9f IR: fix & of array-element 2024-02-08 21:27:53 +01:00
9d6d98930b fix ast printing of & array-element 2024-02-08 21:27:53 +01:00
3cc858db12 Adding Syntax Higlighting for nano (#123) 2024-02-08 21:27:07 +01:00
386a391fd9 added string.lstripped() and string.ltrimmed() 2024-02-07 23:07:45 +01:00
d33aed4ed5 added txt.petscii2scr() and txt.petscii2scr_str() 2024-02-07 22:36:43 +01:00
73ec8c31ad fix 6502 code for zp pointer lookup clobbering registers. 2024-02-07 22:09:04 +01:00
24944ad49e added string.strip() and string.trim() and l/r variants.
fixed memsizer for pointers-to-ubyte.
2024-02-07 02:09:08 +01:00
26ed231f61 version 2024-02-07 00:03:39 +01:00
8485b8429f optimizing +=1/-=1 2024-02-06 23:49:40 +01:00
358215e4dd removed postIncrDecr (still allow ++/-- to be parsed into +=1/-=1) 2024-02-06 18:50:08 +01:00
f874942075 version 10.1 2024-02-04 16:22:43 +01:00
2cadb546d5 optimized in-place memory/pointervar operations some more 2024-02-04 15:17:24 +01:00
344a1b9eb8 cx16: added cx16.get_program_args() and cx16.set_program_args() 2024-02-04 11:35:13 +01:00
3c77f8a020 IR: optimize pointer access 2024-02-04 07:51:50 +01:00
8e00408e3e optimize pointer indexing a bit 2024-02-03 23:24:33 +01:00
abcdfd8e28 optimize postincrdecr on memory a bit 2024-02-03 22:33:27 +01:00
b0f5b6925d getting rid of pointer[idx] in ast, instead always use @(ptr+idx) 2024-02-03 19:22:04 +01:00
ef79d0c43e fix temp variable name clash resulting in faulty code in certain common subexpression optimization 2024-02-03 19:20:28 +01:00
78b4288005 avoid git's line ending translation for this test... 2024-02-03 14:57:25 +01:00
680f5d21ee normalize DOS line endings to just \n, fixes source lines in asm for \r\n source files 2024-02-03 12:53:21 +01:00
c71aa0895f get rid of certain redundant !=0 comparisons in logical expressions 2024-02-01 21:50:01 +01:00
9f8e61789a fix 65c02 codegen for certain in-place byte bitwise invert 2024-01-31 23:55:43 +01:00
932035cdc5 flag "returning a statement" as a syntax error 2024-01-31 21:36:39 +01:00
ef198f1493 fix for certain invalid expression evaluation code.
it no longer reuses the same temporary variable all the time.

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

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

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

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

Before the this fix, the initial replacement issue was of the
logo and not the portion of the screen originally modified by
vtui.
2023-12-20 23:49:47 +01:00
66d939df0d docs about new asm symbol prefixes 2023-12-20 22:37:46 +01:00
6bc079c7b7 more asm symbol prefixing: variables with p8v_, subroutines with p8s_ etc
labels with p8l_ . All this to avoid symbol clashes in the generated assembly code.
Everything got its own distinguishing prefix so we're done with it once and for all and have only 1 breaking change moment.
2023-12-20 22:20:59 +01:00
299419917e added symbol ambiguity error (variable vs block name for scoped symbols)
fixes #114
2023-12-20 00:28:15 +01:00
69f6afe420 block names in asm now prefixed with p8b_ (instead of p8_)
as part of fixing var versus block symbol conflict handling
2023-12-19 23:00:20 +01:00
b7279a3d9e fix 'not in' parsing error
fixes #115
2023-12-19 19:49:25 +01:00
e14b854d7b cx16: added audio kernal routines example 2023-12-19 01:47:05 +01:00
8bd7c601c0 cx16: added all remaining audio kernal routines. added the three x16edit kernal entry points. 2023-12-18 22:16:44 +01:00
997288fa03 added cbm.CLEARST() to reset ST to 0 2023-12-18 01:20:24 +01:00
0f26b39997 improve diskio error handling and device not found errors
for instance if you set drivenumber to 9 without having a second drive connected, it used to hang in various routines
2023-12-17 22:39:08 +01:00
ae66fcac1e added call builtin function for indirect JSR 2023-12-17 15:45:28 +01:00
43944a94eb doc tweaks 2023-12-17 14:47:20 +01:00
eba0bde6f3 Merge branch 'optimize-st'
# Conflicts:
#	examples/test.p8
2023-12-17 02:11:01 +01:00
4544af441b doc tweaks, explain str a bit more 2023-12-17 02:02:59 +01:00
a8be94de6b better error message when attempting to cast a float to integer 2023-12-15 22:28:06 +01:00
b24df31c2b IR: fix codegen for routines returning in CPU Status register flag 2023-12-14 21:16:14 +01:00
332ba8ed7e don't give error when returning uword value in subroutine that returns STR 2023-12-14 02:48:21 +01:00
58400f53bc optimize: flip if true/else blocks if the else block only contains a jump (inverting the condition) 2023-12-13 22:06:53 +01:00
01c2112881 remove PtJump label, just use identifier with dummy 2023-12-13 04:16:49 +01:00
a546c2247d optimize if-else handling of asmsub boolean result in status flags 2023-12-13 04:03:21 +01:00
0da9142009 asm: also work for asmsub that return N or Z flag (Carry already worked) 2023-12-13 02:11:15 +01:00
796add0ee2 add string.isspace and string.isprint 2023-12-13 00:28:34 +01:00
00b32f64e6 tweak for,sort,reverse st use 2023-12-12 20:26:45 +01:00
f97b3f23e2 optimize symbol table for IR 2023-12-12 19:26:27 +01:00
08a079a96e concerns with in for strings 2023-12-11 21:15:48 +01:00
e98e951834 fix chained assignment and multi-vardecl RHS evaluation 2023-12-10 16:44:51 +01:00
2668bf8519 fix void optimization issue 2023-12-09 21:48:22 +01:00
dd4c073e18 version 9.7 2023-12-09 18:54:54 +01:00
c7c72f00c7 document underscores in numeric literals for grouping 2023-12-09 14:07:42 +01:00
ef1c665b9a allow underscores for numerical grouping 2023-12-09 13:13:34 +01:00
d56565be25 fix multi-var decl 2023-12-09 12:32:41 +01:00
e076b3aedc fix multi-var decl in nested scopes 2023-12-09 12:07:09 +01:00
ae3b2ddf5f allow multi var declarations for floats too 2023-12-08 23:29:13 +01:00
1bdc427d73 multi var declarations ubyte x,y,z 2023-12-08 22:18:21 +01:00
6a639ce533 chained assignments x=y=z=42 2023-12-08 01:07:16 +01:00
d91ca8b197 vm: added floats.str_f() 2023-12-07 23:10:27 +01:00
a01c0a283d add check for memory() args to be const, added floats.str_f()
add check for memory() args to be const
2023-12-07 22:39:53 +01:00
5c393091a0 unit test for %encoding 2023-12-07 21:54:01 +01:00
01b680504b Add %encoding to syntax files (#113)
* IDEA `%encoding` syntax

* N++ `%encoding` syntax

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

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

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

* feat: really implement waitkey() on CBM targets

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

corrected to upper case: cx16 kernal calls CLOSE_ALL, LKUPLA, LKUPSA, JSRFAR, PRIMM, MACPTR.
2023-10-09 22:44:36 +02:00
a5cd3728c9 3d rotation multiplications now using verafx acceleration 2023-10-05 22:36:30 +02:00
a48ce35f0b added %option verafxmuls 2023-10-05 22:06:33 +02:00
e1835b5775 removed dysfunctional c128.graphics library module 2023-10-05 21:03:47 +02:00
433832b329 gfx2.clear_screen and monogfx.clear_screen() now have color parameter to clear the screen with
this is much faster than filling a rectangle of the full screen size with a color.
2023-10-05 21:00:39 +02:00
ee81da14d6 cx16: removed monochrome modes from gfx2 (use monogfx instead). New screen mode numbering!
programs will now be a lot smaller than before if they use gfx2 (or monogfx if they were only using monochrome drawing)
monogfx also fixes some drawing errors with small horizontal lines, and stippled vertical lines.
2023-10-05 02:12:46 +02:00
6395d1908e cx16: added monogfx library module, replaces gfx2 for monochrome screenmodes. 2023-10-04 22:32:13 +02:00
989a5a2f8a some notes about array alignment 2023-10-04 01:10:36 +02:00
b7a622c68e fix alignment of uninitialized arrays in aligned blocks (make them initialized with zeros so they don't end up in the BSS section)
fix alignment of uninitialized arrays in aligned blocks (make them initialized with zeros so they don't end up in the BSS section)
2023-10-04 00:12:36 +02:00
a8507b437d add verafx.transparency() 2023-10-03 01:47:52 +02:00
e505bf9ccf added "emudbg" library (cx16 only) to interface with the emulator 2023-10-02 22:23:09 +02:00
a289b32053 Revert "added -verafxmul compiler option to use vera fx multiplication routine on cx16"
This reverts commit 690782bf.
It was too risky, using vera (especially fx) transparently in multiple places especially perhaps in IRQ handlers will create havoc unless much intricate care is taken to save/restore the vera state. Better to do vera fx explicitly where the programmer has full control.
2023-10-02 21:08:52 +02:00
c3f1f09ad1 added verafx.clear() 2023-10-02 01:34:56 +02:00
70ee2026ff fix gfx2 screen fill broken when using verafx 2023-10-02 00:12:48 +02:00
690782bf60 added -verafxmul compiler option to use vera fx multiplication routine on cx16 2023-10-01 22:44:45 +02:00
755cc4835e \n (newline) now also maps to Petscii $0d (return), like \r.
It used to map to $8d (shift-return)
2023-09-29 01:49:15 +02:00
a684ea46e4 fix c64 zp test and improve error for text encoding problem 2023-09-29 01:25:05 +02:00
8fbe13f99d c64: $a5 removed from free ZP (it's actually used by kernal disk routines) 2023-09-29 00:28:04 +02:00
452e9e275f diskio module: set correct read or write i/o channel every time f_read or f_write is called 2023-09-28 23:39:37 +02:00
cd40088636 vm: added math.mul16_last_upper() 2023-09-28 03:18:49 +02:00
9b9e6f4af5 added math.mul16_last_upper() to fetch the upper 16 bits of the last word multiplication 2023-09-25 23:59:57 +02:00
ae6eeadf54 doc about range step value has to be a constant 2023-09-25 23:19:32 +02:00
5268b05060 added bonkram chunk to chunkfile example 2023-09-25 22:24:40 +02:00
390263a34e added cx16 verafx library module 2023-09-24 23:00:40 +02:00
55646edc3e added cx16 chunkedfile example 2023-09-24 20:56:36 +02:00
8d177beb78 fix possible register corruption when calling asmsubs that require Carry flag as a parameter 2023-09-24 14:03:31 +02:00
1da0c59182 vm: remove BNER opcode -> CMP + BSTNE 2023-09-23 11:47:24 +02:00
36e8f10d2b vm: remove BEQR opcode -> CMP + BSTEQ 2023-09-23 11:42:58 +02:00
cdf5a8f20f vm: remove BNE opcode -> CMPI + BSTNE 2023-09-23 11:22:33 +02:00
eb64d92333 vm: remove BEQ opcode -> CMPI + BSTEQ 2023-09-23 11:21:43 +02:00
eb55da63ef weird 2023-09-23 11:21:17 +02:00
918302f79b ir: fix possible crash in validity check about PREPARECALL 2023-09-23 01:35:18 +02:00
9d7131d9f6 vm: setting status bits 2023-09-22 22:50:20 +02:00
229c1114dd vm: fixed array initialization values with address-ofs 2023-09-19 23:54:18 +02:00
885df9156f todo 2023-09-19 00:08:17 +02:00
c319233ddc ir: added preparecall 'meta' instruction for functioncalls 2023-09-18 23:22:03 +02:00
958b5c0780 Merge branch 'addrof-arrayelt'
# Conflicts:
#	docs/source/todo.rst
2023-09-18 04:48:45 +02:00
880c0a5da8 allow taking address of array element 2023-09-18 04:37:41 +02:00
237c6dc856 allow taking address of array element 2023-09-18 04:29:15 +02:00
ccf6e32bf9 adding setlsb() and setmsb() builtin functions to 6502 codegen 2023-09-17 15:16:47 +02:00
a1874f6f00 adding setlsb() and setmsb() builtin functions to 6502 codegen 2023-09-17 01:48:29 +02:00
95e4490a8a adding setlsb() and setmsb() builtin functions 2023-09-15 02:39:16 +02:00
31c132c2eb several optimizations and compiler error fix for @(&var) and @(&var+1) 2023-09-14 23:04:23 +02:00
00b0ec58b4 update to Antlr 4.13.1 2023-09-14 21:11:55 +02:00
a1d0e5bb65 added list of software to docs 2023-09-13 21:51:48 +02:00
03e0d4b2e8 reducing expression codegen complexity (no longer splitting conditional expressions, and using r9 as temp var) 2023-09-13 01:08:42 +02:00
6afdd4e6fd preparing next version 2023-09-12 21:53:49 +02:00
b500a0d477 c64: added a couple of routines that calculate the correct memory locations for video ram and sprite pointers etc. based on current VIC-II memory setup.
the examples with sprites, now use it.
2023-09-08 21:27:38 +02:00
dd2463a440 proper fix for the previous commit. + fix for i/o channel reset in diskio.f_seek()
it wasn't the adressing mode, it was that it assumed the pointer variable was always in zeropage (which might not be)
2023-09-07 22:17:46 +02:00
23a8bebd9e fix invalid addressing mode on 6502 cpu for bytevalue +/- bytearray[i] 2023-09-07 21:40:07 +02:00
3caf9108ad finalizing 9.4.1 release 2023-09-06 21:18:01 +02:00
bde4be8231 fix VM indexed instructions to only use lsb part of the index 2023-09-06 02:44:04 +02:00
0bbbb12ed2 fix bench8 examples 2023-09-05 23:40:54 +02:00
b570bdaed7 fix codegen for array[i] += float expression 2023-09-05 22:38:52 +02:00
8c0843cc87 fix an invalid 6502 instruction on c64 in certain float assignment 2023-09-05 21:54:52 +02:00
31458ffd81 examples cleanup and improving c64 graphics module (shift bitmap to higher ram area) 2023-09-05 20:39:12 +02:00
c15c10a94e fixed 'unroll CONSTANTEXPR' compiler errors 2023-09-05 01:03:35 +02:00
9fca978725 optimized plasma examples even more 2023-09-05 00:23:50 +02:00
b125901717 added cx16 plasma example 2023-09-04 23:54:13 +02:00
eb018ae660 code optimization for bytearray[x] +/- bytearray[y]
use adc array,y or sbc array,y instead of tempvar
2023-09-04 23:01:53 +02:00
7e5a9474fe improve plasma example 2023-09-04 20:35:43 +02:00
525a9b5036 prepare parser to allow chained array indexing later 2023-09-03 19:06:47 +02:00
c3fbdf34ca fixed c64 float problem 2023-09-03 16:40:10 +02:00
48bd51e1a5 c64 float problem 2023-09-03 16:29:01 +02:00
10d0b03a90 use less tempvars 2023-09-03 01:32:47 +02:00
e1b3582f08 fix wordvar -= @(memory) 2023-09-03 01:12:26 +02:00
95be1c9e22 fix optimized swapped in-place byte comparisons 2023-09-03 00:47:55 +02:00
1ce8fe06d5 fix in-place <= for bytes 2023-09-03 00:01:11 +02:00
15c649024e float problems on c64 2023-09-02 23:09:55 +02:00
e97303c226 fix word multiplication to not clobber r0 and r1 anymore
This was causing corruption in certain programs such as the cx16/amiga example.
The problem was introduced in 9.4 with the new multiply_words routine
2023-09-02 20:52:16 +02:00
3b786c819d avoid using temp var even more 2023-09-01 23:47:01 +02:00
04959dbd8b optimize asm: don't use temp var for some additions 2023-09-01 22:24:17 +02:00
5cd4b874ea tweak sprites module 2023-09-01 21:25:19 +02:00
f14ea1b3de micro optimization to save 2 cycles: change some pha+pla into tax+txa 2023-09-01 20:37:24 +02:00
9cc0cda0fb added sprites library module (cx16 only) 2023-09-01 17:35:07 +02:00
09a7a4bbe5 optimize comparison against zero 2023-09-01 02:28:11 +02:00
cfea8b3745 save a cycle 2023-09-01 00:50:24 +02:00
28bf0b61ce added math.log2() and math.log2w() 2023-09-01 00:42:15 +02:00
2dc2429735 tweaks to the cx16 sprite example 2023-08-31 23:24:46 +02:00
83d4592526 tweaks to the cx16 sprite example 2023-08-31 22:33:49 +02:00
2d528c26ae added cx16 sprite demo 2023-08-31 16:56:52 +02:00
66b3dce794 doc tweak 2023-08-30 13:16:39 +02:00
598 changed files with 75779 additions and 32186 deletions

14
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,14 @@
# These are supported funding model platforms
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username
ko_fi: irmen
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: ['https://paypal.me/irmendejong']

View File

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

8
.gitignore vendored
View File

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

File diff suppressed because it is too large Load Diff

12
.idea/kotlinc.xml generated
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="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.1" />
<option name="languageVersion" value="2.1" />
</component>
<component name="KotlinCompilerSettings">
<option name="additionalArguments" value="-Xwhen-guards" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0-release-358" />
<option name="version" value="2.1.10" />
</component>
</project>

View File

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

View File

@ -1,13 +1,13 @@
<component name="libraryTable">
<library name="antlr.antlr4" type="repository">
<properties maven-id="org.antlr:antlr4:4.12.0">
<properties maven-id="org.antlr:antlr4:4.13.2">
<exclude>
<dependency maven-id="com.ibm.icu:icu4j" />
</exclude>
</properties>
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.12.0/antlr4-4.12.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.12.0/antlr4-runtime-4.12.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.2/antlr4-4.13.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.2/antlr4-runtime-4.13.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />

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

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

View File

@ -1,25 +0,0 @@
<component name="libraryTable">
<library name="github.hypfvieh.dbus.java" type="repository">
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.2" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.2/dbus-java-3.3.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.17/jnr-unixsocket-0.38.17.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.11/jnr-ffi-2.2.11.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.9/jffi-1.3.9-native.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.2/asm-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.2/asm-commons-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.2/asm-analysis-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.2/asm-tree-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.2/asm-util-9.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.3/jnr-constants-0.10.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.13/jnr-enxio-0.32.13.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.15/jnr-posix-3.1.15.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

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

View File

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

View File

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

View File

@ -1,30 +1,30 @@
<component name="libraryTable">
<library name="io.kotest.runner.junit5.jvm" type="repository">
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.6.2" />
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.9.1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.6.2/kotest-runner-junit5-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.6.2/kotest-framework-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.9.1/kotest-runner-junit5-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.7.0/kotlinx-coroutines-test-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.6.2/kotest-framework-engine-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.157/classgraph-4.8.157.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.9.1/kotest-framework-engine-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.172/classgraph-4.8.172.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.8.0/kotlinx-coroutines-debug-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.6.2/kotest-framework-discovery-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.6.2/kotest-extensions-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.6.2/kotest-framework-concurrency-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.9.1/kotest-framework-discovery-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.9.1/kotest-extensions-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.9.1/kotest-framework-concurrency-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
@ -32,11 +32,8 @@
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View File

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

View File

@ -1,15 +1,20 @@
<component name="libraryTable">
<library name="michael.bull.kotlin.result.jvm" type="repository">
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" />
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.18/kotlin-result-jvm-1.1.18.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

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

View File

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

9
.idea/misc.xml generated
View File

@ -4,22 +4,25 @@
<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>
</option>
</component>
<component name="Black">
<option name="sdkName" value="Python 3.11" />
</component>
<component name="FrameworkDetectionExcludesConfiguration">
<type id="Python" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

7
.idea/modules.xml generated
View File

@ -2,6 +2,8 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/beanshell/beanshell.iml" filepath="$PROJECT_DIR$/beanshell/beanshell.iml" />
<module fileurl="file://$PROJECT_DIR$/benchmark-program/benchmark-program.iml" filepath="$PROJECT_DIR$/benchmark-program/benchmark-program.iml" />
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
@ -10,12 +12,13 @@
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.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$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
</modules>
</component>

View File

@ -9,17 +9,19 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf
python: "3.12"
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
fail_on_warning: true
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats:
- pdf
- epub

12
Makefile Normal file
View File

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

View File

@ -1,3 +1,7 @@
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H6S0FFF)
PayPal: [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://paypal.me/irmendejong)
[![Documentation](https://readthedocs.org/projects/prog8/badge/?version=latest)](https://prog8.readthedocs.io/)
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
@ -9,6 +13,13 @@ This is a structured programming language for the 8-bit 6502/6510/65c02 micropro
as used in many home computers from that era. It is a medium to low level programming language,
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
**Want to buy me a coffee or a pizza perhaps?**
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
Documentation
-------------
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
@ -21,6 +32,10 @@ How to get it/build it
- Or, if you want/need a bleeding edge development version, you can:
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
Note that if you are not using *gradle* to build it, you might have to perform some manual
tasks once to make it compile fully. These are explained in the linked instructions.
- Alternatively, you can also install the compiler as a package on some linux distros:
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
Community
---------
@ -41,26 +56,32 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code:
What does Prog8 provide?
------------------------
- reduction of source code length over raw assembly
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
- modularity, symbol scoping, subroutines
- all advantages of a higher level language over having to write assembly code manually
- programs run very fast because it's compiled to native machine code
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- modularity, symbol scoping, subroutines. No need for forward declarations.
- various data types other than just bytes (16-bit words, floats, strings)
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
- floating point math is supported on certain targets
- access to most Kernal ROM routines as external subroutine definitions you can call normally
- tight control over Zeropage usage
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
- programs can be configured to execute in ROM
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
- automatic static variable allocations, automatic string and array variables and string sharing
- subroutines with input parameters and result values
- high-level program optimizations
- small program boilerplate/compilersupport overhead
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
- conditional branches
- conditional branches that map 1:1 to cpu status flags
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
- ``on .. goto`` statement for fast jump tables
- ``in`` expression for concise and efficient multi-value/containment check
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
- ``defer`` statement to help write concise and robust subroutine cleanup logic
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
- various powerful built-in libraries to do I/O, number conversions, graphics and more
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- subroutines can return more than one result value
- inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
- encode strings and characters into petscii or screencodes or even other encodings
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
*Rapid edit-compile-run-debug cycle:*
@ -71,11 +92,11 @@ 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)
- "atari": Atari 8 bit such as 800XL (experimental)
- "pet32": Commodore PET (limited support)
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
- 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)
@ -108,14 +129,13 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
%import textio
%zeropage basicsafe
main {
ubyte[256] sieve
bool[256] sieve
ubyte candidate_prime = 2 ; is increased in the loop
sub start() {
sys.memset(sieve, 256, false) ; clear the sieve
sys.memset(sieve, 256, 0) ; clear the sieve
txt.print("prime numbers up to 255:\n\n")
ubyte amount=0
repeat {
@ -151,9 +171,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
}
}
when compiled an ran on a C-64 you'll get:
![c64 screen](docs/source/_static/primes_example.png)

View File

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

View File

@ -0,0 +1,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.

View File

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

View File

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

View File

@ -0,0 +1,10 @@
.PHONY: clean run
run:
prog8c -target cx16 benchmark.p8
x16emu -run -prg benchmark.prg -warp
clean:
rm -f *.prg *.PRG *.asm *.vice-* *.BIN *.PAL *.zip *.7z

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

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

View File

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

View File

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

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

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

View File

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

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

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

View File

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

View File

@ -0,0 +1,69 @@
%import sprites
%import coroutines
%import math
animsprites {
uword num_iterations
ubyte[64] sx
ubyte[64] sy
ubyte[64] sc
ubyte[64] dx
ubyte[64] dy
uword maximum_duration
sub benchmark(uword max_duration) -> uword {
maximum_duration = max_duration
math.rndseed(1122,9876)
cx16.set_screen_mode(3)
cx16.mouse_config2(1)
sprites.set_mousepointer_hand()
repeat 64
void coroutines.add(animsprite, 0)
cx16.mouse_config2(0)
cbm.SETTIM(0,0,0)
coroutines.run(supervisor)
sprites.reset(0, 64)
return num_iterations
}
sub supervisor() -> bool {
if cbm.RDTIM16() >= maximum_duration {
coroutines.killall()
return false
}
return true
}
sub animsprite() {
num_iterations++
; set up the sprite
ubyte sprnum = coroutines.current()
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
sx[sprnum] = math.rnd()
sy[sprnum] = math.rnd()
sc[sprnum] = math.rnd()
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
; move the sprite around
while sc[sprnum]!=0 {
animate(sprnum)
void coroutines.yield()
sprnum = coroutines.current()
}
sub animate(ubyte spr) {
defer sc[spr]--
sprites.pos(spr, sx[spr], sy[spr])
sx[spr] += dx[spr]
sy[spr] += dy[spr]
}
; end the task but replace it with a fresh animated sprite task
void coroutines.add(animsprite, 0)
}
}

View File

@ -0,0 +1,989 @@
%import textio
%import conv
%import strings
textelite {
const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one
const ubyte numforZaonce = 129
const ubyte numforDiso = 147
const ubyte numforRiedquat = 46
uword num_commands
sub bench(uword max_time) -> uword {
num_commands = 0
txt.lowercase()
cbm.SETTIM(0,0,0)
while cbm.RDTIM16()<max_time {
reinit()
run_commands(max_time)
}
return num_commands
}
sub reinit() {
;txt.clear_screen()
;txt.print("\n --- TextElite v1.3 ---\n")
txt.print("\nnew game\n")
elite_planet.set_seed(0, 0)
elite_galaxy.travel_to(1, numforLave)
elite_market.init(0) ; Lave's market is seeded with 0
elite_ship.init()
elite_planet.display(false, 0)
input_index = 0
}
sub run_commands(uword max_time) {
while cbm.RDTIM16() < max_time {
str input = "????????"
;txt.print("\nCash: ")
;elite_util.print_10s(elite_ship.cash)
;txt.print("\nCommand (?=help): ")
ubyte num_chars = next_input(input)
;txt.nl()
if num_chars!=0 {
when input[0] {
'q' -> {
bool has_error = false
if elite_galaxy.number != 2 {
txt.print("\nERROR: galaxy is not 2: ")
txt.print_ub(elite_galaxy.number)
txt.nl()
has_error=true
}
if elite_planet.number != 164 {
txt.print("\nERROR: planet is not 164: ")
txt.print_ub(elite_planet.number)
txt.nl()
has_error=true
}
if elite_planet.x != 116 {
txt.print("\nERROR: planet.x is not 116: ")
txt.print_ub(elite_planet.x)
txt.nl()
has_error=true
}
if elite_planet.y != 201 {
txt.print("\nERROR: planet.y is not 201: ")
txt.print_ub(elite_planet.y)
txt.nl()
has_error=true
}
if "ribeen" != elite_planet.name {
txt.print("\nERROR: planet.name is not 'ribeen': ")
txt.print(elite_planet.name)
txt.nl()
has_error=true
}
if elite_ship.cash != 1212 {
txt.print("\nERROR: cash is not 1212: ")
txt.print_uw(elite_ship.cash)
txt.nl()
has_error=true
}
if elite_ship.fuel != 50 {
txt.print("\nERROR: fuel is not 50:")
txt.print_ub(elite_ship.fuel)
txt.nl()
has_error=true
}
if elite_ship.cargohold[0] != 3 {
txt.print("\nERROR: food is not 3:")
txt.print_ub(elite_ship.cargohold[0])
txt.nl()
has_error=true
}
if elite_ship.cargohold[1] != 0 {
txt.print("\nERROR: textiles is not 0:")
txt.print_ub(elite_ship.cargohold[1])
txt.nl()
has_error=true
}
if has_error
sys.exit(1)
return
}
'b' -> elite_trader.do_buy()
's' -> elite_trader.do_sell()
'f' -> elite_trader.do_fuel()
'j' -> elite_trader.do_jump()
't' -> elite_trader.do_teleport()
'g' -> elite_trader.do_next_galaxy()
'i' -> elite_trader.do_info()
'm' -> {
if input[1]=='a' and input[2]=='p'
elite_trader.do_map()
else
elite_trader.do_show_market()
}
'l' -> elite_trader.do_local()
'c' -> elite_trader.do_cash()
'h' -> elite_trader.do_hold()
}
num_commands++
}
}
}
str[] inputs = [
"i",
"diso",
"i",
"lave",
"m",
"b",
"food",
"15",
"map",
"g",
"map",
"l",
"j",
"zao",
"s",
"food",
"12",
"tele",
"quti",
"tele",
"aro",
"i",
"diso",
"i",
"lave",
"i",
"zao",
"galhyp",
"fuel",
"20",
"j",
"rib",
"i",
"rib",
"i",
"tiri",
"q",
0
]
ubyte input_index
sub next_input(str buffer) -> ubyte {
input_index++
return strings.copy(inputs[input_index], buffer)
}
}
elite_trader {
str input = "??????????"
ubyte num_chars
sub do_jump() {
;txt.print("\nJump to what system? ")
jump_to_system()
}
sub do_teleport() {
;txt.print("\nCheat! Teleport to what system? ")
ubyte fuel = elite_ship.fuel
elite_ship.fuel = 255
jump_to_system()
elite_ship.fuel = fuel
}
sub jump_to_system() {
void textelite.next_input(input)
ubyte current_planet = elite_planet.number
ubyte x = elite_planet.x
ubyte y = elite_planet.y
if elite_galaxy.search_closest_planet(input) {
ubyte distance = elite_planet.distance(x, y)
if distance <= elite_ship.fuel {
elite_galaxy.init_market_for_planet()
elite_ship.fuel -= distance
;txt.print("\n\nHyperspace jump! Arrived at:\n")
elite_planet.display(true,0 )
return
}
;txt.print("\nInsufficient fuel\n")
} else {
;txt.print(" Not found!\n")
}
elite_galaxy.travel_to(elite_galaxy.number, current_planet)
}
sub do_buy() {
;txt.print("\nBuy what commodity? ")
str commodity = "???????????????"
void textelite.next_input(commodity)
ubyte ci = elite_market.match(commodity)
if ci & 128 !=0 {
txt.print("Unknown\n")
} else {
;txt.print("\nHow much? ")
void textelite.next_input(input)
ubyte amount = conv.str2ubyte(input)
if elite_market.current_quantity[ci] < amount {
txt.print(" Insufficient supply!\n")
} else {
uword price = elite_market.current_price[ci] * amount
;txt.print(" Total price: ")
;elite_util.print_10s(price)
if price > elite_ship.cash {
txt.print(" Not enough cash!\n")
} else {
elite_ship.cash -= price
elite_ship.cargohold[ci] += amount
elite_market.current_quantity[ci] -= amount
}
}
}
}
sub do_sell() {
;txt.print("\nSell what commodity? ")
str commodity = "???????????????"
void textelite.next_input(commodity)
ubyte ci = elite_market.match(commodity)
if ci & 128 !=0 {
txt.print("Unknown\n")
} else {
;txt.print("\nHow much? ")
void textelite.next_input(input)
ubyte amount = conv.str2ubyte(input)
if elite_ship.cargohold[ci] < amount {
txt.print(" Insufficient supply!\n")
} else {
uword price = elite_market.current_price[ci] * amount
;txt.print(" Total price: ")
;elite_util.print_10s(price)
elite_ship.cash += price
elite_ship.cargohold[ci] -= amount
elite_market.current_quantity[ci] += amount
}
}
}
sub do_fuel() {
;txt.print("\nBuy fuel. Amount? ")
void textelite.next_input(input)
ubyte buy_fuel = 10*conv.str2ubyte(input)
ubyte max_fuel = elite_ship.Max_fuel - elite_ship.fuel
if buy_fuel > max_fuel
buy_fuel = max_fuel
uword price = buy_fuel as uword * elite_ship.Fuel_cost
if price > elite_ship.cash {
txt.print("Not enough cash!\n")
} else {
elite_ship.cash -= price
elite_ship.fuel += buy_fuel
}
}
sub do_cash() {
;txt.print("\nCheat! Set cash amount: ")
void textelite.next_input(input)
elite_ship.cash = conv.str2uword(input)
}
sub do_hold() {
;txt.print("\nCheat! Set cargohold size: ")
void textelite.next_input(input)
elite_ship.Max_cargo = conv.str2ubyte(input)
}
sub do_next_galaxy() {
txt.print("\n>>>>> Galaxy Hyperjump!\n")
elite_galaxy.travel_to(elite_galaxy.number+1, elite_planet.number)
elite_planet.display(false, 0)
}
sub do_info() {
;txt.print("\nSystem name (empty=current): ")
num_chars = textelite.next_input(input)
if num_chars!=0 {
ubyte current_planet = elite_planet.number
ubyte x = elite_planet.x
ubyte y = elite_planet.y
if elite_galaxy.search_closest_planet(input) {
ubyte distance = elite_planet.distance(x, y)
elite_planet.display(false, distance)
} else {
;txt.print(" Not found!")
}
elite_galaxy.travel_to(elite_galaxy.number, current_planet)
} else {
elite_planet.display(false, 0)
}
}
sub do_local() {
elite_galaxy.local_area()
}
sub do_map() {
;txt.print("\n(l)ocal or (g)alaxy starmap? ")
num_chars = textelite.next_input(input)
if num_chars!=0 {
elite_galaxy.starmap(input[0]=='l')
}
}
sub do_show_market() {
elite_market.display()
;txt.print("\nFuel: ")
;elite_util.print_10s(elite_ship.fuel)
;txt.print(" Cargohold space: ")
;txt.print_ub(elite_ship.cargo_free())
;txt.print("t\n")
}
}
elite_ship {
const ubyte Max_fuel = 70
const ubyte Fuel_cost = 2
ubyte Max_cargo = 20
ubyte fuel
uword cash
ubyte[17] cargohold
sub init() {
sys.memset(cargohold, len(cargohold), 0)
fuel = Max_fuel
cash = 1000
}
}
elite_market {
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
ubyte[17] current_quantity
uword[17] current_price
sub init(ubyte fluct) {
; Prices and availabilities are influenced by the planet's economy type
; (0-7) and a random "fluctuation" byte that was kept within the saved
; commander position to keep the market prices constant over gamesaves.
; Availabilities must be saved with the game since the player alters them
; by buying (and selling(?))
;
; Almost all commands are one byte only and overflow "errors" are
; extremely frequent and exploited.
;
; Trade Item prices are held internally in a single byte=true value/4.
; The decimal point in prices is introduced only when printing them.
; Internally, all prices are integers.
; The player's cash is held in four bytes.
ubyte ci
for ci in 0 to len(names)-1 {
word product
byte changing
product = elite_planet.economy as word * gradients[ci]
changing = fluct & maskbytes[ci] as byte
ubyte q = (basequants[ci] as word + changing - product) as ubyte
if q & $80 !=0
q = 0 ; clip to positive 8-bit
current_quantity[ci] = q & $3f
q = (baseprices[ci] + changing + product) as ubyte
current_price[ci] = q * $0004
}
current_quantity[16] = 0 ; force nonavailability of Alien Items
}
sub display() {
return
; ubyte ci
; txt.nl()
; elite_planet.print_name_uppercase()
; txt.print(" trade market:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
; for ci in 0 to len(names)-1 {
; elite_util.print_right(13, names[ci])
; txt.print(" ")
; elite_util.print_10s(current_price[ci])
; txt.column(24)
; txt.print_ub(current_quantity[ci])
; txt.chrout(' ')
; when units[ci] {
; 0 -> txt.chrout('t')
; 1 -> txt.print("kg")
; 2 -> txt.chrout('g')
; }
; txt.column(32)
; txt.print_ub(elite_ship.cargohold[ci])
; txt.nl()
; }
}
sub match(uword nameptr) -> ubyte {
ubyte ci
for ci in 0 to len(names)-1 {
if elite_util.prefix_matches(nameptr, names[ci])
return ci
}
return 255
}
}
elite_galaxy {
const uword GALSIZE = 256
const uword base0 = $5A4A ; seeds for the first galaxy
const uword base1 = $0248
const uword base2 = $B753
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
ubyte number
uword[3] seed
sub init(ubyte galaxynum) {
number = 1
elite_planet.number = 255
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxynum-1 {
nextgalaxy()
}
}
sub nextgalaxy() {
textelite.num_commands++
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
}
sub travel_to(ubyte galaxynum, ubyte system) {
init(galaxynum)
generate_next_planet() ; always at least planet 0 (separate to avoid repeat ubyte overflow)
repeat system {
generate_next_planet()
textelite.num_commands++
}
elite_planet.name = make_current_planet_name()
init_market_for_planet()
}
sub init_market_for_planet() {
elite_market.init(lsb(seed[0])+msb(seed[2]))
}
sub search_closest_planet(uword nameptr) -> bool {
textelite.num_commands++
ubyte x = elite_planet.x
ubyte y = elite_planet.y
ubyte current_planet_num = elite_planet.number
init(number)
bool found = false
ubyte current_closest_pi
ubyte current_distance = 127
ubyte pi
for pi in 0 to 255 {
generate_next_planet()
elite_planet.name = make_current_planet_name()
if elite_util.prefix_matches(nameptr, elite_planet.name) {
ubyte distance = elite_planet.distance(x, y)
if distance < current_distance {
current_distance = distance
current_closest_pi = pi
found = true
}
}
}
if found
travel_to(number, current_closest_pi)
else
travel_to(number, current_planet_num)
return found
}
sub local_area() {
ubyte current_planet = elite_planet.number
ubyte px = elite_planet.x
ubyte py = elite_planet.y
ubyte pn = 0
init(number)
; txt.print("\nGalaxy #")
; txt.print_ub(number)
; txt.print(" - systems in vicinity:\n")
do {
generate_next_planet()
ubyte distance = elite_planet.distance(px, py)
if distance <= elite_ship.Max_fuel {
; if distance <= elite_ship.fuel
; txt.chrout('*')
; else
; txt.chrout('-')
; txt.spc()
elite_planet.name = make_current_planet_name()
elite_planet.display(true, distance)
}
pn++
} until pn==0
travel_to(number, current_planet)
}
sub starmap(bool local) {
ubyte current_planet = elite_planet.number
ubyte px = elite_planet.x
ubyte py = elite_planet.y
str current_name = " " ; 8 max
ubyte pn = 0
current_name = elite_planet.name
init(number)
; txt.clear_screen()
; txt.print("Galaxy #")
; txt.print_ub(number)
; if local
; txt.print(" - local systems")
; else
; txt.print(" - galaxy")
; txt.print(" starmap:\n")
ubyte max_distance = 255
if local
max_distance = elite_ship.Max_fuel
ubyte home_sx
ubyte home_sy
ubyte home_distance
do {
generate_next_planet()
ubyte distance = elite_planet.distance(px, py)
if distance <= max_distance {
elite_planet.name = make_current_planet_name()
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
uword tx = elite_planet.x
uword ty = elite_planet.y
if local {
tx = tx + 24 - px
ty = ty + 24 - py
}
ubyte sx = display_scale_x(tx)
ubyte sy = display_scale_y(ty)
ubyte char = '*'
if elite_planet.number==current_planet
char = '%'
if local {
print_planet_details(elite_planet.name, sx, sy, distance)
} else if elite_planet.number==current_planet {
home_distance = distance
home_sx = sx
home_sy = sy
}
; txt.setchr(2+sx, 2+sy, char)
}
pn++
} until pn==0
if not local
print_planet_details(current_name, home_sx, home_sy, home_distance)
; if local
; txt.plot(0, display_scale_y(64) + 4)
; else
; txt.plot(0, display_scale_y(256) + 4 as ubyte)
travel_to(number, current_planet)
sub print_planet_details(str name, ubyte screenx, ubyte screeny, ubyte d) {
return
; txt.plot(2+screenx-2, 2+screeny+1)
; txt.print(name)
; if d!=0 {
; txt.plot(2+screenx-2, 2+screeny+2)
; elite_util.print_10s(d)
; txt.print(" LY")
; }
}
sub display_scale_x(uword x) -> ubyte {
if local
return x/2 as ubyte
return x/8 as ubyte
}
sub display_scale_y(uword y) -> ubyte {
if local
return y/4 as ubyte
return y/16 as ubyte
}
}
ubyte pn_pair1
ubyte pn_pair2
ubyte pn_pair3
ubyte pn_pair4
bool longname
sub generate_next_planet() {
determine_planet_properties()
longname = lsb(seed[0]) & 64 !=0
; Always four iterations of random number
pn_pair1 = (msb(seed[2]) & 31) * 2
tweakseed()
pn_pair2 = (msb(seed[2]) & 31) * 2
tweakseed()
pn_pair3 = (msb(seed[2]) & 31) * 2
tweakseed()
pn_pair4 = (msb(seed[2]) & 31) * 2
tweakseed()
}
sub make_current_planet_name() -> str {
ubyte ni = 0
str name = " " ; max 8
if pn_pairs[pn_pair1] != '.' {
name[ni] = pn_pairs[pn_pair1]
ni++
}
if pn_pairs[pn_pair1+1] != '.' {
name[ni] = pn_pairs[pn_pair1+1]
ni++
}
if pn_pairs[pn_pair2] != '.' {
name[ni] = pn_pairs[pn_pair2]
ni++
}
if pn_pairs[pn_pair2+1] != '.' {
name[ni] = pn_pairs[pn_pair2+1]
ni++
}
if pn_pairs[pn_pair3] != '.' {
name[ni] = pn_pairs[pn_pair3]
ni++
}
if pn_pairs[pn_pair3+1] != '.' {
name[ni] = pn_pairs[pn_pair3+1]
ni++
}
if longname {
if pn_pairs[pn_pair4] != '.' {
name[ni] = pn_pairs[pn_pair4]
ni++
}
if pn_pairs[pn_pair4+1] != '.' {
name[ni] = pn_pairs[pn_pair4+1]
ni++
}
}
name[ni] = 0
return name
}
sub determine_planet_properties() {
; create the planet's characteristics
elite_planet.number++
elite_planet.x = msb(seed[1])
elite_planet.y = msb(seed[0])
elite_planet.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
elite_planet.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
if elite_planet.govtype <= 1
elite_planet.economy = (elite_planet.economy | 2)
elite_planet.techlevel = (msb(seed[1]) & 3) + (elite_planet.economy ^ 7)
elite_planet.techlevel += elite_planet.govtype >> 1
if elite_planet.govtype & 1 !=0
elite_planet.techlevel++
elite_planet.population = 4 * elite_planet.techlevel + elite_planet.economy
elite_planet.population += elite_planet.govtype + 1
elite_planet.productivity = ((elite_planet.economy ^ 7) + 3) * (elite_planet.govtype + 4)
elite_planet.productivity *= elite_planet.population * 8
ubyte seed2_msb = msb(seed[2])
elite_planet.radius = mkword((seed2_msb & 15) + 11, elite_planet.x)
elite_planet.species_is_alien = lsb(seed[2]) & 128 !=0 ; bit 7 of w2_lo
if elite_planet.species_is_alien {
elite_planet.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
elite_planet.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
elite_planet.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
elite_planet.species_kind = (elite_planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
elite_planet.goatsoup_seed[0] = lsb(seed[1])
elite_planet.goatsoup_seed[1] = msb(seed[1])
elite_planet.goatsoup_seed[2] = lsb(seed[2])
elite_planet.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {
uword temp = seed[0] + seed[1] + seed[2]
seed[0] = seed[1]
seed[1] = seed[2]
seed[2] = temp
}
sub twist(uword x) -> uword {
ubyte xh = msb(x)
ubyte xl = lsb(x)
xh <<= 1 ; make sure carry flag is not used on first shift!
rol(xl)
return mkword(xh, xl)
}
}
elite_planet {
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,
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
words91, words92, words93, words94, words95, words96, words97, words98,
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
wordsA1, wordsA2, wordsA3, wordsA4]
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
str name = " " ; 8 max
ubyte number ; starts at 0 in new galaxy, then increases by 1 for each generated planet
ubyte x
ubyte y
ubyte economy
ubyte govtype
ubyte techlevel
ubyte population
uword productivity
uword radius
bool species_is_alien ; otherwise "Human Colonials"
ubyte species_size
ubyte species_color
ubyte species_look
ubyte species_kind
sub set_seed(uword s1, uword s2) {
goatsoup_seed[0] = lsb(s1)
goatsoup_seed[1] = msb(s1)
goatsoup_seed[2] = lsb(s2)
goatsoup_seed[3] = msb(s2)
reset_rnd()
}
sub reset_rnd() {
goatsoup_rnd[0] = goatsoup_seed[0]
goatsoup_rnd[1] = goatsoup_seed[1]
goatsoup_rnd[2] = goatsoup_seed[2]
goatsoup_rnd[3] = goatsoup_seed[3]
}
sub random_name() -> str {
ubyte ii
str randname = " " ; 8 chars max
ubyte nx = 0
for ii in 0 to goatsoup_rnd_number() & 3 {
ubyte xx = goatsoup_rnd_number() & $3e
if pairs0[xx] != '.' {
randname[nx] = pairs0[xx]
nx++
}
xx++
if pairs0[xx] != '.' {
randname[nx] = pairs0[xx]
nx++
}
}
randname[nx] = 0
randname[0] = strings.upperchar(randname[0])
return randname
}
sub goatsoup_rnd_number() -> ubyte {
ubyte xx = goatsoup_rnd[0] * 2
uword a = xx as uword + goatsoup_rnd[2]
if goatsoup_rnd[0] > 127
a ++
goatsoup_rnd[0] = lsb(a)
goatsoup_rnd[2] = xx
xx = goatsoup_rnd[1]
ubyte ac = xx + goatsoup_rnd[3] + msb(a)
goatsoup_rnd[1] = ac
goatsoup_rnd[3] = xx
return ac
}
sub distance(ubyte px, ubyte py) -> ubyte {
uword ax
uword ay
if px>x
ax=px-x
else
ax=x-px
if py>y
ay=py-y
else
ay=y-py
ay /= 2
ubyte d = sqrt(ax*ax + ay*ay)
if d>63
return 255
return d*4
}
sub soup() -> str {
str planet_result = " " * 160
uword[6] source_stack
ubyte stack_ptr = 0
str start_source = "\x8F is \x97."
uword source_ptr = &start_source
uword result_ptr = &planet_result
reset_rnd()
recursive_soup()
return planet_result
sub recursive_soup() {
repeat {
ubyte c = @(source_ptr)
source_ptr++
if c == $00 {
@(result_ptr) = 0
return
}
else if c <= $80 {
@(result_ptr) = c
result_ptr++
}
else {
if c <= $a4 {
ubyte rnr = goatsoup_rnd_number()
ubyte wordNr = ((rnr >= $33) as ubyte) + ((rnr >= $66) as ubyte) + ((rnr >= $99) as ubyte) + ((rnr >= $CC) as ubyte)
source_stack[stack_ptr] = source_ptr
stack_ptr++
source_ptr = getword(c, wordNr)
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
stack_ptr--
source_ptr = source_stack[stack_ptr]
} else {
if c == $b0 {
@(result_ptr) = strings.upperchar(name[0])
result_ptr++
concat_string(&name + 1)
}
else if c == $b1 {
@(result_ptr) = strings.upperchar(name[0])
result_ptr++
ubyte ni
for ni in 1 to len(name) {
ubyte cc = name[ni]
if cc in ['e', 'o', 0]
break
else {
@(result_ptr) = cc
result_ptr++
}
}
@(result_ptr) = 'i'
result_ptr++
@(result_ptr) = 'a'
result_ptr++
@(result_ptr) = 'n'
result_ptr++
}
else if c == $b2 {
concat_string(random_name())
}
else {
@(result_ptr) = c
result_ptr++
}
}
}
}
}
sub concat_string(uword str_ptr) {
repeat {
ubyte c = @(str_ptr)
if c==0
break
else {
@(result_ptr) = c
str_ptr++
result_ptr++
}
}
}
}
sub display(bool compressed, ubyte distance) {
txt.print(soup())
txt.nl()
}
sub getword(ubyte listnum, ubyte wordidx) -> uword {
uword list = wordlists[listnum-$81]
return peekw(list + wordidx*2)
}
}
elite_util {
sub prefix_matches(uword prefixptr, uword stringptr) -> bool {
repeat {
ubyte pc = @(prefixptr)
ubyte sc = @(stringptr)
if pc == 0
return true
; to lowercase for case insensitive compare:
if strings.lowerchar(pc)!=strings.lowerchar(sc)
return false
prefixptr++
stringptr++
}
}
}

View File

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

View File

@ -1,10 +0,0 @@
plugins {
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}

34
build.gradle.kts Normal file
View File

@ -0,0 +1,34 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
plugins {
kotlin("jvm") version "2.1.20"
}
allprojects {
apply(plugin="kotlin")
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
compilerOptions {
freeCompilerArgs = listOf("-Xwhen-guards")
jvmTarget = JvmTarget.JVM_11
}
sourceSets.all {
languageSettings {
// enable language features like so:
// enableLanguageFeature(LanguageFeature.WhenGuards.name)
}
}
}
java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11
}
}

View File

@ -1,43 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
// should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
}
// note: there are no unit tests in this module!

24
codeCore/build.gradle.kts Normal file
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.1")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
}
// note: there are no unit tests in this module!

View File

@ -0,0 +1,31 @@
package prog8.code
import java.io.IOException
import java.nio.file.Path
import kotlin.io.path.absolute
// the automatically generated module where all string literals are interned to:
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
// all automatically generated labels everywhere need to have the same label name prefix:
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
/**
* Returns the absolute path of the given path,
* where links are replaced by the actual directories,
* and containing no redundant path elements.
* If the path doesn't refer to an existing directory or file on the file system,
* it is returned unchanged.
*/
fun Path.sanitize(): Path {
return try {
this.toRealPath().normalize()
} catch (_: java.nio.file.NoSuchFileException) {
this.absolute().normalize()
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
} catch (iox: IOException) {
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
}
}

View File

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

View File

@ -1,172 +0,0 @@
package prog8.code.ast
import prog8.code.core.*
sealed interface IPtSubroutine {
val name: String
}
class PtAsmSub(
name: String,
val address: UInt?,
val clobbers: Set<CpuRegister>,
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
val inline: Boolean,
position: Position
) : PtNamedNode(name, position), IPtSubroutine
class PtSub(
name: String,
val parameters: List<PtSubroutineParameter>,
val returntype: DataType?,
position: Position
) : PtNamedNode(name, position), IPtSubroutine {
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes })
throw AssemblyError("non-numeric parameter")
if(returntype!=null && returntype !in NumericDatatypes)
throw AssemblyError("non-numeric returntype $returntype")
parameters.forEach { it.parent=this }
}
}
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
sealed interface IPtAssignment {
val children: MutableList<PtNode>
val target: PtAssignTarget
get() = children[0] as PtAssignTarget
val value: PtExpression
get() = children[1] as PtExpression
}
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
class PtAssignTarget(position: Position) : PtNode(position) {
val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier
val array: PtArrayIndexer?
get() = children.single() as? PtArrayIndexer
val memory: PtMemoryByte?
get() = children.single() as? PtMemoryByte
val type: DataType
get() {
return when(val tgt = children.single()) {
is PtIdentifier -> tgt.type
is PtArrayIndexer -> tgt.type
is PtMemoryByte -> tgt.type
else -> throw AssemblyError("weird target $tgt")
}
}
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this)
}
class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) {
val trueScope: PtNodeGroup
get() = children[0] as PtNodeGroup
val falseScope: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtForLoop(position: Position) : PtNode(position) {
val variable: PtIdentifier
get() = children[0] as PtIdentifier
val iterable: PtExpression
get() = children[1] as PtExpression
val statements: PtNodeGroup
get() = children[2] as PtNodeGroup
}
class PtIfElse(position: Position) : PtNode(position) {
val condition: PtExpression
get() = children[0] as PtExpression
val ifScope: PtNodeGroup
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup
get() = children[2] as PtNodeGroup
}
class PtJump(val identifier: PtIdentifier?,
val address: UInt?,
val generatedLabel: String?,
position: Position) : PtNode(position) {
init {
identifier?.let {it.parent = this }
}
}
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
val target: PtAssignTarget
get() = children.single() as PtAssignTarget
}
class PtRepeatLoop(position: Position) : PtNode(position) {
val count: PtExpression
get() = children[0] as PtExpression
val statements: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtReturn(position: Position) : PtNode(position) {
val hasValue = children.any()
val value: PtExpression?
get() {
return if(children.any())
children.single() as PtExpression
else
null
}
}
sealed interface IPtVariable {
val name: String
val type: DataType
}
class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
init {
value?.let {it.parent=this}
}
}
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable
class PtWhen(position: Position) : PtNode(position) {
val value: PtExpression
get() = children[0] as PtExpression
val choices: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
val values: PtNodeGroup
get() = children[0] as PtNodeGroup
val statements: PtNodeGroup
get() = children[1] as PtNodeGroup
}

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,41 @@ 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)
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 +55,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].isByte && 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,66 +86,67 @@ 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:
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
// cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow:
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"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),
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
"pokemon" 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),
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
"rsave" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
"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)),
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
"abs__word" to FSignature(true, BaseDataType.UWORD, 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("rol", "ror", "rol2", "ror2", "sort", "reverse")
val InplaceModifyingBuiltinFunctions = setOf(
"setlsb", "setmsb",
"rol", "ror", "rol2", "ror2",
"divmod", "divmod__ubyte", "divmod__uword"
)

View File

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

View File

@ -1,11 +1,17 @@
package prog8.code.core
import kotlin.math.abs
import kotlin.math.pow
val powersOfTwoFloat = (0..16).map { (2.0).pow(it) }.toTypedArray()
val negativePowersOfTwoFloat = powersOfTwoFloat.map { -it }.toTypedArray()
val powersOfTwoInt = (0..16).map { 2.0.pow(it).toInt() }.toTypedArray()
fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
@ -14,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')
}
}
@ -22,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')
}
}
@ -58,6 +65,7 @@ fun String.unescape(): String {
'\\' -> '\\'
'n' -> '\n'
'r' -> '\r'
't' -> '\t'
'"' -> '"'
'\'' -> '\''
'u' -> {

View File

@ -1,60 +1,241 @@
package prog8.code.core
enum class DataType {
UBYTE, // pass by value
BYTE, // pass by value
UWORD, // pass by value
WORD, // pass by value
FLOAT, // pass by value
BOOL, // pass by value
import java.util.*
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 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.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
UWORD -> targetType.oneOf(UWORD, FLOAT)
WORD -> targetType.oneOf(WORD, FLOAT)
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 ByteDatatypes -> false
this in WordDatatypes -> other in ByteDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> false
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.isArray -> 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 ByteDatatypes -> other in ByteDatatypes
this in WordDatatypes -> other in WordDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> true
this.isArray && other.isArray -> 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
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
init {
if(base.isArray) {
require(sub != null)
if(base.isSplitWordArray)
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
}
else if(base==BaseDataType.STR)
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
else
require(sub == null) { "only string and array base types can have a subtype"}
}
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 {
val UBYTE = DataType(BaseDataType.UBYTE, null)
val BYTE = DataType(BaseDataType.BYTE, null)
val UWORD = DataType(BaseDataType.UWORD, null)
val WORD = DataType(BaseDataType.WORD, null)
val LONG = DataType(BaseDataType.LONG, null)
val FLOAT = DataType(BaseDataType.FLOAT, null)
val BOOL = DataType(BaseDataType.BOOL, null)
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
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, BaseDataType.UBYTE),
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 && actualElementDt.isWord)
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
else {
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
DataType(BaseDataType.ARRAY, actualElementDt)
else
throw NoSuchElementException("invalid element dt $elementDt")
}
}
}
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!!)
else
throw IllegalArgumentException("not an array")
override fun toString(): String = when(base) {
BaseDataType.ARRAY -> {
when(sub) {
BaseDataType.BOOL -> "bool[]"
BaseDataType.FLOAT -> "float[]"
BaseDataType.BYTE -> "byte[]"
BaseDataType.WORD -> "word[]"
BaseDataType.UBYTE -> "ubyte[]"
BaseDataType.UWORD -> "uword[]"
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
BaseDataType.WORD -> "word[] (split)"
BaseDataType.UWORD -> "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) {
BaseDataType.UBYTE -> "ubyte["
BaseDataType.UWORD -> "@nosplit uword["
BaseDataType.BOOL -> "bool["
BaseDataType.BYTE -> "byte["
BaseDataType.WORD -> "@nosplit word["
BaseDataType.FLOAT -> "float["
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
BaseDataType.UWORD -> "uword["
BaseDataType.WORD -> "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 = base.largerSizeThan(other.base)
fun equalsSize(other: DataType): Boolean = 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 == BaseDataType.BOOL
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
val isFloatArray = base.isArray && sub == 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 == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
val isSplitWordArray = base.isSplitWordArray
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
val isIterable = base.isIterable
val isPassByRef = base.isPassByRef
val isPassByValue = base.isPassByValue
}
enum class CpuRegister {
A,
X,
@ -75,7 +256,14 @@ enum class RegisterOrPair {
R8, R9, R10, R11, R12, R13, R14, R15;
companion object {
val names by lazy { values().map { it.toString()} }
val names by lazy { entries.map { it.toString()} }
fun fromCpuRegister(cpu: CpuRegister): RegisterOrPair {
return when(cpu) {
CpuRegister.A -> A
CpuRegister.X -> X
CpuRegister.Y -> Y
}
}
}
fun asCpuRegister(): CpuRegister = when(this) {
@ -85,6 +273,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 {
@ -94,7 +293,7 @@ enum class Statusflag {
Pn; // don't use
companion object {
val names by lazy { values().map { it.toString()} }
val names by lazy { entries.map { it.toString()} }
}
}
@ -110,48 +309,9 @@ enum class BranchCondition {
PL, // PL == POS
POS,
VS,
VC,
VC
}
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL
)
val PassByValueDatatypes = NumericDatatypes
val 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
)
val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
@ -159,12 +319,17 @@ val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
)
val CpuRegisters = arrayOf(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
)
enum class OutputType {
RAW,
PRG,
XEX
XEX,
LIBRARY
}
enum class CbmPrgLauncherType {
@ -186,3 +351,9 @@ enum class ZeropageWish {
DONTCARE,
NOT_IN_ZEROPAGE
}
enum class SplitWish {
DONTCARE,
SPLIT,
NOSPLIT
}

View File

@ -1,10 +1,43 @@
package prog8.code.core
import java.nio.file.Path
enum class CpuType {
CPU6502,
CPU65C02,
VIRTUAL
}
interface ICompilationTarget: IStringEncoding, IMemSizer {
val name: String
val machine: IMachineDefinition
val supportedEncodings: Set<Encoding>
val defaultEncoding: Encoding
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
val BSSGOLDENRAM_END: UInt
val cpu: CpuType
var zeropage: Zeropage
var golden: GoldenRam
val libraryPath: Path?
val customLauncher: List<String>
val additionalAssemblerOptions: String?
val defaultOutputType: OutputType
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String
fun convertFloatToBytes(num: Double): List<UByte>
fun convertBytesToFloat(bytes: List<UByte>): Double
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
fun isIOAddress(address: UInt): Boolean
override fun encodeString(str: String, encoding: Encoding): List<UByte>
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String

View File

@ -3,11 +3,16 @@ package prog8.code.core
interface IErrorReporter {
fun err(msg: String, position: Position)
fun warn(msg: String, position: Position)
fun info(msg: String, position: Position)
fun undefined(symbol: List<String>, position: Position)
fun noErrors(): Boolean
fun report()
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
fun finalizeNumErrors(numErrors: Int, numWarnings: Int, numInfos: Int) {
if(numErrors>0)
throw ErrorsReportedException("There are $numErrors errors and $numWarnings warnings.")
throw ErrorsReportedException("There are $numErrors errors, $numWarnings warnings, and $numInfos infos.")
}
fun noErrorForLine(position: Position): Boolean
fun printSingleError(errormessage: String)
}

View File

@ -1,30 +0,0 @@
package prog8.code.core
import java.nio.file.Path
enum class CpuType {
CPU6502,
CPU65c02,
VIRTUAL
}
interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
val PROGRAM_LOAD_ADDRESS : UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
val cpu: CpuType
var zeropage: Zeropage
var golden: GoldenRam
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean
}

View File

@ -1,6 +1,15 @@
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: BaseDataType): Int {
if(dt.isPassByRef)
return memorySize(DataType.UWORD, null) // a pointer size
try {
return memorySize(DataType.forDt(dt), null)
} catch (x: NoSuchElementException) {
throw IllegalArgumentException(x.message)
}
}
}

View File

@ -5,10 +5,17 @@ enum class Encoding(val prefix: String) {
PETSCII("petscii"), // c64/c128/cx16
SCREENCODES("sc"), // c64/c128/cx16
ATASCII("atascii"), // atari
ISO("iso") // cx16
ISO("iso"), // cx16 (iso-8859-15)
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
CP437("cp437"), // cx16 (ibm pc, codepage 437)
KATAKANA("kata"), // cx16 (katakana)
C64OS("c64os") // c64 (C64 OS)
}
interface IStringEncoding {
val defaultEncoding: Encoding
fun encodeString(str: String, encoding: Encoding): List<UByte>
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
}

View File

@ -22,7 +22,7 @@ abstract class MemoryAllocator(protected val options: CompilationOptions) {
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
abstract val SCRATCH_B1 : UInt // temp storage for a single byte
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
@ -38,7 +38,14 @@ 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))
}
}
fun retainAllowed() {
synchronized(this) {
for(allowed in options.zpAllowed)
free.retainAll { it in allowed }
}
}
@ -63,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 IntegerDatatypes -> 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)
@ -73,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.FLOAT, null)
if(position!=null)
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
else
@ -87,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))
@ -111,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 -> 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")
}
}
@ -126,8 +133,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
require(size>0)
return free.containsAll((address until address+size.toUInt()).toList())
}
abstract fun allocateCx16VirtualRegisters()
}
@ -143,14 +148,13 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
val size: Int =
when (datatype) {
in IntegerDatatypes -> 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.FLOAT, null)
} else return Err(MemAllocationError("floating point option not enabled"))
}
else -> throw MemAllocationError("weird dt")

View File

@ -1,12 +1,10 @@
package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not")
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not")
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
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) {

View File

@ -1,21 +1,21 @@
package prog8.code.core
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import prog8.code.sanitize
import prog8.code.source.SourceCode
import java.nio.file.InvalidPathException
import kotlin.io.path.Path
import kotlin.io.path.absolute
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
fun toClickableStr(): String {
if(this===DUMMY)
return ""
if(file.startsWith(LIBRARYFILEPREFIX))
if(SourceCode.isLibraryResource(file))
return "$file:$line:$startCol:"
return try {
val path = Path(file).absolute().normalize().toString()
val path = Path(file).sanitize().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:"
}

View File

@ -0,0 +1,74 @@
package prog8.code.source
import prog8.code.core.Position
import prog8.code.sanitize
import java.nio.file.Path
import java.util.*
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 expandTilde(path: String): String = if (path.startsWith("~")) {
val userHome = System.getProperty("user.home")
userHome + path.drop(1)
} else {
path
}
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
val normalized = path.sanitize()
val cached = cache[normalized.toString()]
if (cached != null)
return cached
val file = SourceCode.File(normalized, isLibrary)
cache[normalized.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).sanitize()
val cached2 = cache[path.toString()]
if(cached2 != null)
return getLine(cached2, position.line)
throw NoSuchElementException("cannot get source line $position, with path $path")
}
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 = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER)
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
}

View File

@ -1,15 +1,13 @@
package prog8.code.core
package prog8.code.source
import java.io.File
import prog8.code.sanitize
import java.io.IOException
import java.nio.file.Path
import java.text.Normalizer
import kotlin.io.path.Path
import kotlin.io.path.absolute
import kotlin.io.path.readText
const val internedStringsModuleName = "prog8_interned_strings"
/**
* Encapsulates - and ties together - actual source code (=text) and its [origin].
*/
@ -25,6 +23,11 @@ sealed class SourceCode {
*/
abstract val isFromFilesystem: Boolean
/**
* Whether this [SourceCode] instance was created from a library module file
*/
abstract val isFromLibrary: Boolean
/**
* The logical name of the source code unit. Usually the module's name.
*/
@ -54,23 +57,32 @@ 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:"
val curdir: Path = Path(".").toAbsolutePath()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
fun isRegularFilesystemPath(pathString: String) =
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
private const val LIBRARYFILEPREFIX = "library:"
private const val STRINGSOURCEPREFIX = "string:"
val curdir: Path = Path(".").absolute()
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
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
}
}
/**
* Turn a plain String into a [SourceCode] object.
* [origin] will be something like `string:44c56085`.
*/
class Text(override val text: String): SourceCode() {
class Text(origText: String): SourceCode() {
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
override val isFromResources = false
override val isFromFilesystem = false
override val isFromLibrary = false
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
override val name = "<unnamed-text>"
}
@ -78,12 +90,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, override val isFromLibrary: Boolean): SourceCode() {
override val text: String
override val origin: String
override val name: String
@ -94,7 +107,8 @@ sealed class SourceCode {
val normalized = path.normalize()
origin = relative(normalized).toString()
try {
text = normalized.readText()
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
name = normalized.toFile().nameWithoutExtension
} catch (nfx: java.nio.file.NoSuchFileException) {
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
@ -106,12 +120,14 @@ 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
override val isFromFilesystem = false
override val isFromLibrary = true
override val origin = "$LIBRARYFILEPREFIX$normalized"
override val text: String
override val name: String
@ -121,12 +137,13 @@ 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"
)
}
val stream = object {}.javaClass.getResourceAsStream(normalized)
text = stream!!.reader().use { it.readText() }
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
name = Path(pathString).toFile().nameWithoutExtension
}
}
@ -137,37 +154,8 @@ sealed class SourceCode {
class Generated(override val name: String) : SourceCode() {
override val isFromResources: Boolean = false
override val isFromFilesystem: Boolean = false
override val isFromLibrary: Boolean = false
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
}
}

View File

@ -1,28 +0,0 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.target.atari.AtariMachineDefinition
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = AtariMachineDefinition()
override val supportedEncodings = setOf(Encoding.ATASCII)
override val defaultEncoding = Encoding.ATASCII
companion object {
const val NAME = "atari"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -1,20 +1,81 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.c128.C128MachineDefinition
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.core.*
import prog8.code.target.encodings.Encoder
import prog8.code.target.zp.C128Zeropage
import java.nio.file.Path
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
class C128Target: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
override val name = NAME
override val machine = C128MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object {
const val NAME = "c128"
}
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) {
System.err.println("The c128 target only supports the main emulator (Vice).")
return
}
if(!quiet)
println("\nStarting C-128 emulator x128...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C128Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
}
}

View File

@ -1,22 +1,111 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.c64.C64MachineDefinition
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.core.*
import prog8.code.target.encodings.Encoder
import prog8.code.target.zp.C64Zeropage
import java.io.IOException
import java.nio.file.Path
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
class C64Target: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
override val name = NAME
override val machine = C64MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object {
const val NAME = "c64"
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
}
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val 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 = 0xcfdfu
override val BSSGOLDENRAM_START = 0u // no golden ram on C64
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) {
System.err.println("The c64 target only supports the main emulator (Vice).")
return
}
for(emulator in listOf("x64sc", "x64")) {
if(!quiet)
println("\nStarting C-64 emulator $emulator...")
val viceMonlist = viceMonListName(programNameWithPath.toString())
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process: Process
try {
process=processb.start()
} catch(_: IOException) {
continue // try the next emulator executable
}
process.waitFor()
break
}
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}
val CompilationTargets = listOf(
C64Target.NAME,
C128Target.NAME,
Cx16Target.NAME,
PETTarget.NAME,
VMTarget.NAME
)
fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
C64Target.NAME -> C64Target()
C128Target.NAME -> C128Target()
Cx16Target.NAME -> Cx16Target()
PETTarget.NAME -> PETTarget()
VMTarget.NAME -> VMTarget()
else -> throw IllegalArgumentException("invalid compilation target")
}

View File

@ -0,0 +1,173 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.source.ImportFileSystem.expandTilde
import prog8.code.target.encodings.Encoder
import prog8.code.target.zp.ConfigurableZeropage
import java.io.IOException
import java.nio.file.Path
import java.util.*
import kotlin.io.path.Path
import kotlin.io.path.inputStream
import kotlin.io.path.isDirectory
import kotlin.io.path.nameWithoutExtension
class ConfigFileTarget(
override val name: String,
override val defaultEncoding: Encoding,
override val cpu: CpuType,
override val PROGRAM_LOAD_ADDRESS: UInt,
override val PROGRAM_MEMTOP_ADDRESS: UInt,
override val STARTUP_CODE_RESERVED_SIZE: UInt,
override val BSSHIGHRAM_START: UInt,
override val BSSHIGHRAM_END: UInt,
override val BSSGOLDENRAM_START: UInt,
override val BSSGOLDENRAM_END: UInt,
override val defaultOutputType: OutputType,
override val libraryPath: Path,
override val customLauncher: List<String>,
override val additionalAssemblerOptions: String?,
val ioAddresses: List<UIntRange>,
val zpScratchB1: UInt,
val zpScratchReg: UInt,
val zpScratchW1: UInt,
val zpScratchW2: UInt,
val virtualregistersStart: UInt,
val zpFullsafe: List<UIntRange>,
val zpKernalsafe: List<UIntRange>,
val zpBasicsafe: List<UIntRange>
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
companion object {
private fun Properties.getString(property: String): String {
val value = this.getProperty(property, null)
if(value!=null)
return value
throw NoSuchElementException("string property '$property' not found in config file")
}
private fun Properties.getInteger(property: String): UInt {
val value = this.getProperty(property, null)
if(value!=null) return parseInt(value)
throw NoSuchElementException("integer property '$property' not found in config file")
}
private fun parseInt(value: String): UInt {
if(value.startsWith("0x"))
return value.drop(2).toUInt(16)
if(value.startsWith("$"))
return value.drop(1).toUInt(16)
if(value.startsWith("%"))
return value.drop(1).toUInt(2)
return value.toUInt()
}
private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> {
val rangesStr = props.getString(key)
if(rangesStr.isBlank())
return emptyList()
val result = mutableListOf<UIntRange>()
val ranges = rangesStr.split(",").map { it.trim() }
for(r in ranges) {
if ('-' in r) {
val (fromStr, toStr) = r.split("-")
val from = parseInt(fromStr.trim())
val to = parseInt(toStr.trim())
result.add(from..to)
} else {
val address = parseInt(r)
result.add(address..address)
}
}
return result
}
fun fromConfigFile(configfile: Path): ConfigFileTarget {
val props = Properties()
props.load(configfile.inputStream())
val cpuString = props.getString("cpu").uppercase()
val cpuType = try {
CpuType.valueOf(cpuString)
} catch (_: IllegalArgumentException) {
CpuType.valueOf("CPU$cpuString")
}
val ioAddresses = parseAddressRanges("io_regions", props)
val zpFullsafe = parseAddressRanges("zp_fullsafe", props)
val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props)
val zpBasicsafe = parseAddressRanges("zp_basicsafe", props)
val libraryPath = expandTilde(Path(props.getString("library")))
if(!libraryPath.isDirectory())
throw IOException("invalid library path: $libraryPath")
val customLauncherStr = props.getProperty("custom_launcher_code", null)
val customLauncher =
if(customLauncherStr?.isNotBlank()==true)
(customLauncherStr+"\n").lines().map { it.trimEnd() }
else emptyList()
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
val outputTypeString = props.getProperty("output_type", "PRG")
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
return ConfigFileTarget(
configfile.nameWithoutExtension,
Encoding.entries.first { it.prefix==props.getString("encoding") },
cpuType,
props.getInteger("load_address"),
props.getInteger("memtop"),
0u, // used only in a very specific error condition check in a certain scenario...
props.getInteger("bss_highram_start"),
props.getInteger("bss_highram_end"),
props.getInteger("bss_goldenram_start"),
props.getInteger("bss_goldenram_end"),
defaultOutputType,
libraryPath,
customLauncher,
assemblerOptions,
ioAddresses,
props.getInteger("zp_scratch_b1"),
props.getInteger("zp_scratch_reg"),
props.getInteger("zp_scratch_w1"),
props.getInteger("zp_scratch_w2"),
props.getInteger("virtual_registers"),
zpFullsafe,
zpKernalsafe,
zpBasicsafe,
)
}
}
// TODO floats are not yet supported here, just enter some values
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 8
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam // TODO this is not yet used
override fun getFloatAsmBytes(num: Number) = TODO("floats")
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
}
override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it }
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = ConfigurableZeropage(
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
virtualregistersStart,
zpBasicsafe,
zpKernalsafe,
zpFullsafe,
compilerOptions
)
// note: there's no golden ram yet
}
}

View File

@ -1,20 +1,94 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.target.cx16.CX16MachineDefinition
import prog8.code.core.*
import prog8.code.target.encodings.Encoder
import prog8.code.target.zp.CX16Zeropage
import java.nio.file.Path
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
class Cx16Target: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
override val name = NAME
override val machine = CX16MachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES, Encoding.ISO)
override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object {
const val NAME = "cx16"
}
override val cpu = CpuType.CPU65C02
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
override val BSSGOLDENRAM_START = 0x0400u
override val BSSGOLDENRAM_END = 0x07ffu
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
val emulator: String
val extraArgs: List<String>
when(selectedEmulator) {
1 -> {
emulator = "x16emu"
extraArgs = listOf("-debug")
}
2 -> {
emulator = "box16"
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
}
else -> {
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
return
}
}
if(!quiet)
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
}
}

View File

@ -1,10 +1,9 @@
package prog8.code.target.cbm
package prog8.code.target
import prog8.code.core.InternalCompilerException
import kotlin.math.absoluteValue
import kotlin.math.pow
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
companion object {

View File

@ -0,0 +1,34 @@
package prog8.code.target
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.IMemSizer
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
if(numElements==null) return 2 // treat it as a pointer size
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
BaseDataType.FLOAT-> numElements * floatsize
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
else -> throw IllegalArgumentException("invalid sub type")
}
}
else if (dt.isString) {
return numElements // treat it as the size of the given string with the length
?: 2 // treat it as the size to store a string pointer
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> floatsize * (numElements ?: 1)
dt.isLong -> 4 * (numElements ?: 1)
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
else -> 2 * (numElements ?: 1)
}
}
}

View File

@ -1,20 +1,80 @@
package prog8.code.target
import prog8.code.core.Encoding
import prog8.code.core.ICompilationTarget
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.target.pet.PETMachineDefinition
import prog8.code.core.*
import prog8.code.target.encodings.Encoder
import prog8.code.target.zp.PETZeropage
import java.nio.file.Path
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
class PETTarget: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
override val name = NAME
override val machine = PETMachineDefinition()
override val supportedEncodings = setOf(Encoding.PETSCII, Encoding.SCREENCODES)
override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object {
const val NAME = "pet32"
}
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val 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
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) {
System.err.println("The pet target only supports the main emulator (Vice).")
return
}
if(!quiet)
println("\nStarting PET emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process=processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = PETZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}

View File

@ -1,27 +1,111 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.target.virtual.VirtualMachineDefinition
import prog8.code.target.encodings.Encoder
import java.nio.file.Path
import kotlin.io.path.isReadable
import kotlin.io.path.name
import kotlin.io.path.readText
class VMTarget: ICompilationTarget,
IStringEncoding by Encoder(false),
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = VirtualMachineDefinition()
override val supportedEncodings = setOf(Encoding.ISO)
override val defaultEncoding = Encoding.ISO
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object {
const val NAME = "virtual"
const val FLOAT_MEM_SIZE = 8 // 64-bits double
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
override val cpu = CpuType.VIRTUAL
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
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
override val BSSGOLDENRAM_START = 0u // not actually used
override val BSSGOLDENRAM_END = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used
override lateinit var golden: GoldenRam // not actually used
override fun getFloatAsmBytes(num: Number): String {
// little endian binary representation
val bits = num.toDouble().toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { "$$it" }
return parts.joinToString(", ")
}
override fun convertFloatToBytes(num: Double): List<UByte> {
val bits = num.toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
return parts
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==8) { "need 8 bytes" }
val b0 = bytes[0].toLong() shl (8*7)
val b1 = bytes[1].toLong() shl (8*6)
val b2 = bytes[2].toLong() shl (8*5)
val b3 = bytes[3].toLong() shl (8*4)
val b4 = bytes[4].toLong() shl (8*3)
val b5 = bytes[5].toLong() shl (8*2)
val b6 = bytes[6].toLong() shl (8*1)
val b7 = bytes[7].toLong() shl (8*0)
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(!quiet)
println("\nStarting Virtual Machine...")
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name
if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText(), quiet)
} else {
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
if(withExt.isReadable())
vm.runProgram(withExt.readText(), quiet)
else
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}
override fun isIOAddress(address: UInt): Boolean = false
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = VirtualZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}
interface IVirtualMachineRunner {
fun runProgram(irSource: String, quiet: Boolean)
}
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
override val SCRATCH_B1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_REG: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W2: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
}

View File

@ -1,63 +0,0 @@
package prog8.code.target.atari
import prog8.code.core.*
import java.nio.file.Path
class AtariMachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6
override val PROGRAM_LOAD_ADDRESS = 0x2000u
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.output == OutputType.XEX)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
val emulatorName: String
val cmdline: List<String>
when(selectedEmulator) {
1 -> {
emulatorName = "atari800"
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
}
2 -> {
emulatorName = "altirra"
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
}
else -> {
System.err.println("Atari target only supports atari800 and altirra emulators.")
return
}
}
// TODO monlist?
println("\nStarting Atari800XL emulator $emulatorName...")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = AtariZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}

View File

@ -1,56 +0,0 @@
package prog8.code.target.atari
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
init {
if (options.floats) {
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
ZeropageType.DONTUSE
))
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
when (options.zeropage) {
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
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x80u..0xffu) // TODO
}
ZeropageType.BASICSAFE,
ZeropageType.FLOATSAFE -> {
free.addAll(0x80u..0xffu) // TODO
free.removeAll(0xd4u .. 0xefu) // floating point storage
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
}
override fun allocateCx16VirtualRegisters() {
TODO("Not known if atari can put the virtual regs in ZP")
}
}

View File

@ -1,54 +0,0 @@
package prog8.code.target.c128
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class C128MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The c128 target only supports the main emulator (Vice).")
return
}
println("\nStarting C-128 emulator x128...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C128Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
}
}

View File

@ -1,64 +0,0 @@
package prog8.code.target.c64
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.io.IOException
import java.nio.file.Path
class C64MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val BSSHIGHRAM_START = 0xc000u
override val BSSHIGHRAM_END = 0xd000u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The c64 target only supports the main emulator (Vice).")
return
}
for(emulator in listOf("x64sc", "x64")) {
println("\nStarting C-64 emulator $emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process
try {
process=processb.start()
} catch(x: IOException) {
continue // try the next emulator executable
}
process.waitFor()
break
}
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
}
}

View File

@ -1,18 +0,0 @@
package prog8.code.target.cbm
import prog8.code.core.*
internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -1,66 +0,0 @@
package prog8.code.target.cx16
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class CX16MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU65c02
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib")
else
emptyList()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
val emulator: String
val extraArgs: List<String>
when(selectedEmulator) {
1 -> {
emulator = "x16emu"
extraArgs = emptyList()
}
2 -> {
emulator = "box16"
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
}
else -> {
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
return
}
}
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline).inheritIO()
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
}
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.cbm
package prog8.code.target.encodings
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
@ -26,7 +26,7 @@ object AtasciiEncoding {
'▖',
// $10
'♣',
'♣',
'┌',
'─',
'┼',
@ -62,7 +62,7 @@ object AtasciiEncoding {
'/',
// $30
'0',
'0',
'1',
'2',
'3',
@ -80,7 +80,7 @@ object AtasciiEncoding {
'?',
// $40
'@',
'@',
'A',
'B',
'C',
@ -197,6 +197,7 @@ object AtasciiEncoding {
fun encode(str: String): Result<List<UByte>, CharConversionException> {
val mapped = str.map { chr ->
when (chr) {
'\r' -> 0x9bu
'\u0000' -> 0u
in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly

View File

@ -0,0 +1,327 @@
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import java.io.CharConversionException
object C64osEncoding {
// decoding: from C64 OS Screencodes (0-255) to unicode
// character table from:
// https://www.c64os.com/c64os/usersguide/appendices#charactersets
private val decodingC64os = charArrayOf(
'@' , // @ 0x00 -> COMMERCIAL AT
'a' , // a 0x01 -> LATIN SMALL LETTER A
'b' , // b 0x02 -> LATIN SMALL LETTER B
'c' , // c 0x03 -> LATIN SMALL LETTER C
'd' , // d 0x04 -> LATIN SMALL LETTER D
'e' , // e 0x05 -> LATIN SMALL LETTER E
'f' , // f 0x06 -> LATIN SMALL LETTER F
'g' , // g 0x07 -> LATIN SMALL LETTER G
'h' , // h 0x08 -> LATIN SMALL LETTER H
'i' , // i 0x09 -> LATIN SMALL LETTER I
'j' , // j 0x0A -> LATIN SMALL LETTER J
'k' , // k 0x0B -> LATIN SMALL LETTER K
'l' , // l 0x0C -> LATIN SMALL LETTER L
'm' , // m 0x0D -> LATIN SMALL LETTER M
'n' , // n 0x0E -> LATIN SMALL LETTER N
'o' , // o 0x0F -> LATIN SMALL LETTER O
'p' , // p 0x10 -> LATIN SMALL LETTER P
'q' , // q 0x11 -> LATIN SMALL LETTER Q
'r' , // r 0x12 -> LATIN SMALL LETTER R
's' , // s 0x13 -> LATIN SMALL LETTER S
't' , // t 0x14 -> LATIN SMALL LETTER T
'u' , // u 0x15 -> LATIN SMALL LETTER U
'v' , // v 0x16 -> LATIN SMALL LETTER V
'w' , // w 0x17 -> LATIN SMALL LETTER W
'x' , // x 0x18 -> LATIN SMALL LETTER X
'y' , // y 0x19 -> LATIN SMALL LETTER Y
'z' , // z 0x1A -> LATIN SMALL LETTER Z
'[' , // [ 0x1B -> LEFT SQUARE BRACKET
'\\' , // \ 0x1C -> REVERSE SOLIDUS
']' , // ] 0x1D -> RIGHT SQUARE BRACKET
'^' , // ^ 0x1E -> CIRCUMFLEX
'_' , // _ 0x1F -> UNDERSCORE
' ' , // 0x20 -> SPACE
'!' , // ! 0x21 -> EXCLAMATION MARK
'"' , // " 0x22 -> QUOTATION MARK
'#' , // # 0x23 -> NUMBER SIGN
'$' , // $ 0x24 -> DOLLAR SIGN
'%' , // % 0x25 -> PERCENT SIGN
'&' , // & 0x26 -> AMPERSAND
'\'' , // ' 0x27 -> APOSTROPHE
'(' , // ( 0x28 -> LEFT PARENTHESIS
')' , // ) 0x29 -> RIGHT PARENTHESIS
'*' , // * 0x2A -> ASTERISK
'+' , // + 0x2B -> PLUS SIGN
',' , // , 0x2C -> COMMA
'-' , // - 0x2D -> HYPHEN-MINUS
'.' , // . 0x2E -> FULL STOP
'/' , // / 0x2F -> SOLIDUS
'0' , // 0 0x30 -> DIGIT ZERO
'1' , // 1 0x31 -> DIGIT ONE
'2' , // 2 0x32 -> DIGIT TWO
'3' , // 3 0x33 -> DIGIT THREE
'4' , // 4 0x34 -> DIGIT FOUR
'5' , // 5 0x35 -> DIGIT FIVE
'6' , // 6 0x36 -> DIGIT SIX
'7' , // 7 0x37 -> DIGIT SEVEN
'8' , // 8 0x38 -> DIGIT EIGHT
'9' , // 9 0x39 -> DIGIT NINE
':' , // : 0x3A -> COLON
';' , // ; 0x3B -> SEMICOLON
'<' , // < 0x3C -> LESS-THAN SIGN
'=' , // = 0x3D -> EQUALS SIGN
'>' , // > 0x3E -> GREATER-THAN SIGN
'?' , // ? 0x3F -> QUESTION MARK
'`' , // ` 0x40 -> GRAVE ACCENT
'A' , // A 0x41 -> LATIN CAPITAL LETTER A
'B' , // B 0x42 -> LATIN CAPITAL LETTER B
'C' , // C 0x43 -> LATIN CAPITAL LETTER C
'D' , // D 0x44 -> LATIN CAPITAL LETTER D
'E' , // E 0x45 -> LATIN CAPITAL LETTER E
'F' , // F 0x46 -> LATIN CAPITAL LETTER F
'G' , // G 0x47 -> LATIN CAPITAL LETTER G
'H' , // H 0x48 -> LATIN CAPITAL LETTER H
'I' , // I 0x49 -> LATIN CAPITAL LETTER I
'J' , // J 0x4A -> LATIN CAPITAL LETTER J
'K' , // K 0x4B -> LATIN CAPITAL LETTER K
'L' , // L 0x4C -> LATIN CAPITAL LETTER L
'M' , // M 0x4D -> LATIN CAPITAL LETTER M
'N' , // N 0x4E -> LATIN CAPITAL LETTER N
'O' , // O 0x4F -> LATIN CAPITAL LETTER O
'P' , // P 0x50 -> LATIN CAPITAL LETTER P
'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
'R' , // R 0x52 -> LATIN CAPITAL LETTER R
'S' , // S 0x53 -> LATIN CAPITAL LETTER S
'T' , // T 0x54 -> LATIN CAPITAL LETTER T
'U' , // U 0x55 -> LATIN CAPITAL LETTER U
'V' , // V 0x56 -> LATIN CAPITAL LETTER V
'W' , // W 0x57 -> LATIN CAPITAL LETTER W
'X' , // X 0x58 -> LATIN CAPITAL LETTER X
'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
'{' , // { 0x5B -> LEFT BRACE
'|' , // | 0x5C -> VERTICAL BAR
'}' , // } 0x5D -> RIGHT BRACE
'~' , // ~ 0x5E -> TILDE
'\ufffe', // 0x5F -> RESERVED
'\u00a0', // 0x60 -> NO-BREAK SPACE (TRANSPARENT)
'\ufffe', // 0x61 -> COMMODORE SYMBOL
'\u2191', // ↑ 0x62 -> UP ARROW
'\u2193', // ↓ 0x63 -> DOWN ARROW
'\u2190', // ← 0x64 -> LEFT ARROW
'\u2192', // → 0x65 -> RIGHT ARROW
'\u231A', // ⌚ 0x66 -> WATCH (ANALOG CLOCKFACE)
'\u21BB', // ↻ 0x67 -> CYCLE ARROWS
'\u2026', // … 0x68 -> ELLIPSIS
'\u25a7', // ▧ 0x69 -> DIAGNONAL STRIPES
'\u2610', // ☐ 0x6A -> CHECKBOX UNCHECKED
'\u2611', // ☑ 0x6B -> CHECKBOX CHECKED
'\ufffe', // 0x6C -> RADIO BUTTON UNSELECTED
'\ufffe', // 0x6D -> RADIO BUTTON SELECTED
'\ufffe', // 0x6E -> UTILITY CLOSE BUTTON
'\ufffe', // 0x6F -> UTILITY TITLE BAR
'\u00a9', // © 0x70 -> COPYRIGHT
'\u2713', // ✓ 0x71 -> CHECKMARK
'\u2261', // ≡ 0x72 -> THREE HORIZONTAL STRIPES
'\ufffe', // 0x73 -> TICK TRACK
'\ufffe', // 0x74 -> TICK TRACK NUB
'\ufffe', // 0x75 -> TAB CORNER
'\u2980', // ⦀ 0x76 -> THREE VERTICAL STRIPES
'\ufffe', // 0x77 -> CUSTOM 1
'\ufffe', // 0x78 -> CUSTOM 2
'\ufffe', // 0x79 -> CUSTOM 3
'\ufffe', // 0x7A -> CUSTOM 4
'\ufffe', // 0x7B -> CUSTOM 5
'\ufffe', // 0x7C -> CUSTOM 6
'\ufffe', // 0x7D -> CUSTOM 7
'\ufffe', // 0x7E -> CUSTOM 8
'\ufffe', // 0x7F -> CUSTOM 9
'\ufffe', // 0x80 -> REVERSED COMMERCIAL AT
'\ufffe', // 0x81 -> REVERSED LATIN SMALL LETTER A
'\ufffe', // 0x82 -> REVERSED LATIN SMALL LETTER B
'\ufffe', // 0x83 -> REVERSED LATIN SMALL LETTER C
'\ufffe', // 0x84 -> REVERSED LATIN SMALL LETTER D
'\ufffe', // 0x85 -> REVERSED LATIN SMALL LETTER E
'\ufffe', // 0x86 -> REVERSED LATIN SMALL LETTER F
'\ufffe', // 0x87 -> REVERSED LATIN SMALL LETTER G
'\ufffe', // 0x88 -> REVERSED LATIN SMALL LETTER H
'\ufffe', // 0x89 -> REVERSED LATIN SMALL LETTER I
'\ufffe', // 0x8A -> REVERSED LATIN SMALL LETTER J
'\ufffe', // 0x8B -> REVERSED LATIN SMALL LETTER K
'\ufffe', // 0x8C -> REVERSED LATIN SMALL LETTER L
'\ufffe', // 0x8D -> REVERSED LATIN SMALL LETTER M
'\ufffe', // 0x8E -> REVERSED LATIN SMALL LETTER N
'\ufffe', // 0x8F -> REVERSED LATIN SMALL LETTER O
'\ufffe', // 0x90 -> REVERSED LATIN SMALL LETTER P
'\ufffe', // 0x91 -> REVERSED LATIN SMALL LETTER Q
'\ufffe', // 0x92 -> REVERSED LATIN SMALL LETTER R
'\ufffe', // 0x93 -> REVERSED LATIN SMALL LETTER S
'\ufffe', // 0x94 -> REVERSED LATIN SMALL LETTER T
'\ufffe', // 0x95 -> REVERSED LATIN SMALL LETTER U
'\ufffe', // 0x96 -> REVERSED LATIN SMALL LETTER V
'\ufffe', // 0x97 -> REVERSED LATIN SMALL LETTER W
'\ufffe', // 0x98 -> REVERSED LATIN SMALL LETTER X
'\ufffe', // 0x99 -> REVERSED LATIN SMALL LETTER Y
'\ufffe', // 0x9A -> REVERSED LATIN SMALL LETTER Z
'\ufffe', // 0x9B -> REVERSED LEFT SQUARE BRACKET
'\ufffe', // 0x9C -> REVERSED REVERSE SOLIDUS
'\ufffe', // 0x9D -> REVERSED RIGHT SQUARE BRACKET
'\ufffe', // 0x9E -> REVERSED CIRCUMFLEX
'\ufffe', // 0x9F -> REVERSED UNDERSCORE
'\ufffe', // 0xA0 -> REVERSED SPACE
'\ufffe', // 0xA1 -> REVERSED EXCLAMATION MARK
'\ufffe', // 0xA2 -> REVERSED QUOTATION MARK
'\ufffe', // 0xA3 -> REVERSED NUMBER SIGN
'\ufffe', // 0xA4 -> REVERSED DOLLAR SIGN
'\ufffe', // 0xA5 -> REVERSED PERCENT SIGN
'\ufffe', // 0xA6 -> REVERSED AMPERSAND
'\ufffe', // 0xA7 -> REVERSED APOSTROPHE
'\ufffe', // 0xA8 -> REVERSED LEFT PARENTHESIS
'\ufffe', // 0xA9 -> REVERSED RIGHT PARENTHESIS
'\ufffe', // 0xAA -> REVERSED ASTERISK
'\ufffe', // 0xAB -> REVERSED PLUS SIGN
'\ufffe', // 0xAC -> REVERSED COMMA
'\ufffe', // 0xAD -> REVERSED HYPHEN-MINUS
'\ufffe', // 0xAE -> REVERSED FULL STOP
'\ufffe', // 0xAF -> REVERSED SOLIDUS
'\ufffe', // 0xB0 -> REVERSED DIGIT ZERO
'\ufffe', // 0xB1 -> REVERSED DIGIT ONE
'\ufffe', // 0xB2 -> REVERSED DIGIT TWO
'\ufffe', // 0xB3 -> REVERSED DIGIT THREE
'\ufffe', // 0xB4 -> REVERSED DIGIT FOUR
'\ufffe', // 0xB5 -> REVERSED DIGIT FIVE
'\ufffe', // 0xB6 -> REVERSED DIGIT SIX
'\ufffe', // 0xB7 -> REVERSED DIGIT SEVEN
'\ufffe', // 0xB8 -> REVERSED DIGIT EIGHT
'\ufffe', // 0xB9 -> REVERSED DIGIT NINE
'\ufffe', // 0xBA -> REVERSED COLON
'\ufffe', // 0xBB -> REVERSED SEMICOLON
'\ufffe', // 0xBC -> REVERSED LESS-THAN SIGN
'\ufffe', // 0xBD -> REVERSED EQUALS SIGN
'\ufffe', // 0xBE -> REVERSED GREATER-THAN SIGN
'\ufffe', // 0xBF -> REVERSED QUESTION MARK
'\ufffe', // 0xC0 -> REVERSED GRAVE ACCENT
'\ufffe', // 0xC1 -> REVERSED LATIN CAPITAL LETTER A
'\ufffe', // 0xC2 -> REVERSED LATIN CAPITAL LETTER B
'\ufffe', // 0xC3 -> REVERSED LATIN CAPITAL LETTER C
'\ufffe', // 0xC4 -> REVERSED LATIN CAPITAL LETTER D
'\ufffe', // 0xC5 -> REVERSED LATIN CAPITAL LETTER E
'\ufffe', // 0xC6 -> REVERSED LATIN CAPITAL LETTER F
'\ufffe', // 0xC7 -> REVERSED LATIN CAPITAL LETTER G
'\ufffe', // 0xC8 -> REVERSED LATIN CAPITAL LETTER H
'\ufffe', // 0xC9 -> REVERSED LATIN CAPITAL LETTER I
'\ufffe', // 0xCA -> REVERSED LATIN CAPITAL LETTER J
'\ufffe', // 0xCB -> REVERSED LATIN CAPITAL LETTER K
'\ufffe', // 0xCC -> REVERSED LATIN CAPITAL LETTER L
'\ufffe', // 0xCD -> REVERSED LATIN CAPITAL LETTER M
'\ufffe', // 0xCE -> REVERSED LATIN CAPITAL LETTER N
'\ufffe', // 0xCF -> REVERSED LATIN CAPITAL LETTER O
'\ufffe', // 0xD0 -> REVERSED LATIN CAPITAL LETTER P
'\ufffe', // 0xD1 -> REVERSED LATIN CAPITAL LETTER Q
'\ufffe', // 0xD2 -> REVERSED LATIN CAPITAL LETTER R
'\ufffe', // 0xD3 -> REVERSED LATIN CAPITAL LETTER S
'\ufffe', // 0xD4 -> REVERSED LATIN CAPITAL LETTER T
'\ufffe', // 0xD5 -> REVERSED LATIN CAPITAL LETTER U
'\ufffe', // 0xD6 -> REVERSED LATIN CAPITAL LETTER V
'\ufffe', // 0xD7 -> REVERSED LATIN CAPITAL LETTER W
'\ufffe', // 0xD8 -> REVERSED LATIN CAPITAL LETTER X
'\ufffe', // 0xD9 -> REVERSED LATIN CAPITAL LETTER Y
'\ufffe', // 0xDA -> REVERSED LATIN CAPITAL LETTER Z
'\ufffe', // 0xDB -> REVERSED LEFT BRACE
'\ufffe', // 0xDC -> REVERSED VERTICAL BAR
'\ufffe', // 0xDD -> REVERSED RIGHT BRACE
'\ufffe', // 0xDE -> REVERSED TILDE
'\ufffe', // 0xDF -> RESERVED
'\ufffe', // 0xE0 -> RESERVED
'\ufffe', // 0xE1 -> REVERSED COMMODORE SYMBOL
'\ufffe', // 0xE2 -> REVERSED UP ARROW
'\ufffe', // 0xE3 -> REVERSED DOWN ARROW
'\ufffe', // 0xE4 -> REVERSED LEFT ARROW
'\ufffe', // 0xE5 -> REVERSED RIGHT ARROW
'\ufffe', // 0xE6 -> REVERSED ANALOG CLOCKFACE
'\ufffe', // 0xE7 -> REVERSED CYCLE ARROWS
'\ufffe', // 0xE8 -> REVERSED ELLIPSIS
'\ufffe', // 0xE9 -> REVERSED DIAGONAL STRIPES
'\ufffe', // 0xEA -> REVERSED CHECKBOX UNCHECKED
'\ufffe', // 0xEB -> REVERSED CHECKBOX CHECKED
'\ufffe', // 0xEC -> REVERSED RADIO BUTTON UNSELECTED
'\ufffe', // 0xED -> REVERSED RADIO BUTTON SELECTED
'\ufffe', // 0xEE -> MEMORY CHIP ICON
'\u21e7', // ⇧ 0xEF -> SHIFT SYMBOL
'\ufffe', // 0xF0 -> REVERSED COPYRIGHT SYMBOL
'\ufffe', // 0xF1 -> REVERSED CHECKMARK
'\ufffe', // 0xF2 -> REVERSED THREE HORIZONTAL STRIPES
'\ufffe', // 0xF3 -> REVERSED TICK TRACK
'\ufffe', // 0xF4 -> REVERSED TICK TRACK NUB
'\ufffe', // 0xF5 -> REVERSED TAB CORNER
'\ufffe', // 0xF6 -> REVERSED THREE VERTICAL STRIPES
'\ufffe', // 0xF7 -> CUSTOM 10
'\ufffe', // 0xF8 -> CUSTOM 11
'\ufffe', // 0xF9 -> CUSTOM 12
'\ufffe', // 0xFA -> CUSTOM 13
'\ufffe', // 0xFB -> CUSTOM 14
'\ufffe', // 0xFC -> CUSTOM 15
'\ufffe', // 0xFD -> CUSTOM 16
'\ufffe', // 0xFE -> CUSTOM 17
'\ufffe' // 0xFF -> CUSTOM 18
)
// encoding: from unicode to C64 OS Screencodes (0-255)
private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index}
private fun replaceSpecial(chr: Char): Char =
when(chr) {
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
else -> chr
}
fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
val chr = replaceSpecial(chr3)
val screencode = encodingC64os[chr]
return screencode?.toUByte() ?: when (chr) {
'\u0000' -> 0u
'\n' -> 13u
in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte()
}
else -> {
if(chr.isISOControl())
throw CharConversionException("no c64os character for char #${chr.code}")
else
throw CharConversionException("no c64os character for char #${chr.code} '${chr}'")
}
}
}
return try {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
encodeChar(it, !lowercase)
}
})
} catch(cx: CharConversionException) {
Err(cx)
}
}
fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
return try {
Ok(screencode.map {
val code = it.toInt()
if(code<0 || code>= decodingC64os.size)
throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}")
decodingC64os[code]
}.joinToString(""))
} catch(ce: CharConversionException) {
Err(ce)
}
}
}

View File

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

View File

@ -1,21 +1,24 @@
package prog8.code.target
package prog8.code.target.encodings
import com.github.michaelbull.result.fold
import prog8.code.core.Encoding
import prog8.code.core.IStringEncoding
import prog8.code.core.InternalCompilerException
import prog8.code.target.cbm.AtasciiEncoding
import prog8.code.target.cbm.IsoEncoding
import prog8.code.target.cbm.PetsciiEncoding
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
override val defaultEncoding: Encoding = Encoding.ISO
object Encoder: IStringEncoding {
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
val coded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
Encoding.ISO -> IsoEncoding.encode(str)
Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
Encoding.CP437 -> Cp437Encoding.encode(str)
Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
Encoding.ATASCII -> AtasciiEncoding.encode(str)
Encoding.C64OS -> C64osEncoding.encode(str)
else -> throw InternalCompilerException("unsupported encoding $encoding")
}
return coded.fold(
@ -27,8 +30,13 @@ object Encoder: IStringEncoding {
val decoded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
Encoding.ISO -> IsoEncoding.decode(bytes)
Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.CP437 -> Cp437Encoding.decode(bytes)
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
Encoding.C64OS -> C64osEncoding.decode(bytes)
else -> throw InternalCompilerException("unsupported encoding $encoding")
}
return decoded.fold(
@ -36,4 +44,4 @@ object Encoder: IStringEncoding {
success = { it }
)
}
}
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.cbm
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
import java.io.CharConversionException
import java.nio.charset.Charset
object IsoEncoding {
val charset: Charset = Charset.forName("ISO-8859-15")
fun encode(str: String): Result<List<UByte>, CharConversionException> {
open class IsoEncodingBase(charsetName: String) {
val charset: Charset = Charset.forName(charsetName)
fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
return try {
val mapped = str.map { chr ->
when (chr) {
'\u0000' -> 0u
'\n' -> if(newlineToCarriageReturn) 13u else 10u
in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte()
@ -27,11 +29,21 @@ object IsoEncoding {
}
}
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
Ok(String(bytes.map {
when(it) {
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
else -> it.toByte()
}
}.toByteArray(), charset))
} catch (ce: CharConversionException) {
Err(ce)
}
}
}
object IsoEncoding: IsoEncodingBase("ISO-8859-15")
object IsoCyrillicEncoding: IsoEncodingBase("ISO-8859-5")
object IsoEasternEncoding: IsoEncodingBase("ISO-8859-16")

View File

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

View File

@ -1,4 +1,4 @@
package prog8.code.target.cbm
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
@ -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(
@ -21,10 +21,10 @@ object PetsciiEncoding {
'\ufffe', // 0x07 -> UNDEFINED
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
'\ufffe', // 0x0A -> UNDEFINED
'\n', // 0x0A -> LINE FEED (RETURN)
'\ufffe', // 0x0B -> UNDEFINED
'\ufffe', // 0x0C -> UNDEFINED
'\r' , // 0x0D -> CARRIAGE RETURN
'\n' , // 0x0D -> LINE FEED (RETURN)
'\u000e', // 0x0E -> SHIFT OUT
'\ufffe', // 0x0F -> UNDEFINED
'\ufffe', // 0x10 -> UNDEFINED
@ -152,7 +152,7 @@ object PetsciiEncoding {
'\uf113', //  0x8A -> FUNCTION KEY 4 (CUS)
'\uf115', //  0x8B -> FUNCTION KEY 6 (CUS)
'\uf117', //  0x8C -> FUNCTION KEY 8 (CUS)
'\n' , // 0x8D -> LINE FEED
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
'\u000f', //  0x8E -> SHIFT IN
'\ufffe', // 0x8F -> UNDEFINED
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
@ -283,7 +283,7 @@ object PetsciiEncoding {
'\ufffe', // 0x0A -> UNDEFINED
'\ufffe', // 0x0B -> UNDEFINED
'\ufffe', // 0x0C -> UNDEFINED
'\r' , // 0x0D -> CARRIAGE RETURN
'\n' , // 0x0D -> LINE FEED (RETURN)
'\u000e', // 0x0E -> SHIFT OUT
'\ufffe', // 0x0F -> UNDEFINED
'\ufffe', // 0x10 -> UNDEFINED
@ -411,7 +411,7 @@ object PetsciiEncoding {
'\uf113', // 0x8A -> FUNCTION KEY 4 (CUS)
'\uf115', // 0x8B -> FUNCTION KEY 6 (CUS)
'\uf117', // 0x8C -> FUNCTION KEY 8 (CUS)
'\n' , // 0x8D -> LINE FEED
'\r' , // 0x8D -> CARRIAGE RETURN (SHIFT-RETURN)
'\u000f', // 0x8E -> SHIFT IN
'\ufffe', // 0x8F -> UNDEFINED
'\uf105', // 0x90 -> BLACK COLOR SWITCH (CUS)
@ -1061,6 +1061,7 @@ object PetsciiEncoding {
'}' -> '├'
'|' -> '│'
'\\' -> '╲'
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
else -> chr
}
@ -1076,7 +1077,10 @@ object PetsciiEncoding {
}
else -> {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}Petscii character for '${chr}' (${chr.code})")
if(chr.isISOControl())
throw CharConversionException("no ${case}Petscii character for char #${chr.code}")
else
throw CharConversionException("no ${case}Petscii character for char #${chr.code} '${chr}'")
}
}
}
@ -1085,7 +1089,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})
@ -1113,13 +1117,18 @@ object PetsciiEncoding {
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
return screencode?.toUByte() ?: when (chr) {
'\u0000' -> 0u
'\n' -> 141u
'\r' -> 141u
in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte()
}
else -> {
val case = if (lowercase) "lower" else "upper"
throw CharConversionException("no ${case}Screencode character for '${chr}' (${chr.code})")
if(chr.isISOControl())
throw CharConversionException("no ${case}Screencode character for char #${chr.code}")
else
throw CharConversionException("no ${case}Screencode character for char #${chr.code} '${chr}'")
}
}
}
@ -1128,7 +1137,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})
@ -1150,16 +1159,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) {

View File

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

View File

@ -1,73 +0,0 @@
package prog8.code.target.virtual
import prog8.code.core.*
import java.nio.file.Path
import kotlin.io.path.isReadable
import kotlin.io.path.name
import kotlin.io.path.readText
class VirtualMachineDefinition: IMachineDefinition {
override val cpu = CpuType.VIRTUAL
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used
override lateinit var golden: GoldenRam // not actually used
override fun getFloatAsmBytes(num: Number): String {
// little endian binary representation
val bits = num.toFloat().toBits().toUInt()
val hexStr = bits.toString(16).padStart(8, '0')
val parts = hexStr.chunked(2).map { "\$" + it }
return parts.joinToString(", ")
}
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return listOf("syslib")
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
println("\nStarting Virtual Machine...")
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name
if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText())
} else {
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
if(withExt.isReadable())
vm.runProgram(withExt.readText())
else
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
}
}
override fun isIOAddress(address: UInt): Boolean = false
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = VirtualZeropage(compilerOptions)
}
}
interface IVirtualMachineRunner {
fun runProgram(irSource: String)
}
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
override val SCRATCH_B1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_REG: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W2: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.c128
package prog8.code.target.zp
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
@ -6,22 +6,16 @@ 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) {
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
override val SCRATCH_REG = 0x75u // temp storage for a register, must be B1+1
override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
init {
if (options.floats) {
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
@ -33,18 +27,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 +47,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))
@ -69,9 +63,6 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
free.addAll(distinctFree)
removeReservedFromFreePool()
}
override fun allocateCx16VirtualRegisters() {
TODO("Not known if C128 can put the virtual regs in ZP")
retainAllowed()
}
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.c64
package prog8.code.target.zp
import prog8.code.core.*
@ -6,7 +6,7 @@ import prog8.code.core.*
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x02u // temp storage for a single byte
override val SCRATCH_REG = 0x03u // temp storage for a register, must be B1+1
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
@ -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,
@ -53,11 +53,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
).map{it.toUInt()})
}
if(options.zeropage!= ZeropageType.DONTUSE) {
if(options.zeropage != ZeropageType.DONTUSE) {
// add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
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 {
// don't use the zeropage at all
@ -75,11 +75,13 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
// in these cases there is enough space on the zero page to stick the cx16 virtual registers in there as well.
allocateCx16VirtualRegisters()
}
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {
private 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) {

View File

@ -1,4 +1,4 @@
package prog8.code.target.cx16
package prog8.code.target.zp
import prog8.code.core.*
@ -6,7 +6,7 @@ import prog8.code.core.*
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
@ -47,14 +47,14 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
free.addAll(distinctFree)
removeReservedFromFreePool()
allocateCx16VirtualRegisters()
retainAllowed()
}
}
override fun allocateCx16VirtualRegisters() {
private 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

View File

@ -0,0 +1,61 @@
package prog8.code.target.zp
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class ConfigurableZeropage(
override val SCRATCH_B1: UInt, // temp storage for a single byte
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
override val SCRATCH_W1: UInt, // temp storage 1 for a word
override val SCRATCH_W2: UInt, // temp storage 2 for a word
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
basicsafe: List<UIntRange>,
kernalsafe: List<UIntRange>,
fullsafe: List<UIntRange>,
options: CompilationOptions
) : Zeropage(options) {
init {
if (options.floats) {
TODO("floats in configurable target zp")
}
if(SCRATCH_REG!=SCRATCH_B1+1u)
throw IllegalArgumentException("Zero page scratch variable REG should be B1+1")
when (options.zeropage) {
ZeropageType.DONTUSE -> { /* don't use any zeropage at all */ }
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
allocateCx16VirtualRegisters()
retainAllowed()
}
private 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) {
val address = virtualRegistersStart + (2*reg).toUInt()
if(address<=0xffu) {
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
}
}
}
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.pet
package prog8.code.target.zp
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
@ -11,15 +11,11 @@ import prog8.code.core.ZeropageType
class PETZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
init {
if (options.floats) {
throw InternalCompilerException("PET target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
@ -50,9 +46,6 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
free.addAll(distinctFree)
removeReservedFromFreePool()
}
override fun allocateCx16VirtualRegisters() {
TODO("Not known if C128 can put the virtual regs in ZP")
retainAllowed()
}
}

View File

@ -1,63 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
}
java {
toolchain {
languageVersion = 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:1.1.18"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -0,0 +1,47 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
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")
}
}

View File

@ -11,8 +11,10 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
</component>
</module>

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,16 @@
package prog8.codegen.cpu6502
import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.StConstant
import prog8.code.StMemVar
import prog8.code.SymbolTable
import prog8.code.core.IMachineDefinition
import prog8.code.core.ICompilationTarget
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
var numberOfOptimizations = 0
@ -43,6 +44,20 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++
}
mods = optimizeUnneededTempvarInAdd(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeTSBtoRegularOr(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
var linesByFourteen = getLinesBy(lines, 14)
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
if(mods.isNotEmpty()) {
@ -51,15 +66,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++
}
mods = optimizeSamePointerIndexing(linesByFourteen)
mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14)
numberOfOptimizations++
}
// TODO more assembly peephole optimizations
return numberOfOptimizations
}
@ -68,24 +81,49 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?, val removeLabel: Boolean=false)
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
if(modification.remove)
lines.removeAt(modification.lineIndex)
if(modification.remove) {
if(modification.removeLabel)
lines.removeAt(modification.lineIndex)
else {
val line = lines[modification.lineIndex]
if (line.length < 2 || line[0] == ';' || line.trimStart()[0] == ';')
lines.removeAt(modification.lineIndex)
else if (haslabel(line)) {
val label = keeplabel(line)
if (label.isNotEmpty())
lines[modification.lineIndex] = label
else
lines.removeAt(modification.lineIndex)
} else lines.removeAt(modification.lineIndex)
}
}
else
lines[modification.lineIndex] = modification.replacement!!
}
}
private fun haslabel(line: String): Boolean {
return line.length>1 && line[0]!=';' && (!line[0].isWhitespace() || ':' in line)
}
private fun keeplabel(line: String): String {
if(':' in line)
return line.substringBefore(':') + ':'
val splits = line.split('\t', ' ', limit=2)
return if(splits.size>1) splits[0] + ':' else ""
}
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
// all lines (that aren't empty or comments) in sliding windows of certain size
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
lines.asSequence().withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
private fun optimizeSameAssignments(
linesByFourteen: List<List<IndexedValue<String>>>,
machine: IMachineDefinition,
linesByFourteen: Sequence<List<IndexedValue<String>>>,
machine: ICompilationTarget,
symbolTable: SymbolTable
): List<Modification> {
@ -274,7 +312,7 @@ private fun optimizeSameAssignments(
return mods
}
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
private fun optimizeSamePointerIndexingAndUselessBeq(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
// if Y isn't modified in between we can omit the second LDY:
@ -311,33 +349,60 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
mods.add(Modification(lines[4].index, true, null))
}
}
/*
beq +
lda #1
+
[ optional: label_xxxx_shortcut line here]
beq label_xxxx_shortcut / bne label_xxxx_shortcut
or *_afterif labels.
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
*/
val autoLabelPrefix = GENERATED_LABEL_PREFIX
if(first=="beq +" && second=="lda #1" && third=="+") {
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(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))
mods.add(Modification(lines[2].index, true, null))
}
}
}
}
return mods
}
private fun optimizeStoreLoadSame(
linesByFour: List<List<IndexedValue<String>>>,
machine: IMachineDefinition,
linesByFour: Sequence<List<IndexedValue<String>>>,
machine: ICompilationTarget,
symbolTable: SymbolTable
): List<Modification> {
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
val mods = mutableListOf<Modification>()
for (lines in linesByFour) {
val first = lines[1].value.trimStart()
val second = lines[2].value.trimStart()
val third = lines[3].value.trimStart()
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("stx ") && second.startsWith("ldx "))
(first.startsWith("ldx ") && second.startsWith("ldx "))
) {
val third = lines[3].value.trimStart()
val attemptRemove =
if(third.isBranch()) {
// a branch instruction follows, we can only remove the load instruction if
@ -374,21 +439,27 @@ private fun optimizeStoreLoadSame(
} else if(first=="phx" && second=="pla") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " txa"))
} else if(first=="phx" && second=="ply") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " txy"))
} else if(first=="phy" && second=="pla") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tya"))
} else if(first=="phy" && second=="plx") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tyx"))
}
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
(first.startsWith("ldy ") && second.startsWith("sty "))
) {
val firstLoc = first.substring(4).trimStart()
val secondLoc = second.substring(4).trimStart()
if (firstLoc == secondLoc)
mods.add(Modification(lines[2].index, true, null))
}
}
return mods
}
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
// try to get the constant value address, could return null if it's a symbol instead
@ -413,7 +484,7 @@ private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
}
}
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
val mods = mutableListOf<Modification>()
for (lines in linesByFour) {
@ -430,50 +501,166 @@ private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
}
}
return mods
}
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// jsr Sub + rts -> jmp Sub
// jmp Sub + rts -> jmp Sub
// rts + jmp -> remove jmp
// rts + bxx -> remove bxx
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
// and some other optimizations.
val mods = mutableListOf<Modification>()
for (lines in linesByFour) {
val first = lines[0].value
val second = lines[1].value
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
mods += Modification(lines[1].index, true, null)
val third = lines[2].value
if(!haslabel(second)) {
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[1].index, true, null)
}
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
mods += Modification(lines[1].index, true, null)
}
}
else if (" rts" in first || "\trts" in first) {
if (" jmp" in second || "\tjmp" in second)
mods += Modification(lines[1].index, true, null)
else if (" bra" in second || "\tbra" in second)
mods += Modification(lines[1].index, true, null)
else if (" bcc" in second || "\tbcc" in second)
mods += Modification(lines[1].index, true, null)
else if (" bcs" in second || "\tbcs" in second)
mods += Modification(lines[1].index, true, null)
else if (" beq" in second || "\tbeq" in second)
mods += Modification(lines[1].index, true, null)
else if (" bne" in second || "\tbne" in second)
mods += Modification(lines[1].index, true, null)
else if (" bmi" in second || "\tbmi" in second)
mods += Modification(lines[1].index, true, null)
else if (" bpl" in second || "\tbpl" in second)
mods += Modification(lines[1].index, true, null)
else if (" bvs" in second || "\tbvs" in second)
mods += Modification(lines[1].index, true, null)
else if (" bvc" in second || "\tbvc" in second)
mods += Modification(lines[1].index, true, null)
}
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
) {
mods.add(Modification(lines[1].index, true, null))
}
else if(" cmp #0" in second || "\tcmp #0" in second) {
// there are many instructions that modify A and set the bits...
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
if(" $instr" in first || "\t$instr" in first) {
mods.add(Modification(lines[1].index, true, null))
}
}
}
// only remove bra followed by jmp or jmp followed by bra
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
mods.add(Modification(lines[1].index, true, null))
}
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
mods.add(Modification(lines[1].index, true, null))
}
}
else if (" rts" in first || "\trts" in first) {
if (" jmp" in second || "\tjmp" in second)
mods += Modification(lines[1].index, true, null)
else if (" bra" in second || "\tbra" in second)
mods += Modification(lines[1].index, true, null)
else if (" bcc" in second || "\tbcc" in second)
mods += Modification(lines[1].index, true, null)
else if (" bcs" in second || "\tbcs" in second)
mods += Modification(lines[1].index, true, null)
else if (" beq" in second || "\tbeq" in second)
mods += Modification(lines[1].index, true, null)
else if (" bne" in second || "\tbne" in second)
mods += Modification(lines[1].index, true, null)
else if (" bmi" in second || "\tbmi" in second)
mods += Modification(lines[1].index, true, null)
else if (" bpl" in second || "\tbpl" in second)
mods += Modification(lines[1].index, true, null)
else if (" bvs" in second || "\tbvs" in second)
mods += Modification(lines[1].index, true, null)
else if (" bvc" in second || "\tbvc" in second)
mods += Modification(lines[1].index, true, null)
/*
LDA NUM1
CMP NUM2
BCC LABEL
BEQ LABEL
(or something similar) which branches to LABEL when NUM1 <= NUM2. (In this case NUM1 and NUM2 are unsigned numbers.) However, consider the following sequence:
LDA NUM2
CMP NUM1
BCS LABEL
*/
val tfirst = first.trimStart()
val tsecond = second.trimStart()
val tthird = lines[2].value.trimStart()
val tfourth = lines[3].value.trimStart()
if(tfirst.startsWith("lda") && tsecond.startsWith("cmp") && tthird.startsWith("bcc") && tfourth.startsWith("beq")) {
val label = tthird.substring(4)
if(label==tfourth.substring(4)) {
mods += Modification(lines[0].index, false, " lda ${tsecond.substring(4)}")
mods += Modification(lines[1].index, false, " cmp ${tfirst.substring(4)}")
mods += Modification(lines[2].index, false, " bcs $label")
mods += Modification(lines[3].index, true, null)
}
}
fun sameLabel(branchInstr: String, jumpInstr: String, labelInstr: String): Boolean {
if('(' in jumpInstr) return false // indirect jump cannot be replaced
val label = labelInstr.trimEnd().substringBefore(':').substringBefore(' ').substringBefore('\t')
val branchLabel = branchInstr.trimStart().substring(3).trim()
return label==branchLabel
}
// beq Label + jmp Addr + Label -> bne Addr
if((" jmp" in second || "\tjmp " in second) && haslabel(third)) {
if((" beq " in first || "\tbeq " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bne")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bne " in first || "\tbne " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "beq")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bcc " in first || "\tbcc " in first) && sameLabel(first, second, third)){
val branch = second.replace("jmp", "bcs")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bcs " in first || "\tbcs " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bcc")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bpl " in first || "\tbpl " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bmi")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bmi " in first || "\tbmi " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bpl")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bvc " in first || "\tbvc " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bvs")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
else if((" bvs " in first || "\tbvs " in first) && sameLabel(first, second, third)) {
val branch = second.replace("jmp", "bvc")
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, false, branch))
}
}
}
return mods
}
private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
val mods = mutableListOf<Modification>()
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
@ -506,6 +693,77 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
optimize('a', lines)
optimize('x', lines)
optimize('y', lines)
val first = lines[1].value.trimStart()
val second = lines[2].value.trimStart()
val third = lines[3].value.trimStart()
// phy + ldy + pla -> tya + ldy
// phx + ldx + pla -> txa + ldx
// pha + lda + pla -> nop
when (first) {
"phy" if second.startsWith("ldy ") && third=="pla" -> {
mods.add(Modification(lines[3].index, true, null))
mods.add(Modification(lines[1].index, false, " tya"))
}
"phx" if second.startsWith("ldx ") && third=="pla" -> {
mods.add(Modification(lines[3].index, true, null))
mods.add(Modification(lines[1].index, false, " txa"))
}
"pha" if second.startsWith("lda ") && third=="pla" -> {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
return mods
}
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
val first = lines[0].value.trimStart()
val second = lines[1].value.trimStart()
val third = lines[2].value.trimStart()
if(first.startsWith("lda") && second.startsWith("tsb") && third.startsWith("lda")) {
val operand1 = first.substring(3)
val operand2 = second.substring(3)
val operand3 = third.substring(3)
if(operand1!=operand2 && operand2==operand3) {
mods.add(Modification(lines[0].index, false, " lda $operand2"))
mods.add(Modification(lines[1].index, false, " ora $operand1"))
mods.add(Modification(lines[2].index, false, " sta $operand2"))
}
}
}
return mods
}
}
private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
// this can be performed without the scratch variable: clc / adc something
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
val first = lines[0].value.trimStart()
val second = lines[1].value.trimStart()
val third = lines[2].value.trimStart()
val fourth = lines[3].value.trimStart()
if(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
if(fourth.substring(4)==first.substring(4)) {
mods.add(Modification(lines[0].index, false, " clc"))
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
return mods
}

View File

@ -1,9 +1,8 @@
package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.ast.PtAsmSub
import prog8.code.core.Cx16VirtualRegisters
import prog8.code.core.RegisterOrPair
import prog8.code.core.RegisterOrStatusflag
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
@ -11,50 +10,23 @@ fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
// order is:
// 1) cx16 virtual word registers,
// 2) paired CPU registers,
// 3) single CPU registers (X last), except A,
// 4) CPU Carry status flag
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
// 3) single CPU registers (order Y,X,A),
// 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 (regsWithoutA, args4) = args3.partition { it.value.first.registerOrPair != RegisterOrPair.A }
val (regA, rest) = args4.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 }
regsWithoutA.forEach {
if(it.value.first.registerOrPair != RegisterOrPair.X)
order += it.index
}
regsWithoutA.firstOrNull { it.value.first.registerOrPair==RegisterOrPair.X } ?.let { 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 }
regA.forEach { order += it.index }
require(order.size==sub.parameters.size)
return order
}
fun asmsub6502ArgsHaveRegisterClobberRisk(
args: List<PtExpression>,
params: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>
): Boolean {
fun isClobberRisk(expr: PtExpression): Boolean {
when (expr) {
is PtArrayIndexer -> {
return params.any {
it.first.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
}
}
is PtBuiltinFunctionCall -> {
if (expr.name == "lsb" || expr.name == "msb")
return isClobberRisk(expr.args[0])
if (expr.name == "mkword")
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
return !expr.isSimple()
}
else -> return !expr.isSimple()
}
}
return args.size>1 && args.any { isClobberRisk(it) }
}

View File

@ -1,7 +1,14 @@
package prog8.codegen.cpu6502
import prog8.code.core.*
import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.IAssemblyProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.OutputType
import prog8.code.target.C128Target
import prog8.code.target.C64Target
import prog8.code.target.PETTarget
import java.nio.file.Path
@ -21,15 +28,12 @@ internal class AssemblyProgram(
val assemblerCommand: List<String>
when (compTarget.name) {
in setOf("c64", "c128", "cx16", "pet32") -> {
when(options.output) {
OutputType.PRG -> {
// CBM machines .prg generation.
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", // "-Wno-strict-bool", "-Werror",
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile", "--no-monitor"
)
val command = mutableListOf("64tass", "--cbm-prg", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
if(options.warnSymbolShadowing)
command.add("-Wshadow")
@ -39,34 +43,20 @@ internal class AssemblyProgram(
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
if(options.asmListfile) {
command.add("--list=$listFile")
val outFile = when (options.output) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating prg for target ${compTarget.name}.")
prgFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
else -> throw AssemblyError("invalid output type")
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
assemblerCommand = command
if(!options.quiet)
println("\nCreating prg for target ${compTarget.name}.")
}
"atari" -> {
OutputType.XEX -> {
// Atari800XL .xex generation.
// TODO are these options okay for atari?
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", // "-Werror", "-Wno-strict-bool"
"--no-monitor"
)
val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
if(options.warnSymbolShadowing)
command.add("-Wshadow")
@ -79,28 +69,73 @@ internal class AssemblyProgram(
if(options.asmListfile)
command.add("--list=$listFile")
val outFile = when (options.output) {
OutputType.XEX -> {
command.add("--atari-xex")
println("\nCreating xex for target ${compTarget.name}.")
xexFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
else -> throw AssemblyError("invalid output type")
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
assemblerCommand = command
if(!options.quiet)
println("\nCreating xex for target ${compTarget.name}.")
}
OutputType.RAW -> {
// Neo6502/headerless raw program generation.
val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
command.add("--list=$listFile")
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
assemblerCommand = command
if(!options.quiet)
println("\nCreating raw binary for target ${compTarget.name}.")
}
OutputType.LIBRARY -> {
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
command.add("--list=$listFile")
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
if(!options.quiet)
println("\nCreating binary library file with header for target ${compTarget.name}.")
command.add("--cbm-prg")
} else {
if(!options.quiet)
println("\nCreating binary library file without header for target ${compTarget.name}.")
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
command.addAll(listOf("--output", binFile.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(options.compTarget.additionalAssemblerOptions!=null)
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
val proc = ProcessBuilder(assemblerCommand)
if(!options.quiet)
proc.inheritIO()
val process = proc.start()
val result = process.waitFor()
if (result == 0) {
removeGeneratedLabelsFromMonlist()
generateBreakpointList()
}
@ -108,7 +143,7 @@ internal class AssemblyProgram(
}
private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
val lines = viceMonListFile.toFile().readLines()
viceMonListFile.toFile().outputStream().bufferedWriter().use {
for (line in lines) {
@ -125,7 +160,7 @@ internal class AssemblyProgram(
for (line in viceMonListFile.toFile().readLines()) {
val match = pattern.matchEntire(line)
if (match != null)
breakpoints.add("break \$" + match.groupValues[1])
breakpoints.add("break $" + match.groupValues[1])
}
val num = breakpoints.size
breakpoints.add(0, "; breakpoint list now follows")

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
package prog8.codegen.cpu6502
import prog8.code.StNodeType
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignSource
@ -15,9 +16,16 @@ 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 IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>): Boolean {
// When the parameter(s) are passed via an explicit register or register pair,
// we consider them NOT to be optimized into (possibly different) CPU registers.
// Just load them in whatever the register spec says.
return when (params.size) {
1 -> params[0].type.isIntegerOrBool && params[0].register == null
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
else -> false
}
}
internal fun translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call
@ -25,8 +33,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol!!.astNode as IPtSubroutine
val symbol = asmgen.symbolTable.lookup(call.name)!!
if(symbol.type == StNodeType.LABEL) {
require(call.void)
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
return
}
val sub = symbol.astNode as IPtSubroutine
val subAsmName = asmgen.asmSymbolName(call.name)
if(sub is PtAsmSub) {
@ -40,27 +54,109 @@ 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) {
if(asmgen.options.romable)
TODO("no codegen yet for non-const bank in subroutine call that's usable in ROM ${call.position}")
// self-modifying code: set jsrfar bank argument
when(asmgen.options.compTarget.name) {
"cx16" -> {
// JSRFAR can jump to a banked RAM address as well!
asmgen.out("""
php
pha
lda $varbank
sta +
pla
plp
jsr cx16.JSRFAR
.word $subAsmName ; ${sub.address!!.address.toHex()}
+ .byte 0 ; modified"""
)
}
"c64" -> {
asmgen.out("""
php
pha
lda $varbank
sta +
pla
plp
jsr c64.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
+ .byte 0 ; modified"""
)
}
"c128" -> {
asmgen.out("""
php
pha
lda $varbank
sta +
pla
plp
jsr c128.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
+ .byte 0 ; modified"""
)
}
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
}
} else {
asmgen.out(" jsr $subAsmName")
}
}
else {
when(asmgen.options.compTarget.name) {
"cx16" -> {
// JSRFAR can jump to a banked RAM address as well!
asmgen.out("""
jsr cx16.JSRFAR
.word $subAsmName ; ${sub.address!!.address.toHex()}
.byte $bank"""
)
}
"c64" -> {
asmgen.out("""
jsr c64.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
.byte $bank"""
)
}
"c128" -> {
asmgen.out("""
jsr c128.x16jsrfar
.word $subAsmName ; ${sub.address!!.address.toHex()}
.byte $bank"""
)
}
else -> throw AssemblyError("callfar is not supported on the selected compilation target")
}
}
}
}
else if(sub is PtSub) {
if(optimizeIntArgsViaRegisters(sub)) {
if(sub.parameters.size==1) {
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
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 paramValues = sub.parameters.zip(call.args)
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
if (normalParams.isNotEmpty()) {
for (p in normalParams)
argumentViaVariable(sub, p.first, p.second)
}
if (registerParams.isNotEmpty()) {
// the R0-R15 'registers' are not really registers. They're just special variables.
for (p in registerParams)
argumentViaVariable(sub, p.first, p.second)
}
}
asmgen.out(" jsr $subAsmName")
}
@ -69,37 +165,94 @@ 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 argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
if(sub.parameters.size==1) {
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0])
} else {
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) {
registerArgsViaCpuStackEvaluation(call, sub)
} else {
asmsub6502ArgsEvalOrder(sub).forEach {
val param = sub.parameters[it]
val arg = call.args[it]
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
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
if(asmgen.needAsaveForExpr(args[0]) && !asmgen.needAsaveForExpr(args[1])) {
// first 0 then 1
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
} else if(!asmgen.needAsaveForExpr(args[0]) && asmgen.needAsaveForExpr(args[1])) {
// first 1 then 0
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
} else {
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 registerArgsViaCpuStackEvaluation(call: PtFunctionCall, callee: PtAsmSub) {
// this is called when one or more of the arguments are 'complex' and
// cannot be assigned to a register easily or risk clobbering other registers.
if(callee.parameters.isEmpty())
return
// use the cpu hardware stack as intermediate storage for the arguments.
val argOrder = asmsub6502ArgsEvalOrder(callee)
argOrder.reversed().forEach {
asmgen.pushCpuStack(callee.parameters[it].second.type, call.args[it])
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
return when(arg) {
is PtBuiltinFunctionCall -> {
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])
return !arg.isSimple()
}
is PtAddressOf -> false
is PtIdentifier -> false
is PtIrRegister -> false
is PtMemoryByte -> arg.address !is PtNumber && arg.address !is PtIdentifier
is PtNumber -> false
is PtBool -> false
else -> true
}
argOrder.forEach {
val param = callee.parameters[it]
asmgen.popCpuStack(callee, param.second, param.first)
}
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
val registersUsed = mutableListOf<RegisterOrStatusflag>()
fun usedA() = registersUsed.any {it.registerOrPair==RegisterOrPair.A || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.AY}
fun usedX() = registersUsed.any {it.registerOrPair==RegisterOrPair.X || it.registerOrPair==RegisterOrPair.AX || it.registerOrPair==RegisterOrPair.XY}
fun usedY() = registersUsed.any {it.registerOrPair==RegisterOrPair.Y || it.registerOrPair==RegisterOrPair.AY || it.registerOrPair==RegisterOrPair.XY}
if(sub.parameters.size==1) {
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0])
} else {
val optimalEvalOrder = asmsub6502ArgsEvalOrder(sub)
optimalEvalOrder.forEach {
val param = sub.parameters[it]
val arg = call.args[it]
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
else if(registersUsed.any { r-> r.statusflag!=null }) {
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
}
else {
if(usedX()) asmgen.saveRegisterStack(CpuRegister.X, usedA())
if(usedY()) asmgen.saveRegisterStack(CpuRegister.Y, usedA())
if(usedA()) asmgen.saveRegisterStack(CpuRegister.A, usedA())
val used = argumentViaRegister(sub, IndexedValue(it, param.second), arg)
if(usedA()) asmgen.restoreRegisterStack(CpuRegister.A, false)
if(usedY()) asmgen.restoreRegisterStack(CpuRegister.Y, true)
if(usedX()) asmgen.restoreRegisterStack(CpuRegister.X, true)
used
}
} else {
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
}
}
}
}
@ -108,11 +261,18 @@ 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) {
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
// pass argument via a register parameter
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible")
@ -125,12 +285,12 @@ 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) {
if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte value is required")
throw AssemblyError("for statusflag, byte or bool value is required")
if (statusflag == Statusflag.Pc) {
// this boolean param needs to be set last, right before the jsr
// for now, this is already enforced on the subroutine definition by the Ast Checker
@ -139,8 +299,12 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc")
}
is PtBool -> {
asmgen.out(if(value.value) " sec" else " clc")
}
is PtIdentifier -> {
val sourceName = asmgen.asmVariableName(value)
// note: cannot use X register here to store A because it might be used for other arguments
asmgen.out("""
pha
clc
@ -155,29 +319,29 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
}
} else throw AssemblyError("can only use Carry as status flag parameter")
return RegisterOrStatusflag(null, statusflag)
}
else {
// via register or register pair
register!!
if(requiredDt largerThan 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.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)
addr.parent = sub as PtNode
addr.parent = scope as PtNode
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
@ -185,26 +349,31 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
}
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
asmgen.translateNormalAssignment(AsmAssignment(src, listOf(target), program.memsizer, target.position), scope)
}
return RegisterOrStatusflag(register, null)
}
}
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType)
return true
if(argType in ByteDatatypes && paramType in ByteDatatypes)
if(argType.isBool && paramType.isBool)
return true
if(argType in WordDatatypes && paramType in WordDatatypes)
if(argType.isByte && paramType.isByte)
return true
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

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